This commit is contained in:
Wastl Kraus 2025-11-28 01:22:50 +01:00
parent 7985030df6
commit 7a5c425fa8
9 changed files with 140 additions and 98 deletions

View File

@ -28,6 +28,14 @@ Here's an example of the output from the `dshot300` example sketch:
- **Lightweight:** The core library has no external dependencies. - **Lightweight:** The core library has no external dependencies.
- **Arduino and ESP-IDF Compatible:** Can be used in both Arduino and ESP-IDF projects. - **Arduino and ESP-IDF Compatible:** Can be used in both Arduino and ESP-IDF projects.
## How it Works
The library is architected around a single C++ class, `DShotRMT`. It abstracts the ESP32's RMT (Remote Control) peripheral, which is a hardware timer peripheral capable of generating and receiving precisely timed signals.
1. **Signal Generation (TX):** The library uses an RMT 'bytes_encoder'. This encoder is configured with the specific pulse durations for DShot '0' and '1' bits based on the selected speed (e.g., DSHOT300, DSHOT600). When a user calls `sendThrottle()`, the library constructs a 16-bit DShot frame (11-bit throttle, 1-bit telemetry request, 4-bit CRC) and hands it to the RMT encoder. The RMT hardware then autonomously generates the correct electrical signal on the specified GPIO pin.
2. **Bidirectional Telemetry (RX):** For bidirectional communication, the library configures a second RMT channel in receive mode on the same GPIO. An interrupt service routine (`_on_rx_done`) is registered. When the ESC sends back a telemetry signal, the RMT peripheral captures it and triggers the interrupt. The interrupt code decodes the GCR-encoded signal, validates its CRC, and stores the resulting eRPM value in a thread-safe `atomic` variable. The main application can then poll for this data using the `getTelemetry()` method.
## ⏱️ DShot Timing Information ## ⏱️ DShot Timing Information
The DShot protocol defines specific timing characteristics for each mode. The following table outlines the bit length, T1H (high time for a '1' bit), T0H (high time for a '0' bit), and frame length for the supported DShot modes: The DShot protocol defines specific timing characteristics for each mode. The following table outlines the bit length, T1H (high time for a '1' bit), T0H (high time for a '0' bit), and frame length for the supported DShot modes:
@ -119,18 +127,14 @@ The main class is `DShotRMT`. Here are the most important methods:
- `sendCommand(dshotCommands_e command)`: Sends a DShot command to the ESC. Automatically handles repetitions and delays for specific commands (e.g., `DSHOT_CMD_SAVE_SETTINGS`). - `sendCommand(dshotCommands_e command)`: Sends a DShot command to the ESC. Automatically handles repetitions and delays for specific commands (e.g., `DSHOT_CMD_SAVE_SETTINGS`).
- `sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us)`: Sends a DShot command to the ESC with a specified repeat count and delay. This is a blocking function. - `sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us)`: Sends a DShot command to the ESC with a specified repeat count and delay. This is a blocking function.
- `sendCommand(uint16_t command_value)`: Sends a DShot command to the ESC by accepting an integer value. It validates the input and then calls `sendCommand(dshotCommands_e command)`. - `sendCommand(uint16_t command_value)`: Sends a DShot command to the ESC by accepting an integer value. It validates the input and then calls `sendCommand(dshotCommands_e command)`.
- `getTelemetry(uint16_t magnet_count = 0)`: Retrieves telemetry data from the ESC. If `magnet_count` is 0, uses the stored motor magnet count. - `sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us)`: Sends a custom DShot command to the ESC. Advanced feature, use with caution.
- `getESCInfo()`: Sends a command to the ESC to request ESC information. - `getTelemetry()`: Retrieves telemetry data from the ESC. If bidirectional DShot is enabled, this function will return the last received telemetry data.
- `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal. - `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal.
- `saveESCSettings()`: Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory. - `saveESCSettings()`: Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory.
- `printDShotResult(dshot_result_t &result, Stream &output = Serial)`: Prints the result of a DShot operation to the specified output stream.
- `printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)`: Prints detailed DShot signal information for a given DShotRMT instance.
- `printCpuInfo(Stream &output = Serial)`: Prints detailed CPU information.
- `setMotorMagnetCount(uint16_t magnet_count)`: Sets the motor magnet count for RPM calculation.
- `getMode()`: Gets the current DShot mode. - `getMode()`: Gets the current DShot mode.
- `isBidirectional()`: Checks if bidirectional DShot is enabled. - `isBidirectional()`: Checks if bidirectional DShot is enabled.
- `getEncodedFrameValue()`: Gets the last encoded DShot frame value.
- `getThrottleValue()`: Gets the last transmitted throttle value. - `getThrottleValue()`: Gets the last transmitted throttle value.
- `getEncodedFrameValue()`: Gets the last encoded DShot frame value.
## ⚙️ ESP-IDF Integration ## ⚙️ ESP-IDF Integration

