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

Returns

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

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

set_log_level

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

get_log_level

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

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

std::reference_wrapper<M> motor

Pointer to the motor to use for haptics.

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.

Logger::Verbosity log_level

Log level to use for the haptics.