BLDC Haptics
The BldcHaptics class is a high-level interface for controlling a BLDC motor with a haptic feedback loop. It is designed to be used to provide haptic feedback as part of a rotary input device (the BLDC motor). The component provides a DetentConfig interface for configuring the input / haptic feedback profile for the motor dynamically with configuration of:
Range of motion (min/max position + width of each position)
Width of each position in the range (which will be used to calculate the actual range of motion based on the number of positions)
Strength of the haptic feedback at each position (detent)
Strength of the haptic feedback at the edges of the range of motion
Specific positions to provide haptic feedback at (detents)
Snap point (percentage of position width which will trigger a snap to the nearest position)
The component also provides a HapticConfig interface for configuring the haptic feedback loop with configuration of:
Strength of the haptic feedback
Frequency of the haptic feedback [currently not implemented]
Duration of the haptic feedback [currently not implemented]
API Reference
Header File
Header File
Header File
Classes
-
template<MotorConcept M>
class BldcHaptics : public espp::BaseComponent Class which creates haptic feedback for the user by vibrating the motor This class is based on the work at https://github.com/scottbez1/smartknob to use a small BLDC gimbal motor as a haptic feedback device. It does so by varying the control type, setpoints, and gains of the motor to create a vibration. The motor is driven using the ESP32’s MCPWM peripheral. The motor is driven in a closed loop using the encoder feedback.
The haptics provided by this class enable configuration of:
Positions (with non-magnetic detents) - evenly spaced across the allowed range of motion
Number of magnetic detents - where the
Width of the detents
Strength of the detents
Snap point (position at which the motor will snap to the next detent / position)
Bounds on the rotation of the motor - can be unbounded, bounded within a single revolution, or bounded within multiple revolutions
The haptics provided by this class provide the following functionality:
Positions: Evenly spaced positions across the allowed range of motion (specified by the min and max position) will have a detent.
Detents: Manually specified detents will be placed at the specified positions. The detents will have a specified width and strength.
End Stops: The motor will vibrate when it is at the min or max position. This is useful for providing feedback when the motor is at the end of its range of motion. The end stops are configured by specifying the strength of the end stops.
Snap point: The snap point is the position at which the motor will snap to the next detent / position. This is useful for providing feedback when the motor is at a certain position. The snap point is configured by specifying the snap point (percentage of the way through the detent) and the snap point bias (percentage of the way through the detent to bias the snap point).
Some example configurations are provided as static constexpr in espp::detail. They are:
UNBOUNDED_NO_DETENTS: No detents, no end stops, no snap point, no bounds
BOUNDED_NO_DETENTS: No detents, no end stops, no snap point, bounded within a single revolution
MULTI_REV_NO_DETENTS: No detents, no end stops, no snap point, bounded within multiple revolutions
COARSE_VALUES_STRONG_DETENTS: detents, end stops, snap point, bounded within a single revolution
FINE_VALUES_NO_DETENTS: No detents, end stops, snap point, bounded
FINE_VALUES_WITH_DETENTS: detents, end stops, snap point, bounded
RETURN_TO_CENTER_WITH_DETENTS: 3 detents, end stops, snap point, bounded within a single revolution
RETURN_TO_CENTER_WITH_DETENTS_AND_MULTIPLE_REVOLUTIONS: 3 detents, end stops, snap point, bounded within multiple revolutions
Some haptic behaviors that can be implemented with this library are:
Unbounded with no detents
Bounded with no detents
Multiple revolutions
On/off with strong detent
Return to center without detents
Return to center with detents
Fine values with no detents
Fine values with detents
Coarse values with strong detents
Coarse values with weak detents
Example 1: Basic usage
using BldcHaptics = espp::BldcHaptics<BldcMotor>; auto haptic_motor = BldcHaptics({.motor = motor, .kp_factor = 2, .kd_factor_min = 0.01, .kd_factor_max = 0.04, .log_level = espp::Logger::Verbosity::INFO}); // auto detent_config = espp::detail::UNBOUNDED_NO_DETENTS; // auto detent_config = espp::detail::BOUNDED_NO_DETENTS; // auto detent_config = espp::detail::MULTI_REV_NO_DETENTS; // auto detent_config = espp::detail::ON_OFF_STRONG_DETENTS; auto detent_config = espp::detail::COARSE_VALUES_STRONG_DETENTS; // auto detent_config = espp::detail::FINE_VALUES_NO_DETENTS; // auto detent_config = espp::detail::FINE_VALUES_WITH_DETENTS; // auto detent_config = espp::detail::MAGNETIC_DETENTS; // auto detent_config = espp::detail::RETURN_TO_CENTER_WITH_DETENTS; logger.info("{}", detent_config); haptic_motor.update_detent_config(detent_config); // this will start the haptic motor thread which will run in the background. // If we want to change the detent config we can call update_detent_config() // and it will update the detent config in the background thread. haptic_motor.start();
Example 2: Playing a haptic click / buzz
haptic_motor.play_haptic(espp::detail::HapticConfig{ .strength = 5.0f, .frequency = 200.0f, // Hz, NOTE: frequency is unused for now .duration = 1s // NOTE: duration is unused for now });
Note
The motor is configured to be driven in an open-loop mode, so that the PID contained in the motor controller does not interfere with the haptic feedback.
Public Functions
-
inline explicit BldcHaptics(const Config &config)
Constructor for the haptic motor.
- Parameters
config – Configuration for the haptic motor
-
inline ~BldcHaptics()
Destructor for the haptic motor.
Note
This will stop the motor if it is running
-
inline bool is_running() const
Check if the haptic motor is running.
- Returns
True if the haptic motor is running, false otherwise
-
inline void start()
Start the haptic motor.
-
inline void stop()
Stop the haptic motor.
-
inline float get_position() const
Get the current position of the haptic motor.
- Returns
Current position of the haptic motor
-
inline void update_detent_config(const detail::DetentConfig &config)
Configure the detents for the haptic motor.
-
inline void play_haptic(const detail::HapticConfig &config)
Play haptic feedback.
Note
Plays a somewhat-configurable haptic “buzz” / “click” for the user
Note
This is a blocking call that will wait for the haptic feedback to finish before returning. It will also block the motor/detent task from running until the haptic feedback is finished.
- Parameters
config – Configuration for the haptic feedback
-
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 for the haptic motor.
Public Members
-
float kp_factor = {2}
Factor to multiply the detent strength by to get kp (default 2). Used for both detents and end stops.
Note
Depending on the motor, this may need to be adjusted to get the desired behavior.
-
float kd_factor_min = {0.01}
Min Factor to multiply the detent strength by to get kd (default 0.01).
Note
Depending on the motor, this may need to be adjusted to get the desired behavior.
-
float kd_factor_max = {0.04}
Max Factor to multiply the detent strength by to get kd (default 0.04).
Note
Depending on the motor, this may need to be adjusted to get the desired behavior.
-
float kp_factor = {2}