2025-09-09 16:19:57 +01:00
|
|
|
/**
|
|
|
|
|
* @file DShotRMT.h
|
2025-10-01 23:31:24 +01:00
|
|
|
* @brief DShot signal generation using ESP32 RMT with bidirectional support
|
2025-09-09 16:19:57 +01:00
|
|
|
* @author Wastl Kraus
|
2025-10-01 23:31:24 +01:00
|
|
|
* @date 2025-06-11
|
2025-09-09 16:19:57 +01:00
|
|
|
* @license MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
#include <atomic>
|
|
|
|
|
#include <driver/gpio.h>
|
2025-09-09 16:19:57 +01:00
|
|
|
#include <driver/rmt_tx.h>
|
|
|
|
|
#include <driver/rmt_rx.h>
|
|
|
|
|
|
2025-10-01 08:48:26 +01:00
|
|
|
#include "dshot_definitions.h"
|
2025-10-01 15:50:24 +01:00
|
|
|
#include "dshot_config.h"
|
2025-10-01 08:48:26 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
//
|
2025-09-09 16:19:57 +01:00
|
|
|
class DShotRMT
|
|
|
|
|
{
|
|
|
|
|
public:
|
2025-10-01 23:31:24 +01:00
|
|
|
// Constructor with GPIO number
|
|
|
|
|
DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count);
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Constructor using pin number
|
|
|
|
|
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count);
|
2025-09-18 10:23:39 +01:00
|
|
|
|
2025-10-01 08:48:26 +01:00
|
|
|
// Destructor
|
2025-09-09 16:19:57 +01:00
|
|
|
~DShotRMT();
|
|
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Initialize DShotRMT
|
2025-09-09 16:19:57 +01:00
|
|
|
dshot_result_t begin();
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Send throttle value
|
|
|
|
|
dshot_result_t sendThrottle(uint16_t throttle);
|
|
|
|
|
|
|
|
|
|
// Send throttle value as a percentage
|
2025-09-20 15:47:39 +01:00
|
|
|
dshot_result_t sendThrottlePercent(float percent);
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Sends a DShot command (0-47) to the ESC by accepting an integer value.
|
|
|
|
|
dshot_result_t sendCommand(uint16_t command_value);
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 08:48:26 +01:00
|
|
|
// Sends a DShot command (0-47) to the ESC.
|
|
|
|
|
dshot_result_t sendCommand(dshotCommands_e command);
|
2025-10-01 23:31:24 +01:00
|
|
|
|
2025-10-01 15:50:24 +01:00
|
|
|
// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay.
|
|
|
|
|
dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us);
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Get telemetry data
|
2025-10-01 08:48:26 +01:00
|
|
|
dshot_result_t getTelemetry();
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Reverse motor direction directly
|
2025-09-18 15:10:59 +01:00
|
|
|
dshot_result_t setMotorSpinDirection(bool reversed);
|
2025-09-25 13:03:55 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Use with caution
|
2025-09-18 15:10:59 +01:00
|
|
|
dshot_result_t saveESCSettings();
|
2025-09-18 10:23:39 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Getters for DShot info
|
2025-09-25 13:03:55 +01:00
|
|
|
dshot_mode_t getMode() const { return _mode; }
|
|
|
|
|
bool isBidirectional() const { return _is_bidirectional; }
|
2025-10-01 23:31:24 +01:00
|
|
|
uint16_t getThrottleValue() const { return _last_throttle; }
|
2025-09-25 13:03:55 +01:00
|
|
|
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
|
|
|
|
|
2025-09-09 16:19:57 +01:00
|
|
|
private:
|
|
|
|
|
gpio_num_t _gpio;
|
|
|
|
|
dshot_mode_t _mode;
|
|
|
|
|
bool _is_bidirectional;
|
2025-09-20 15:47:39 +01:00
|
|
|
uint16_t _motor_magnet_count;
|
2025-10-01 23:31:24 +01:00
|
|
|
dshot_timing_us_t _dshot_timing;
|
2025-10-01 08:48:26 +01:00
|
|
|
rmt_channel_handle_t _rmt_tx_channel = nullptr;
|
|
|
|
|
rmt_channel_handle_t _rmt_rx_channel = nullptr;
|
|
|
|
|
rmt_encoder_handle_t _dshot_encoder = nullptr;
|
2025-10-01 23:31:24 +01:00
|
|
|
rmt_ticks_t _rmt_ticks;
|
|
|
|
|
uint16_t _pulse_level = 1; // Default to high
|
|
|
|
|
uint16_t _idle_level = 0; // Default to low
|
|
|
|
|
uint64_t _last_transmission_time_us = 0;
|
|
|
|
|
uint64_t _frame_timer_us = 0;
|
|
|
|
|
uint16_t _last_throttle = 0;
|
|
|
|
|
dshot_packet_t _packet;
|
|
|
|
|
uint16_t _encoded_frame_value = 0;
|
|
|
|
|
uint64_t _last_command_timestamp = 0;
|
2025-09-09 16:19:57 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Telemetry related
|
|
|
|
|
std::atomic<uint16_t> _last_erpm_atomic = 0;
|
|
|
|
|
std::atomic<bool> _telemetry_ready_flag_atomic = false;
|
|
|
|
|
rmt_rx_event_callbacks_t _rx_event_callbacks = {
|
|
|
|
|
.on_recv_done = _on_rx_done,
|
|
|
|
|
};
|
2025-10-01 15:50:24 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
// Private helper functions
|
|
|
|
|
bool _isValidCommand(dshotCommands_e command) const;
|
|
|
|
|
dshot_result_t _executeCommand(dshotCommands_e command);
|
2025-10-01 08:48:26 +01:00
|
|
|
dshot_packet_t _buildDShotPacket(const uint16_t &value) const;
|
|
|
|
|
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const;
|
|
|
|
|
uint16_t _calculateCRC(const uint16_t &data) const;
|
2025-09-15 14:56:04 +01:00
|
|
|
void _preCalculateRMTTicks();
|
2025-09-09 16:19:57 +01:00
|
|
|
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
2025-10-01 23:31:24 +01:00
|
|
|
uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const;
|
|
|
|
|
bool IRAM_ATTR _isFrameIntervalElapsed() const;
|
2025-09-26 18:24:50 +01:00
|
|
|
void _recordFrameTransmissionTime();
|
2025-09-18 10:23:39 +01:00
|
|
|
|
2025-09-13 10:54:30 +01:00
|
|
|
// Static Callback Functions
|
2025-10-01 23:31:24 +01:00
|
|
|
static bool IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
2025-09-25 15:15:36 +01:00
|
|
|
};
|
2025-10-01 08:48:26 +01:00
|
|
|
|
2025-10-01 23:31:24 +01:00
|
|
|
#include "dshot_utils.h" // Workround for util functions
|