Revert "Use official RMT Encoder API"

This reverts commit 96b5595c72.
This commit is contained in:
Wastl Kraus 2025-09-26 19:01:55 +02:00
parent 992a968ee4
commit 0028db7dca
9 changed files with 162 additions and 171 deletions

View File

@ -158,3 +158,4 @@ jobs:
else else
echo "| 🔨 Compilation | ❌ Failed | One or more examples failed to compile |" >> $GITHUB_STEP_SUMMARY echo "| 🔨 Compilation | ❌ Failed | One or more examples failed to compile |" >> $GITHUB_STEP_SUMMARY
fi fi

View File

@ -2,7 +2,7 @@
[![Arduino CI](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml/badge.svg)](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml) [![Arduino CI](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml/badge.svg)](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml)
A C++ library for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT encoder API** (`rmt_tx.h` / `rmt_rx.h`). It leverages the standard `rmt_bytes_encoder` to ensure an efficient, hardware-timed, and maintainable implementation. The library provides a simple way to control brushless motors in both Arduino and ESP-IDF projects. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch. A C++ library for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT encoder API** (`rmt_tx.h` / `rmt_rx.h`). It provides a simple, efficient, and hardware-timed way to control brushless motors in both Arduino and ESP-IDF projects. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch.
## 🚀 Core Features ## 🚀 Core Features

View File

