DRV2605 Haptic Motor Driver
The DRV2605 haptic motor driver component allows the user to configure and play custom or preconfigured haptic feedback sequences via a serial interface such as I2C. It supports directly driving ECM (eccentric rotating mass) and LRA (linear resonant actuator) type haptic motors. It also supports fully custom waveforms (e.g. via using the audio, pwm / analog functions) as well as a preset library of 123 different haptic waveforms which can be played in sequences of up to 8 waveforms.
API Reference
Header File
Classes
-
class Drv2605 : public espp::BasePeripheral<>
Class for controlling the Texas Instruments DRV2605 Haptic Motor Driver. Drives ECM (eccentric rotating mass) and LRA (linear resonant actuator) types of haptic motors. The datasheet for the DRV2605 can be found here: https://www.ti.com/lit/ds/symlink/drv2605.pdf?ts=1678892742599.
DRV2605 Example
// make the I2C that we'll use to communicate espp::I2c i2c({ .port = I2C_NUM_1, .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); // make the actual drv2605 class espp::Drv2605 drv2605(espp::Drv2605::Config{ .device_address = espp::Drv2605::DEFAULT_ADDRESS, .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), .read_register = std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .motor_type = espp::Drv2605::MotorType::LRA}); std::error_code ec; // we're using an ERM motor, so select an ERM library (1-5). // drv2605.select_library(1, ec); // we're using an LRA motor, so select an LRA library (6). drv2605.select_library(espp::Drv2605::Library::LRA, ec); if (ec) { logger.error("select library failed: {}", ec.message()); } // make the task which will cycle through all the waveforms auto task_fn = [&drv2605](std::mutex &m, std::condition_variable &cv) { static auto start = std::chrono::high_resolution_clock::now(); auto now = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration<float>(now - start).count(); static uint8_t waveform = 0; std::error_code ec; drv2605.stop(ec); if (ec) { logger.error("stop failed: {}", ec.message()); return false; } drv2605.set_waveform(0, (espp::Drv2605::Waveform)waveform, ec); if (ec) { logger.error("set waveform failed: {}", ec.message()); return false; } drv2605.set_waveform(1, espp::Drv2605::Waveform::END, ec); if (ec) { logger.error("set waveform failed: {}", ec.message()); return false; } drv2605.start(ec); if (ec) { logger.error("start failed: {}", ec.message()); return false; } waveform++; if (waveform > (uint8_t)espp::Drv2605::Waveform::MAX) { waveform = 0; } logger.info("{:.3f}, {}", elapsed, waveform); // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { using namespace std::chrono_literals; std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, 1s); } // we don't want to stop, so return false return false; }; auto task = espp::Task::make_unique({.callback = task_fn, .task_config = { .name = "example", .stack_size_bytes{4 * 1024}, }, .log_level = espp::Logger::Verbosity::INFO}); task->start();
Public Types
-
enum class Mode : uint8_t
The mode of the vibration.
Values:
-
enumerator INTTRIG
Internal Trigger (call star() to start playback)
-
enumerator EXTTRIGEDGE
External edge trigger (rising edge on IN pin starts playback)
-
enumerator EXTTRIGLVL
External level trigger (playback follows state of IN pin)
-
enumerator PWMANALOG
PWM/Analog input.
-
enumerator AUDIOVIBE
Audio-to-vibe mode.
-
enumerator REALTIME
Real-time playback (RTP)
-
enumerator DIAGNOS
Diagnostics.
-
enumerator AUTOCAL
Auto-calibration.
-
enumerator INTTRIG
-
enum class Waveform : uint8_t
The waveforms supported by the DRV2605. It has 123 different waveforms, with waveform ID 0 being a special END identifier used when playing multiple waveforms in sequence.
Note
All of the waveform names have not been enumerated here. For a complete list, please see https://learn.adafruit.com/assets/72593
Values:
-
enumerator END
Signals this is the end of the waveforms to play.
-
enumerator STRONG_CLICK
-
enumerator SHARP_CLICK
-
enumerator SOFT_BUMP
-
enumerator DOUBLE_CLICK
-
enumerator TRIPLE_CLICK
-
enumerator SOFT_FUZZ
-
enumerator STRONG_BUZZ
-
enumerator ALERT_750MS
-
enumerator ALERT_1000MS
-
enumerator BUZZ1
-
enumerator BUZZ2
-
enumerator BUZZ3
-
enumerator BUZZ4
-
enumerator BUZZ5
-
enumerator PULSING_STRONG_1
-
enumerator PULSING_STRONG_2
-
enumerator TRANSITION_CLICK_1
-
enumerator TRANSITION_HUM_1
-
enumerator MAX
Values >= to this do not correspond to valid waveforms.
-
enumerator END
-
enum class MotorType
The type of vibration motor connected to the Drv2605.
Values:
-
enumerator ERM
Eccentric Rotating Mass (more common, therefore default)
-
enumerator LRA
Linear Resonant Actuator.
-
enumerator ERM
-
enum class Library
The library of waveforms to use.
Note
The DRV2605 has 7 different libraries of waveforms. The first library is empty, and the next 5 are ERM (eccentric rotating mass) libraries. The last library is an LRA (linear resonant actuator) library.
Values:
-
enumerator EMPTY
-
enumerator ERM_0
-
enumerator ERM_1
-
enumerator ERM_2
-
enumerator ERM_3
-
enumerator ERM_4
-
enumerator LRA
-
enumerator EMPTY
-
typedef std::function<bool(uint8_t)> probe_fn
Function to probe the peripheral
- Param address
The address to probe
- Return
True if the peripheral is found at the given address
Public Functions
-
inline void initialize(std::error_code &ec)
Initialize the DRV2605.
- Parameters
ec – Error code to set if there is an error.
-
inline void start(std::error_code &ec)
Start playing the configured waveform / sequence.
- Parameters
ec – Error code to set if there is an error.
-
inline void stop(std::error_code &ec)
Stop playing the waveform / sequence.
- Parameters
ec – Error code to set if there is an error.
-
inline void set_mode(Mode mode, std::error_code &ec)
Set the mode of the vibration.
- Parameters
mode – Mode to set to.
ec – Error code to set if there is an error.
-
inline void set_waveform(uint8_t slot, Waveform w, std::error_code &ec)
Set the waveform slot at
slot
to \w.Note
When calling start() to play the configured waveform slots, the driver will always start playing from slot 0 and will continue until it reaches a slot that has been configured with the Waveform::END.
- Parameters
slot – The slot (0-8) to set.
w – The Waveform to play in slot
slot
.ec – Error code to set if there is an error.
-
inline void select_library(Library lib, std::error_code &ec)
Select the waveform library to use.
- Parameters
lib – Library to use, 0=Empty, 1-5 are ERM, 6 is LRA
ec – Error code to set if there is an error.
- inline bool probe (std::error_code &ec) requires(UseAddress)
Probe the peripheral
Note
This function is thread safe
Note
If the probe function is not set, this function will return false and set the error code to operation_not_supported
Note
This function is only available if UseAddress is true
- Parameters
ec – The error code to set if there is an error
- Returns
True if the peripheral is found
- inline void set_address (uint8_t address) requires(UseAddress)
Set the address of the peripheral
Note
This function is thread safe
Note
This function is only available if UseAddress is true
- Parameters
address – The address of the peripheral
- inline void set_probe (const probe_fn &probe) requires(UseAddress)
Set the probe function
Note
This function is thread safe
Note
This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.
Note
This function is only available if UseAddress is true
- Parameters
probe – The probe function
-
inline void set_write(const write_fn &write)
Set the write function
Note
This function is thread safe
Note
This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.
- Parameters
write – The write function
-
inline void set_read(const read_fn &read)
Set the read function
Note
This function is thread safe
Note
This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.
- Parameters
read – The read function
-
inline void set_read_register(const read_register_fn &read_register)
Set the read register function
Note
This function is thread safe
Note
This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.
- Parameters
read_register – The read register function
-
inline void set_write_then_read(const write_then_read_fn &write_then_read)
Set the write then read function
Note
This function is thread safe
Note
This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then
- Parameters
write_then_read – The write then read function
-
inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)
Set the delay between the write and read operations in write_then_read
Note
This function is thread safe
Note
This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.
Note
This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.
- Parameters
delay – The delay between the write and read operations in write_then_read
-
inline void set_config(const Config &config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters
config – The configuration for the peripheral
-
inline void set_config(Config &&config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters
config – The configuration for the peripheral
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters
rate_limit – The rate limit to use for the logger
-
struct Config
Configuration structure for the DRV2605.
Public Members
-
uint8_t device_address = DEFAULT_ADDRESS
I2C address of the device.
-
BasePeripheral::write_fn write
Function for writing a byte to a register on the Drv2605.
-
BasePeripheral::read_register_fn read_register
Function for reading a register from the Drv2605.
-
bool auto_init = {true}
If true, the driver will initialize the DRV2605 on construction.
-
uint8_t device_address = DEFAULT_ADDRESS
-
enum class Mode : uint8_t