View File

@ -65,7 +65,7 @@ void loop()
if (input.length() > 0) if (input.length() > 0)
{ {
handleSerialInput(input, throttle, continuous_throttle); handleSerialInput(input, throttle, continuous_throttle, motor01);
} }
} }
@ -118,14 +118,14 @@ void printMenu()
} }
// //
void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle) void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle, DShotRMT &session)
{ {
if (input == "0") if (input == "0")
{ {
// Stop motor // Stop motor
throttle = 0; throttle = 0;
continuous_throttle = true; continuous_throttle = true;
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP); dshot_result_t result = session.sendCommand(DSHOT_CMD_MOTOR_STOP);
printDShotResult(result); printDShotResult(result);
} }
else if (input == "info") else if (input == "info")

View File

@ -8,6 +8,18 @@
DShotRMT KEYWORD1 DShotRMT KEYWORD1
#######################################
# Methods (KEYWORD2)
#######################################
begin KEYWORD2
sendThrottle KEYWORD2
sendThrottlePercent KEYWORD2
sendCommand KEYWORD2
sendCustomCommand KEYWORD2
getTelemetry KEYWORD2
setMotorSpinDirection KEYWORD2
saveESCSettings KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)
@ -53,15 +65,9 @@ DSHOT_CMD_LED2_OFF LITERAL1
DSHOT_CMD_LED3_OFF LITERAL1 DSHOT_CMD_LED3_OFF LITERAL1
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF LITERAL1 DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF LITERAL1
DSHOT_CMD_SILENT_MODE_ON_OFF LITERAL1 DSHOT_CMD_SILENT_MODE_ON_OFF LITERAL1
DSHOT_CMD_MAX LITERAL1
# DShot Command Types
DSHOT_CMD_TYPE_INLINE LITERAL1
DSHOT_CMD_TYPE_BLOCKING LITERAL1
# Protocol Constants # Protocol Constants
DSHOT_BITS_PER_FRAME LITERAL1 DSHOT_BITS_PER_FRAME LITERAL1
DSHOT_SWITCH_TIME LITERAL1
DSHOT_NULL_PACKET LITERAL1 DSHOT_NULL_PACKET LITERAL1
DSHOT_RX_TIMEOUT_MS LITERAL1 DSHOT_RX_TIMEOUT_MS LITERAL1
GCR_BITS_PER_FRAME LITERAL1 GCR_BITS_PER_FRAME LITERAL1
@ -69,11 +75,8 @@ GCR_BITS_PER_FRAME LITERAL1
# RMT Constants # RMT Constants
DSHOT_CLOCK_SRC_DEFAULT LITERAL1 DSHOT_CLOCK_SRC_DEFAULT LITERAL1
DSHOT_RMT_RESOLUTION LITERAL1 DSHOT_RMT_RESOLUTION LITERAL1
RMT_BUFFER_SIZE LITERAL1
RMT_BUFFER_SYMBOLS LITERAL1 RMT_BUFFER_SYMBOLS LITERAL1
RMT_QUEUE_DEPTH LITERAL1 RMT_QUEUE_DEPTH LITERAL1
DSHOT_PULSE_MIN LITERAL1
DSHOT_PULSE_MAX LITERAL1
# Status Constants # Status Constants
DSHOT_OK LITERAL1 DSHOT_OK LITERAL1