@ -88,7 +88,7 @@ void loop()
if (IS_BIDIRECTIONAL) if (IS_BIDIRECTIONAL)
{ {
dshot_result_t telem_result = motor01.getTelemetry(); dshot_result_t telem_result = motor01.getTelemetry();
DShotRMT::printDShotResult(telem_result); printDShotResult(telem_result);
} }
USB_SERIAL.println("Type 'help' to show Menu"); USB_SERIAL.println("Type 'help' to show Menu");
@ -128,7 +128,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
throttle = 0; throttle = 0;
continuous_throttle = true; continuous_throttle = true;
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP); dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else if (input == "info") else if (input == "info")
{ {
@ -137,7 +137,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
else if (input == "rpm" && IS_BIDIRECTIONAL) else if (input == "rpm" && IS_BIDIRECTIONAL)
{ {
dshot_result_t result = motor01.getTelemetry(); dshot_result_t result = motor01.getTelemetry();
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else if (input.startsWith("cmd ")) else if (input.startsWith("cmd "))
{ {
@ -149,7 +149,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX) if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
{ {
dshot_result_t result = motor01.sendCommand(cmd_num); dshot_result_t result = motor01.sendCommand(cmd_num);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -171,7 +171,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
continuous_throttle = true; continuous_throttle = true;
dshot_result_t result = motor01.sendThrottle(throttle); dshot_result_t result = motor01.sendThrottle(throttle);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {

View File

@ -94,7 +94,7 @@ void handleSerialInput(const String &input)
{ {
// Stop motor // Stop motor
dshot_result_t result = motor01.sendThrottlePercent(0.0f); dshot_result_t result = motor01.sendThrottlePercent(0.0f);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else if (input == "info") else if (input == "info")
{ {
@ -103,7 +103,7 @@ void handleSerialInput(const String &input)
else if (input == "rpm" && IS_BIDIRECTIONAL) else if (input == "rpm" && IS_BIDIRECTIONAL)
{ {
dshot_result_t result = motor01.getTelemetry(); dshot_result_t result = motor01.getTelemetry();
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else if (input.startsWith("cmd ")) else if (input.startsWith("cmd "))
{ {
@ -113,7 +113,7 @@ void handleSerialInput(const String &input)
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX) if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
{ {
dshot_result_t result = motor01.sendCommand(cmd_num); dshot_result_t result = motor01.sendCommand(cmd_num);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -132,7 +132,7 @@ void handleSerialInput(const String &input)
if (throttle_percent >= 0.0f && throttle_percent <= 100.0f) if (throttle_percent >= 0.0f && throttle_percent <= 100.0f)
{ {
dshot_result_t result = motor01.sendThrottlePercent(throttle_percent); dshot_result_t result = motor01.sendThrottlePercent(throttle_percent);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {

View File

@ -178,7 +178,7 @@ void loop()
if (IS_BIDIRECTIONAL && isArmed) if (IS_BIDIRECTIONAL && isArmed)
{ {
dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
DShotRMT::printDShotResult(telem_result); printDShotResult(telem_result);
} }
USB_SERIAL.println("Type 'help' to show Menu"); USB_SERIAL.println("Type 'help' to show Menu");
@ -495,7 +495,7 @@ void handleSerialInput(const String &input)
if (isArmed) if (isArmed)
{ {
dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -520,7 +520,7 @@ void handleSerialInput(const String &input)
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX) if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
{ {
dshot_result_t result = motor01.sendCommand(cmd_num); dshot_result_t result = motor01.sendCommand(cmd_num);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -581,7 +581,7 @@ void handleSerialInput(const String &input)
throttle = 0; throttle = 0;
continuous_throttle = false; continuous_throttle = false;
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP); dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
DShotRMT::printDShotResult(result); printDShotResult(result);
return; return;
} }

View File

@ -146,7 +146,7 @@ void loop()
if (IS_BIDIRECTIONAL && isArmed) if (IS_BIDIRECTIONAL && isArmed)
{ {
dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
DShotRMT::printDShotResult(telem_result); printDShotResult(telem_result);
} }
USB_SERIAL.println("Type 'help' to show Menu"); USB_SERIAL.println("Type 'help' to show Menu");
@ -264,7 +264,7 @@ void handleSerialInput(const String &input)
if (isArmed) if (isArmed)
{ {
dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -288,7 +288,7 @@ void handleSerialInput(const String &input)
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX) if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
{ {
dshot_result_t result = motor01.sendCommand(cmd_num); dshot_result_t result = motor01.sendCommand(cmd_num);
DShotRMT::printDShotResult(result); printDShotResult(result);
} }
else else
{ {
@ -341,7 +341,7 @@ void handleSerialInput(const String &input)
throttle = 0; throttle = 0;
continuous_throttle = false; continuous_throttle = false;
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP); dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
DShotRMT::printDShotResult(result); printDShotResult(result);
return; return;
} }

View File

@ -23,8 +23,8 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, ui
_last_command_timestamp(0), _last_command_timestamp(0),
_encoded_frame_value(0), _encoded_frame_value(0),
_packet{0}, _packet{0},
_pulse_level(1), // DShot standard: signal is idle-low, so pulses start by going HIGH _level0(1), // DShot standard: signal is idle-low, so pulses start by going HIGH
_idle_level(0), // DShot standard: signal returns to LOW after the high pulse _level1(0), // DShot standard: signal returns to LOW after the high pulse
_rmt_tx_channel(nullptr), _rmt_tx_channel(nullptr),
_rmt_rx_channel(nullptr), _rmt_rx_channel(nullptr),
_dshot_encoder(nullptr), _dshot_encoder(nullptr),
@ -355,25 +355,9 @@ dshot_result_t DShotRMT::_initRXChannel()
dshot_result_t DShotRMT::_initDShotEncoder() dshot_result_t DShotRMT::_initDShotEncoder()
{ {
rmt_bytes_encoder_config_t encoder_config = { rmt_copy_encoder_config_t encoder_config = {};
.bit0 = {
.duration0 = _rmt_ticks.t0h_ticks,
.level0 = _pulse_level,
.duration1 = _rmt_ticks.t0l_ticks,
.level1 = _idle_level,
},
.bit1 = {
.duration0 = _rmt_ticks.t1h_ticks,
.level0 = _pulse_level,
.duration1 = _rmt_ticks.t1l_ticks,
.level1 = _idle_level,
},
.flags = {
.msb_first = 1 // DShot is MSB first
}
};
if (rmt_new_bytes_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
{ {
return {false, dshot_msg_code_t::DSHOT_ERROR_ENCODER_INIT_FAILED}; return {false, dshot_msg_code_t::DSHOT_ERROR_ENCODER_INIT_FAILED};
} }
@ -440,27 +424,50 @@ void DShotRMT::_preCalculateRMTTicks()
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) dshot_result_t DShotRMT::_sendDShotFrame(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 (!_timer_signal())
{ {
return {true, dshot_msg_code_t::DSHOT_ERROR_NONE}; return {true, dshot_msg_code_t::DSHOT_ERROR_NONE};
} }
_encoded_frame_value = _buildDShotFrameValue(packet); rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME];
dshot_result_t result = _encodeDShotFrame(packet, tx_symbols);
// The DShot frame is 16 bits, which is 2 bytes if (!result.success)
size_t tx_size_bytes = sizeof(_encoded_frame_value); {
return result;
}
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &_encoded_frame_value, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK) size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK)
{ {
return {false, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_FAILED}; return {false, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_FAILED};
} }
_recordFrameTransmissionTime(); // Reset the timer for the next frame _timer_reset(); // Reset the timer for the next frame
return {true, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_SUCCESS}; return {true, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_SUCCESS};
} }
// This function needs to be fast, as it generates the RMT symbols just before sending
dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols)
{
_encoded_frame_value = _buildDShotFrameValue(packet);
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
{
int bit_position = DSHOT_BITS_PER_FRAME - 1 - i;
bool bit = (_encoded_frame_value >> bit_position) & 1;
// A '1' bit has a longer high-time, a '0' bit has a shorter high-time
symbols[i].level0 = _level0; // Go HIGH
symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks;
symbols[i].level1 = _level1; // Go LOW
symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks;
}
return {true, dshot_msg_code_t::DSHOT_ERROR_ENCODING_SUCCESS};
}
// Placed in IRAM for high performance, as it's called from an ISR context // Placed in IRAM for high performance, as it's called from an ISR context
uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
@ -503,7 +510,7 @@ uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
} }
// Timing Control Functions // Timing Control Functions
bool IRAM_ATTR DShotRMT::_isFrameIntervalElapsed() bool IRAM_ATTR DShotRMT::_timer_signal()
{ {
// Check if the minimum interval between frames has passed // Check if the minimum interval between frames has passed
uint64_t current_time = esp_timer_get_time(); uint64_t current_time = esp_timer_get_time();
@ -511,10 +518,11 @@ bool IRAM_ATTR DShotRMT::_isFrameIntervalElapsed()
return elapsed >= _frame_timer_us; return elapsed >= _frame_timer_us;
} }
void DShotRMT::_recordFrameTransmissionTime() bool DShotRMT::_timer_reset()
{ {
// Record the time of the current transmission // Record the time of the current transmission
_last_transmission_time_us = esp_timer_get_time(); _last_transmission_time_us = esp_timer_get_time();
return true;
} }
// Static Callback Functions // Static Callback Functions
@ -538,123 +546,14 @@ bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const
return false; return false;
} }
void DShotRMT::printDShotResult(const dshot_result_t &result, Stream &output)
{
const char *msg_str;
switch (result.error_code)
{
case dshot_msg_code_t::DSHOT_ERROR_NONE:
msg_str = "None";
break;
case dshot_msg_code_t::DSHOT_ERROR_UNKNOWN:
msg_str = "Unknown Error!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TX_INIT_FAILED:
msg_str = "TX RMT channel init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_RX_INIT_FAILED:
msg_str = "RX RMT channel init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_ENCODER_INIT_FAILED:
msg_str = "RMT encoder init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_CALLBACK_REGISTERING_FAILED:
msg_str = "RMT RX Callback registering failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_RECEIVER_FAILED:
msg_str = "RMT receiver failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_FAILED:
msg_str = "Transmission failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_THROTTLE_NOT_IN_RANGE:
msg_str = "Throttle not in range! (48 - 2047)";
break;
case dshot_msg_code_t::DSHOT_ERROR_PERCENT_NOT_IN_RANGE:
msg_str = "Percent not in range! (0.0 - 100.0)";
break;
case dshot_msg_code_t::DSHOT_ERROR_COMMAND_NOT_VALID:
msg_str = "Command not valid! (0 - 47)";
break;
case dshot_msg_code_t::DSHOT_ERROR_BIDIR_NOT_ENABLED:
msg_str = "Bidirectional DShot not enabled!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TELEMETRY_FAILED:
msg_str = "No valid Telemetric Frame received!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INVALID_MAGNET_COUNT:
msg_str = "Invalid motor magnet count!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INVALID_COMMAND:
msg_str = "Invalid command!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TIMING_CORRECTION:
msg_str = "Timing correction!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INIT_FAILED:
msg_str = "SignalGeneratorRMT init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INIT_SUCCESS:
msg_str = "SignalGeneratorRMT initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TX_INIT_SUCCESS:
msg_str = "TX RMT channel initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_RX_INIT_SUCCESS:
msg_str = "RX RMT channel initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_ENCODING_SUCCESS:
msg_str = "Packet encoded successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_SUCCESS:
msg_str = "Transmission successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TELEMETRY_SUCCESS:
msg_str = "Valid Telemetric Frame received!";
break;
case dshot_msg_code_t::DSHOT_ERROR_COMMAND_SUCCESS:
msg_str = "DShot command sent successfully";
break;
default:
msg_str = "Unhandled Error Code!";
break;
}
output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", msg_str);
// Print telemetry data if available
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
{
output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm);
}
output.println();
}
// Public Static Utility Functions // Public Static Utility Functions
void DShotRMT::printDShotInfo(const DShotRMT &dshot_rmt, Stream &output) void DShotRMT::printDShotInfo(const DShotRMT &dshot_rmt, Stream &output)
{ {
output.println("\n === DShot Signal Info === "); output.println("\n === DShot Signal Info === ");
output.printf("Current Mode: DSHOT%d\n", dshot_rmt.getMode() == dshot_mode_t::DSHOT150 ? 150 : dshot_rmt.getMode() == dshot_mode_t::DSHOT300 ? 300
int mode_val = 0; : dshot_rmt.getMode() == dshot_mode_t::DSHOT600 ? 600
switch (dshot_rmt.getMode()) : dshot_rmt.getMode() == dshot_mode_t::DSHOT1200 ? 1200
{ : 0);
case dshot_mode_t::DSHOT150:
mode_val = 150;
break;
case dshot_mode_t::DSHOT300:
mode_val = 300;
break;
case dshot_mode_t::DSHOT600:
mode_val = 600;
break;
case dshot_mode_t::DSHOT1200:
mode_val = 1200;
break;
default:
break;
}
output.printf("Current Mode: DSHOT%d\n", 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: ");

View File

@ -113,13 +113,6 @@ public:
dshot_result_t saveESCSettings(); dshot_result_t saveESCSettings();
// Public Utility & Info Functions // Public Utility & Info Functions
/**
* @brief Prints the result of a DShot operation to the specified output stream.
* @param result The dshot_result_t structure containing the operation's outcome.
* @param output The output stream (e.g., Serial) to print to. Defaults to Serial.
*/
static void printDShotResult(const dshot_result_t &result, Stream &output = Serial);
/** /**
* @brief Prints detailed DShot signal information for a given DShotRMT instance. * @brief Prints detailed DShot signal information for a given DShotRMT instance.
* @param dshot_rmt The DShotRMT instance to get information from. * @param dshot_rmt The DShotRMT instance to get information from.
@ -220,8 +213,8 @@ private:
uint64_t _last_command_timestamp; uint64_t _last_command_timestamp;
uint16_t _encoded_frame_value; uint16_t _encoded_frame_value;
dshot_packet_t _packet; dshot_packet_t _packet;
uint16_t _pulse_level; // DShot protocol: Signal is idle-low, so pulses start by going HIGH. uint16_t _level0; // DShot protocol: Signal is idle-low, so pulses start by going HIGH.
uint16_t _idle_level; // DShot protocol: Signal returns to LOW after the high pulse. uint16_t _level1; // DShot protocol: Signal returns to LOW after the high pulse.
// RMT Hardware Handles // RMT Hardware Handles
rmt_channel_handle_t _rmt_tx_channel; rmt_channel_handle_t _rmt_tx_channel;
@ -252,12 +245,12 @@ private:
// Private Frame Processing Functions // Private Frame Processing Functions
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
// Private Timing Control Functions // Private Timing Control Functions
bool _isFrameIntervalElapsed(); bool _timer_signal();
void _recordFrameTransmissionTime(); bool _timer_reset();
// Static Callback Functions // Static Callback Functions
static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);

View File

@ -217,3 +217,101 @@ const char *const TIMING_CORRECTION = "Timing correction!";
const char *const CALLBACK_REGISTERING_FAILED = "RMT RX Callback registering failed!"; const char *const CALLBACK_REGISTERING_FAILED = "RMT RX Callback registering failed!";
const char *const INVALID_COMMAND = "Invalid command!"; const char *const INVALID_COMMAND = "Invalid command!";
const char *const COMMAND_SUCCESS = "DShot command sent successfully"; const char *const COMMAND_SUCCESS = "DShot command sent successfully";
// Helper Functions
/**
* @brief Prints the result of a DShot operation to the specified output stream.
* @param result The dshot_result_t structure containing the operation's outcome.
* @param output The output stream (e.g., Serial) to print to. Defaults to Serial.
*/
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
{
const char *msg_str;
switch (result.error_code)
{
case dshot_msg_code_t::DSHOT_ERROR_NONE:
msg_str = "None";
break;
case dshot_msg_code_t::DSHOT_ERROR_UNKNOWN:
msg_str = "Unknown Error!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TX_INIT_FAILED:
msg_str = "TX RMT channel init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_RX_INIT_FAILED:
msg_str = "RX RMT channel init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_ENCODER_INIT_FAILED:
msg_str = "RMT encoder init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_CALLBACK_REGISTERING_FAILED:
msg_str = "RMT RX Callback registering failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_RECEIVER_FAILED:
msg_str = "RMT receiver failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_FAILED:
msg_str = "Transmission failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_THROTTLE_NOT_IN_RANGE:
msg_str = "Throttle not in range! (48 - 2047)";
break;
case dshot_msg_code_t::DSHOT_ERROR_PERCENT_NOT_IN_RANGE:
msg_str = "Percent not in range! (0.0 - 100.0)";
break;
case dshot_msg_code_t::DSHOT_ERROR_COMMAND_NOT_VALID:
msg_str = "Command not valid! (0 - 47)";
break;
case dshot_msg_code_t::DSHOT_ERROR_BIDIR_NOT_ENABLED:
msg_str = "Bidirectional DShot not enabled!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TELEMETRY_FAILED:
msg_str = "No valid Telemetric Frame received!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INVALID_MAGNET_COUNT:
msg_str = "Invalid motor magnet count!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INVALID_COMMAND:
msg_str = "Invalid command!";
break;
case dshot_msg_code_t::DSHOT_ERROR_TIMING_CORRECTION:
msg_str = "Timing correction!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INIT_FAILED:
msg_str = "SignalGeneratorRMT init failed!";
break;
case dshot_msg_code_t::DSHOT_ERROR_INIT_SUCCESS:
msg_str = "SignalGeneratorRMT initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TX_INIT_SUCCESS:
msg_str = "TX RMT channel initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_RX_INIT_SUCCESS:
msg_str = "RX RMT channel initialized successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_ENCODING_SUCCESS:
msg_str = "Packet encoded successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_SUCCESS:
msg_str = "Transmission successfully";
break;
case dshot_msg_code_t::DSHOT_ERROR_TELEMETRY_SUCCESS:
msg_str = "Valid Telemetric Frame received!";
break;
case dshot_msg_code_t::DSHOT_ERROR_COMMAND_SUCCESS:
msg_str = "DShot command sent successfully";
break;
default:
msg_str = "Unhandled Error Code!";
break;
}
output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", msg_str);
// Print telemetry data if available
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
{
output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm);
}
output.println();
}