View File

@ -8,6 +8,27 @@
#include "DShotRMT.h" #include "DShotRMT.h"
// Configuration Constants
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
static constexpr auto DSHOT_PADDING_US = 20; // Pause between frames
static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer frame
static constexpr auto POLE_PAIRS_MIN = 1;
static constexpr auto MAGNETS_PER_POLE_PAIR = 2;
static constexpr auto NO_DSHOT_TELEMETRY = 0;
static constexpr auto DSHOT_PULSE_MIN_NS = 800; // 0.8us minimum pulse
static constexpr auto DSHOT_PULSE_MAX_NS = 8000; // 8.0us maximum pulse
static constexpr auto DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX;
static constexpr auto DSHOT_TELEMETRY_BIT_POSITION = 11;
static constexpr auto DSHOT_CRC_BIT_SHIFT = 4;
// Command Constants
static constexpr auto DEFAULT_CMD_DELAY_US = 10;
static constexpr auto DEFAULT_CMD_REPEAT_COUNT = 1;
static constexpr auto SETTINGS_COMMAND_REPEATS = 10;
static constexpr auto SETTINGS_COMMAND_DELAY_US = 5;
// Constructor with GPIO number // Constructor with GPIO number
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count) DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
: _gpio(gpio), : _gpio(gpio),
@ -30,28 +51,7 @@ DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, ui
// Destructor // Destructor
DShotRMT::~DShotRMT() DShotRMT::~DShotRMT()
{ {
// Cleanup TX channel _cleanupRmtResources();
if (_rmt_tx_channel)
{
rmt_disable(_rmt_tx_channel);
rmt_del_channel(_rmt_tx_channel);
_rmt_tx_channel = nullptr;
}
// Cleanup RX channel
if (_rmt_rx_channel)
{
rmt_disable(_rmt_rx_channel);
rmt_del_channel(_rmt_rx_channel);
_rmt_rx_channel = nullptr;
}
// Cleanup encoder
if (_dshot_encoder)
{
rmt_del_encoder(_dshot_encoder);
_dshot_encoder = nullptr;
}
} }
// Initialize DShotRMT // Initialize DShotRMT
@ -92,6 +92,8 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
// A throttle value of 0 is a disarm command // A throttle value of 0 is a disarm command
if (throttle == 0) if (throttle == 0)
{ {
// just to be sure
_last_throttle = 0;
return sendCommand(DSHOT_CMD_MOTOR_STOP); return sendCommand(DSHOT_CMD_MOTOR_STOP);
} }
@ -99,19 +101,19 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
_last_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); _last_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
_packet = _buildDShotPacket(_last_throttle); _packet = _buildDShotPacket(_last_throttle);
return _sendDShotFrame(_packet); return _sendPacket(_packet);
} }
// Send throttle value as a percentage // Send throttle value as a percentage
dshot_result_t DShotRMT::sendThrottlePercent(float percent) dshot_result_t DShotRMT::sendThrottlePercent(float percent)
{ {
if (percent < 0.0f || percent > 100.0f) if (percent < DSHOT_PERCENT_MIN || percent > DSHOT_PERCENT_MAX)
{ {
return {false, DSHOT_PERCENT_NOT_IN_RANGE}; return {false, DSHOT_PERCENT_NOT_IN_RANGE};
} }
// Map percent to DShot throttle range // Map percent to DShot throttle range
uint16_t throttle = static_cast<uint16_t>(DSHOT_THROTTLE_MIN + ((DSHOT_THROTTLE_MAX - DSHOT_THROTTLE_MIN) / 100.0f) * percent); uint16_t throttle = static_cast<uint16_t>(DSHOT_THROTTLE_MIN + ((DSHOT_THROTTLE_MAX - DSHOT_THROTTLE_MIN) / DSHOT_PERCENT_MAX) * percent);
return sendThrottle(throttle); return sendThrottle(throttle);
} }
@ -236,11 +238,45 @@ dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed)
return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
} }
// Sends a raw DShot command to the ESC. dshot_result_t DShotRMT::sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us)
dshot_result_t DShotRMT::sendRawCommand(uint16_t command_value)
{ {
_packet = _buildDShotPacket(command_value); // Validate the integer command value before casting
return _sendDShotFrame(_packet); if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX_VALUE)
{
return {false, DSHOT_COMMAND_NOT_VALID};
}
dshot_result_t result = {false, DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
bool all_successful = true;
// Send command multiple times with delay
for (uint16_t i = 0; i < repeat_count; i++)
{
dshot_result_t single_result = _sendRawDshotFrame(command_value);
if (!single_result.success)
{
all_successful = false;
result.result_code = single_result.result_code;
break;
}
// Add delay between repetitions (except for last repetition)
if (i < repeat_count - 1)
{
delayMicroseconds(delay_us);
}
}
result.success = all_successful;
if (result.success)
{
result.result_code = DSHOT_COMMAND_SUCCESS;
}
return result;
} }
// Use with caution // Use with caution
@ -255,13 +291,19 @@ bool DShotRMT::_isValidCommand(dshotCommands_e command) const
return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX); return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX);
} }
dshot_result_t DShotRMT::_sendRawDshotFrame(uint16_t value)
{
_packet = _buildDShotPacket(value);
return _sendPacket(_packet);
}
// Executes a single DShot command by building and sending a DShot frame. // Executes a single DShot command by building and sending a DShot frame.
dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command) dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command)
{ {
uint64_t start_time = esp_timer_get_time(); uint64_t start_time = esp_timer_get_time();
_packet = _buildDShotPacket(static_cast<uint16_t>(command)); _packet = _buildDShotPacket(static_cast<uint16_t>(command));
dshot_result_t result = _sendDShotFrame(_packet); dshot_result_t result = _sendPacket(_packet);
uint64_t end_time = esp_timer_get_time(); uint64_t end_time = esp_timer_get_time();
_last_command_timestamp = end_time; _last_command_timestamp = end_time;
@ -325,7 +367,7 @@ void DShotRMT::_preCalculateRMTTicks()
} }
// Private Frame Processing Functions // Private Frame Processing Functions
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) dshot_result_t DShotRMT::_sendPacket(const dshot_packet_t &packet)
{ {
// Ensure enough time has passed since the last transmission // Ensure enough time has passed since the last transmission
if (!_isFrameIntervalElapsed()) if (!_isFrameIntervalElapsed())
@ -362,7 +404,9 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
rmt_transmit_config_t tx_config = {}; // Initialize all members to zero rmt_transmit_config_t tx_config = {}; // Initialize all members to zero
tx_config.loop_count = 0; // No automatic loops - real-time calculation tx_config.loop_count = 0; // No automatic loops - real-time calculation
// TODO: Find out, why this is needed // In bidirectional mode, the RMT RX channel must be disabled before transmitting.
// This is to prevent the receiver from picking up the transmitted signal, which would cause a loopback issue.
// The ESP32's RMT peripheral TX and RX channels on the same pin are not fully independent.
if (_is_bidirectional) if (_is_bidirectional)
{ {
// Disable RMT RX for sending // Disable RMT RX for sending

View File

@ -56,8 +56,14 @@ public:
// Sends a DShot command to the ESC with a specified repeat count and delay. // Sends a DShot command 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); dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us);
// Sends a raw DShot command to the ESC. /**
dshot_result_t sendRawCommand(uint16_t command_value); * @brief Sends a custom DShot command to the ESC. Advanced feature, use with caution.
* @param command_value The raw command value (0-47).
* @param repeat_count The number of times to send the command.
* @param delay_us The delay in microseconds between repetitions.
* @return dshot_result_t The result of the operation.
*/
dshot_result_t sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us);
// Retrieves telemetry data from the ESC. // Retrieves telemetry data from the ESC.
dshot_result_t getTelemetry(); dshot_result_t getTelemetry();
@ -75,6 +81,7 @@ public:
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
private: private:
dshot_result_t _sendRawDshotFrame(uint16_t value);
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); 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);
// DShot Configuration Parameters // DShot Configuration Parameters
@ -115,7 +122,7 @@ private:
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; // Combines packet data into a 16-bit DShot frame value uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; // Combines packet data into a 16-bit DShot frame value
uint16_t _calculateCRC(const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame uint16_t _calculateCRC(const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame
void _preCalculateRMTTicks(); // Pre-calculates RMT timing ticks for the selected DShot mode void _preCalculateRMTTicks(); // Pre-calculates RMT timing ticks for the selected DShot mode
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); // Sends a DShot frame via RMT TX channel dshot_result_t _sendPacket(const dshot_packet_t &packet); // Sends a DShot frame via RMT TX channel
uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; // Decodes a received RMT symbol array into an eRPM value uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; // Decodes a received RMT symbol array into an eRPM value
bool IRAM_ATTR _isFrameIntervalElapsed() const; // Checks if enough time has passed since the last frame transmission bool IRAM_ATTR _isFrameIntervalElapsed() const; // Checks if enough time has passed since the last frame transmission
void _recordFrameTransmissionTime(); // Records the current time as the last frame transmission time void _recordFrameTransmissionTime(); // Records the current time as the last frame transmission time

View File

@ -16,6 +16,8 @@ static constexpr uint16_t DSHOT_FRAME_LENGTH = 16; // 11 throttle bits
static constexpr uint16_t DSHOT_BITS_PER_FRAME = 16; static constexpr uint16_t DSHOT_BITS_PER_FRAME = 16;
static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047; // Maximum throttle value (0-2047) static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047; // Maximum throttle value (0-2047)
static constexpr uint16_t DSHOT_THROTTLE_MIN = 48; // Minimum throttle value for motor spin static constexpr uint16_t DSHOT_THROTTLE_MIN = 48; // Minimum throttle value for motor spin
static constexpr float DSHOT_PERCENT_MIN = 0.0f;
static constexpr float DSHOT_PERCENT_MAX = 100.0f;
static constexpr uint16_t DSHOT_CMD_MIN = 0; // Minimum command value static constexpr uint16_t DSHOT_CMD_MIN = 0; // Minimum command value
static constexpr uint16_t DSHOT_CMD_MAX = 47; // Maximum command value static constexpr uint16_t DSHOT_CMD_MAX = 47; // Maximum command value
static constexpr uint16_t DSHOT_TELEMETRY_BIT_MASK = 0x0800; // Bit mask for telemetry request bit (11th bit) static constexpr uint16_t DSHOT_TELEMETRY_BIT_MASK = 0x0800; // Bit mask for telemetry request bit (11th bit)
@ -136,30 +138,11 @@ static constexpr int DSHOT_OK = 0;
static constexpr int DSHOT_ERROR = 1; static constexpr int DSHOT_ERROR = 1;
// Configuration Constants // Configuration Constants
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
static constexpr auto DSHOT_RMT_RESOLUTION = 8000000; // 8 MHz resolution static constexpr auto DSHOT_RMT_RESOLUTION = 8000000; // 8 MHz resolution
static constexpr auto RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / 1000000; // RMT Ticks per microsecond static constexpr auto RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / 1000000; // RMT Ticks per microsecond
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
static constexpr auto DSHOT_PADDING_US = 20; // Pause between frames
static constexpr auto RMT_BUFFER_SYMBOLS = 64; static constexpr auto RMT_BUFFER_SYMBOLS = 64;
static constexpr auto RMT_QUEUE_DEPTH = 1; static constexpr auto RMT_QUEUE_DEPTH = 1;
static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer frame
static constexpr auto POLE_PAIRS_MIN = 1;
static constexpr auto MAGNETS_PER_POLE_PAIR = 2;
static constexpr auto NO_DSHOT_TELEMETRY = 0;
static constexpr auto DSHOT_PULSE_MIN_NS = 800; // 0.8us minimum pulse
static constexpr auto DSHOT_PULSE_MAX_NS = 8000; // 8.0us maximum pulse
static constexpr auto DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX;
static constexpr auto DSHOT_TELEMETRY_BIT_POSITION = 11;
static constexpr auto DSHOT_CRC_BIT_SHIFT = 4;
// Command Constants
static constexpr auto DEFAULT_CMD_DELAY_US = 10;
static constexpr auto DEFAULT_CMD_REPEAT_COUNT = 1;
static constexpr auto SETTINGS_COMMAND_REPEATS = 10;
static constexpr auto SETTINGS_COMMAND_DELAY_US = 5;
// Timing parameters for each DShot mode // Timing parameters for each DShot mode
const dshot_timing_us_t DSHOT_TIMING_US[] = { const dshot_timing_us_t DSHOT_TIMING_US[] = {

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include "dshot_definitions.h"
// Forward declaration of the DShotRMT class to break circular dependency // Forward declaration of the DShotRMT class to break circular dependency
class DShotRMT; class DShotRMT;
@ -28,9 +29,9 @@ static constexpr char ENCODING_SUCCESS[] = "Packet encoded successfully";
static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully"; static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully";
static constexpr char TRANSMISSION_FAILED[] = "Transmission failed!"; static constexpr char TRANSMISSION_FAILED[] = "Transmission failed!";
static constexpr char RECEIVER_FAILED[] = "RMT receiver failed!"; static constexpr char RECEIVER_FAILED[] = "RMT receiver failed!";
static constexpr char THROTTLE_NOT_IN_RANGE[] = "Throttle not in range! (48 - 2047)"; static constexpr char THROTTLE_NOT_IN_RANGE[] = "Throttle not in range!";
static constexpr char PERCENT_NOT_IN_RANGE[] = "Percent not in range! (0.0 - 100.0)"; static constexpr char PERCENT_NOT_IN_RANGE[] = "Percent not in range!";
static constexpr char COMMAND_NOT_VALID[] = "Command not valid! (0 - 47)"; static constexpr char COMMAND_NOT_VALID[] = "Command not valid!";
static constexpr char BIDIR_NOT_ENABLED[] = "Bidirectional DShot not enabled!"; static constexpr char BIDIR_NOT_ENABLED[] = "Bidirectional DShot not enabled!";
static constexpr char TELEMETRY_SUCCESS[] = "Valid Telemetric Frame received!"; static constexpr char TELEMETRY_SUCCESS[] = "Valid Telemetric Frame received!";
static constexpr char TELEMETRY_FAILED[] = "No valid Telemetric Frame received!"; static constexpr char TELEMETRY_FAILED[] = "No valid Telemetric Frame received!";
@ -100,6 +101,24 @@ inline const char *get_result_code_str(dshot_msg_code_t code)
} }
} }
// Helper to get DShot mode string
inline const char *get_dshot_mode_str(dshot_mode_t mode)
{
switch (mode)
{
case DSHOT150:
return "DSHOT150";
case DSHOT300:
return "DSHOT300";
case DSHOT600:
return "DSHOT600";
case DSHOT1200:
return "DSHOT1200";
default:
return "DSHOT_OFF";
}
}
// Helper to quick print DShot result codes // Helper to quick print DShot result codes
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial) inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
{ {
@ -118,25 +137,7 @@ inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
inline void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial) inline void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)
{ {
output.println("\n === DShot Signal Info === "); output.println("\n === DShot Signal Info === ");
output.printf("Current Mode: %s\n", get_dshot_mode_str(dshot_rmt.getMode()));
uint16_t dshot_mode_val = 0;
switch (dshot_rmt.getMode())
{
case DSHOT150:
dshot_mode_val = 150;
break;
case DSHOT300:
dshot_mode_val = 300;
break;
case DSHOT600:
dshot_mode_val = 600;
break;
case DSHOT1200:
dshot_mode_val = 1200;
break;
}
output.printf("Current Mode: DSHOT%d\n", dshot_mode_val);
output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO"); output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO");
output.printf("Current Packet: "); output.printf("Current Packet: ");