...update error handling

This commit is contained in:
Wastl Kraus 2025-09-07 13:48:48 +02:00
parent cfca2de4e9
commit feb6f4ecac
6 changed files with 190 additions and 169 deletions

View File

@ -22,7 +22,7 @@ dshot_result_t DShotCommandManager::begin()
{ {
dshot_result_t result; dshot_result_t result;
result.success = true; result.success = true;
result.error_message = "Success"; result.msg = "Success";
return result; return result;
} }
@ -36,10 +36,10 @@ dshot_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16
dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms) dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms)
{ {
dshot_result_t result = {false, "Unknown error"}; dshot_result_t result = {false, "Unknown error"};
if (!isValidCommand(command)) if (!isValidCommand(command))
{ {
result.error_message = "Invalid command"; result.msg = "Invalid command";
return result; return result;
} }
@ -53,7 +53,7 @@ dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t comman
if (!single_result.success) if (!single_result.success)
{ {
all_successful = false; all_successful = false;
result.error_message = single_result.error_message; result.msg = single_result.msg;
break; break;
} }
@ -64,12 +64,12 @@ dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t comman
} }
} }
// //
result.success = all_successful; result.success = all_successful;
if (result.success) if (result.success)
{ {
result.error_message = "Success"; result.msg = "Success";
} }
return result; return result;
@ -194,7 +194,7 @@ dshot_result_t DShotCommandManager::setSilentMode(bool enable)
} }
// --- SEQUENCE COMMANDS --- // --- SEQUENCE COMMANDS ---
dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length) dshot_result_t DShotCommandManager::executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length)
{ {
dshot_result_t result = {true, "Success"}; dshot_result_t result = {true, "Success"};
uint64_t total_start_time = esp_timer_get_time(); uint64_t total_start_time = esp_timer_get_time();
@ -209,7 +209,7 @@ dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence
if (!item_result.success) if (!item_result.success)
{ {
result.success = false; result.success = false;
result.error_message = item_result.error_message; result.msg = item_result.msg;
break; break;
} }
@ -229,7 +229,7 @@ dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence
dshot_result_t DShotCommandManager::executeInitSequence() dshot_result_t DShotCommandManager::executeInitSequence()
{ {
// Basic ESC initialization sequence // Basic ESC initialization sequence
dshot_command_sequence_item_t init_sequence[] = { dshot_commandmanager_item_t init_sequence[] = {
{DSHOT_CMD_MOTOR_STOP, 5, 100}, // Stop motor, repeat 5 times, wait 100ms {DSHOT_CMD_MOTOR_STOP, 5, 100}, // Stop motor, repeat 5 times, wait 100ms
{DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE, 1, 50}, // Enable telemetry, wait 50ms {DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE, 1, 50}, // Enable telemetry, wait 50ms
{DSHOT_CMD_ESC_INFO, 1, 100} // Request ESC info, wait 100ms {DSHOT_CMD_ESC_INFO, 1, 100} // Request ESC info, wait 100ms
@ -242,7 +242,7 @@ dshot_result_t DShotCommandManager::executeInitSequence()
dshot_result_t DShotCommandManager::executeCalibrationSequence() dshot_result_t DShotCommandManager::executeCalibrationSequence()
{ {
// Basic ESC calibration sequence // Basic ESC calibration sequence
dshot_command_sequence_item_t calibration_sequence[] = { dshot_commandmanager_item_t calibration_sequence[] = {
{DSHOT_CMD_MOTOR_STOP, 10, 500}, // Ensure motor is stopped {DSHOT_CMD_MOTOR_STOP, 10, 500}, // Ensure motor is stopped
{DSHOT_CMD_SPIN_DIRECTION_NORMAL, 10, 100}, // Set normal spin direction {DSHOT_CMD_SPIN_DIRECTION_NORMAL, 10, 100}, // Set normal spin direction
{DSHOT_CMD_3D_MODE_OFF, 10, 100}, // Disable 3D mode {DSHOT_CMD_3D_MODE_OFF, 10, 100}, // Disable 3D mode

View File

@ -12,13 +12,13 @@
#include <DShotRMT.h> #include <DShotRMT.h>
#include <dshot_commands.h> #include <dshot_commands.h>
// Command sequence item // Command item
typedef struct typedef struct
{ {
dshot_commands_t command; dshot_commands_t command;
uint16_t repeat_count; uint16_t repeat_count;
uint32_t delay_ms; uint32_t delay_ms;
} dshot_command_sequence_item_t; } dshot_commandmanager_item_t;
// Advanced DShot command manager class // Advanced DShot command manager class
class DShotCommandManager class DShotCommandManager
@ -76,7 +76,7 @@ public:
// --- SEQUENCE COMMANDS --- // --- SEQUENCE COMMANDS ---
// Execute a sequence of DShot commands // Execute a sequence of DShot commands
dshot_result_t executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length); dshot_result_t executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length);
// Execute ESC initialization sequence // Execute ESC initialization sequence
dshot_result_t executeInitSequence(); dshot_result_t executeInitSequence();

View File

@ -18,6 +18,36 @@ static constexpr dshot_timing_t DSHOT_TIMINGS[] = {
{16, 8, 6, 2, 3, 5} // DSHOT1200 {16, 8, 6, 2, 3, 5} // DSHOT1200
}; };
// --- HELPERS ---
void printDShotResult(dshot_result_t &result, Stream &output)
{
if (result.success)
{
output.printf("Staus: SUCCESS - %s\n", result.msg);
}
else
{
output.printf("Status: FAILED - %s\n", result.msg);
}
output.println(" ");
}
//
void printDShotTelemetry(dshot_telemetry_result_t &result, Stream &output)
{
if (result.success)
{
output.printf("Telemetry: eRPM=%u, Motor RPM=%u \n", result.erpm, result.motor_rpm);
}
else
{
output.printf("Telemetry: FAILED - %s\n", result.msg);
}
output.println(" ");
}
// Constructor with GPIO number // Constructor with GPIO number
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
: _gpio(gpio), : _gpio(gpio),
@ -26,11 +56,8 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
_frame_timer_us(0), _frame_timer_us(0),
_timing_config(DSHOT_TIMINGS[mode]), _timing_config(DSHOT_TIMINGS[mode]),
_last_transmission_time(0), _last_transmission_time(0),
_last_erpm(0),
_parsed_packet(0), _parsed_packet(0),
_packet{0}, _packet{0},
_total_transmissions(0),
_failed_transmissions(0),
_rmt_tx_channel(nullptr), _rmt_tx_channel(nullptr),
_rmt_rx_channel(nullptr), _rmt_rx_channel(nullptr),
_dshot_encoder(nullptr), _dshot_encoder(nullptr),
@ -98,46 +125,45 @@ DShotRMT::~DShotRMT()
// Init DShotRMT // Init DShotRMT
dshot_result_t DShotRMT::begin() dshot_result_t DShotRMT::begin()
{ {
dshot_result_t result = {false, UNKNOWN_ERROR}; // Result container
uint64_t start_time = esp_timer_get_time(); dshot_result_t result = {false, INIT_FAILED};
// Init RX channel first // Init RX channel first
if (_is_bidirectional) if (_is_bidirectional)
{ {
if (!_initRXChannel()) if (!_initRXChannel().success)
{ {
result.error_message = RX_INIT_FAILED; result.msg = RX_INIT_FAILED;
_dshot_log(RX_INIT_FAILED);
return result; return result;
} }
} }
// Init TX channel // Init TX channel
if (!_initTXChannel()) if (!_initTXChannel().success)
{ {
result.error_message = TX_INIT_FAILED; result.msg = TX_INIT_FAILED;
_dshot_log(TX_INIT_FAILED);
return result; return result;
} }
// Init DShot encoder // Init DShot encoder
if (_initDShotEncoder() != DSHOT_OK) if (!_initDShotEncoder().success)
{ {
result.error_message = ENCODER_INIT_FAILED; result.msg = ENCODER_INIT_FAILED;
_dshot_log(ENCODER_INIT_FAILED);
return result; return result;
} }
uint64_t end_time = esp_timer_get_time();
result.success = true; result.success = true;
result.error_message = INIT_SUCCESS; result.msg = INIT_SUCCESS;
return result; return result;
} }
// Init RMT TX channel // Init RMT TX channel
bool DShotRMT::_initTXChannel() dshot_result_t DShotRMT::_initTXChannel()
{ {
// Result container
dshot_result_t result = {false, TX_INIT_FAILED};
// Configure TX channel // Configure TX channel
_tx_channel_config.gpio_num = _gpio; _tx_channel_config.gpio_num = _gpio;
_tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
@ -152,21 +178,33 @@ bool DShotRMT::_initTXChannel()
// Create RMT TX channel // Create RMT TX channel
if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK)
{ {
_dshot_log(TX_INIT_FAILED); return result;
return DSHOT_ERROR;
} }
return (rmt_enable(_rmt_tx_channel) == DSHOT_OK); //
if (rmt_enable(_rmt_tx_channel) != 0)
{
return result;
}
result.success = true;
result.msg = TX_INIT_SUCCESS;
return result;
} }
// Init RMT RX channel // Init RMT RX channel
bool DShotRMT::_initRXChannel() dshot_result_t DShotRMT::_initRXChannel()
{ {
// Result container
dshot_result_t result = {false, RX_INIT_FAILED};
// Create a queue for RX callback data // Create a queue for RX callback data
_rx_queue = xQueueCreate(RMT_QUEUE_DEPTH, sizeof(rmt_rx_done_event_data_t)); _rx_queue = xQueueCreate(RMT_QUEUE_DEPTH, sizeof(rmt_rx_done_event_data_t));
if (_rx_queue == nullptr) if (_rx_queue == nullptr)
{ {
return DSHOT_ERROR; result.msg = RX_BUFFER_FAILED;
return result;
} }
// Config RMT RX // Config RMT RX
@ -182,8 +220,7 @@ bool DShotRMT::_initRXChannel()
// Create RMT RX channel // Create RMT RX channel
if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK)
{ {
_dshot_log(RX_INIT_FAILED); return result;
return DSHOT_ERROR;
} }
// Register RX callback // Register RX callback
@ -191,48 +228,64 @@ bool DShotRMT::_initRXChannel()
if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, _rx_queue) != DSHOT_OK) if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, _rx_queue) != DSHOT_OK)
{ {
_dshot_log(RX_INIT_FAILED); result.msg = RX_BUFFER_FAILED;
return DSHOT_ERROR; return result;
} }
return (rmt_enable(_rmt_rx_channel) == DSHOT_OK); //
if (rmt_enable(_rmt_rx_channel) != 0)
{
return result;
}
result.success = true;
result.msg = RX_INIT_SUCCESS;
return result;
} }
// Callback for RMT RX // Callback for RMT RX
bool IRAM_ATTR DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) bool IRAM_ATTR DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{ {
// Init RX buffer // Init RX buffer
QueueHandle_t rx_queue = (QueueHandle_t)user_data; QueueHandle_t rx_buffer = (QueueHandle_t)user_data;
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Copy callback data into RX buffer // Copy callback data into RX buffer
xQueueGenericSendFromISR(rx_queue, edata, &xHigherPriorityTaskWoken, queueSEND_TO_BACK); xQueueGenericSendFromISR(rx_buffer, edata, &xHigherPriorityTaskWoken, queueSEND_TO_BACK);
return (xHigherPriorityTaskWoken == pdTRUE); return (xHigherPriorityTaskWoken == pdTRUE);
} }
// Initialize DShot encoder // Initialize DShot encoder
bool DShotRMT::_initDShotEncoder() dshot_result_t DShotRMT::_initDShotEncoder()
{ {
// Result container
dshot_result_t result = {false, ENCODER_INIT_FAILED};
// Create copy encoder configuration // Create copy encoder configuration
rmt_copy_encoder_config_t encoder_config = {}; rmt_copy_encoder_config_t encoder_config = {};
// Create encoder instance // Create encoder instance
if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
{ {
_dshot_log(ENCODER_INIT_FAILED); return result;
return DSHOT_ERROR;
} }
return DSHOT_OK; result.success = true;
result.msg = ENCODER_INIT_SUCCESS;
return result;
} }
// Send throttle value // Send throttle value
dshot_result_t DShotRMT::sendThrottle(uint16_t throttle) dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
{ {
static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP; // Result container
dshot_result_t result = {false, UNKNOWN_ERROR}; dshot_result_t result = {false, UNKNOWN_ERROR};
static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP;
// Special case: if throttle is 0, use sendCommand() instead // Special case: if throttle is 0, use sendCommand() instead
if (throttle == 0) if (throttle == 0)
{ {
@ -242,8 +295,7 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
// Log only if throttle is out of range and different from last time // Log only if throttle is out of range and different from last time
if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle) if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle)
{ {
_dshot_log(THROTTLE_NOT_IN_RANGE); result.msg = THROTTLE_NOT_IN_RANGE;
result.error_message = THROTTLE_NOT_IN_RANGE;
} }
// Always store the original throttle value // Always store the original throttle value
@ -259,63 +311,67 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
// Send DShot command to ESC // Send DShot command to ESC
dshot_result_t DShotRMT::sendCommand(uint16_t command) dshot_result_t DShotRMT::sendCommand(uint16_t command)
{ {
// Result container
dshot_result_t result = {false, UNKNOWN_ERROR}; dshot_result_t result = {false, UNKNOWN_ERROR};
// Validate command is within DShot specification range // Validate command is within DShot specification range
if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX) if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX)
{ {
_dshot_log(COMMAND_NOT_VALID); result.msg = COMMAND_NOT_VALID;
result.error_message = COMMAND_NOT_VALID;
return result; return result;
} }
// Build packet and transmit // Build packet and transmit
_packet = _buildDShotPacket(command); _packet = _buildDShotPacket(command);
return _sendDShotFrame(_packet); return _sendDShotFrame(_packet);
} }
// Get telemetry data with timing and error handling // Get telemetry data with timing and error handling
dshot_telemetry_result_t DShotRMT::getTelemetry(uint8_t magnet_count) dshot_telemetry_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
{ {
dshot_telemetry_result_t result = {false, 0, 0, UNKNOWN_ERROR}; // Result container
dshot_telemetry_result_t result = {false, 0, 0, TELEMETRY_FAILED};
// Check if bidirectional mode is enabled // Check if bidirectional mode is enabled
if (!_is_bidirectional) if (!_is_bidirectional)
{ {
result.error_message = BIDIR_NOT_ENABLED; result.msg = BIDIR_NOT_ENABLED;
_dshot_log(BIDIR_NOT_ENABLED);
return result; return result;
} }
// Get eRPM // Get eRPM
uint16_t erpm = getERPM(); uint16_t erpm = _getERPM();
if (erpm == DSHOT_NULL_PACKET) if (erpm == DSHOT_NULL_PACKET)
{ {
result.error_message = TELEMETRY_TIMEOUT;
return result; return result;
} }
// Calculate motor RPM // Calculate motor RPM
uint8_t pole_pairs = max(1, magnet_count / 2); uint8_t pole_pairs = max(1, (magnet_count / 2));
uint32_t motor_rpm = erpm / pole_pairs; uint32_t motor_rpm = (erpm / pole_pairs);
result.success = true; result.success = true;
result.erpm = erpm; result.erpm = erpm;
result.motor_rpm = motor_rpm; result.motor_rpm = motor_rpm;
result.error_message = TELEMETRY_SUCCESS; result.msg = TELEMETRY_SUCCESS;
return result; return result;
} }
// Get RPM from ESC (bidirectional mode only) - backward compatibility // Get RPM from ESC (bidirectional mode only) - backward compatibility
uint16_t DShotRMT::getERPM() uint16_t DShotRMT::_getERPM()
{ {
static uint16_t last_erpm = 0;
// Result container
dshot_telemetry_result_t result = {false, 0, 0, TELEMETRY_FAILED};
// Check if bidirectional mode is enabled // Check if bidirectional mode is enabled
if (!_is_bidirectional) if (!_is_bidirectional)
{ {
_dshot_log(BIDIR_NOT_ENABLED); return last_erpm;
return _last_erpm;
} }
// RMT RX event data // RMT RX event data
@ -327,11 +383,11 @@ uint16_t DShotRMT::getERPM()
// Decode the received symbols if a valid frame was received // Decode the received symbols if a valid frame was received
if (rx_data.num_symbols > DSHOT_NULL_PACKET) if (rx_data.num_symbols > DSHOT_NULL_PACKET)
{ {
_last_erpm = _decodeDShotFrame(rx_data.received_symbols); last_erpm = _decodeDShotFrame(rx_data.received_symbols);
} }
} }
return _last_erpm; return last_erpm;
} }
// Build a complete DShot packet // Build a complete DShot packet
@ -343,8 +399,6 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value)
// Re-check for valid value // Re-check for valid value
if (value > DSHOT_THROTTLE_MAX) if (value > DSHOT_THROTTLE_MAX)
{ {
_dshot_log(PACKET_BUILD_ERROR);
// Something is really wrong // Something is really wrong
return packet; return packet;
} }
@ -390,7 +444,6 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data)
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
{ {
dshot_result_t result = {false, UNKNOWN_ERROR}; dshot_result_t result = {false, UNKNOWN_ERROR};
uint64_t start_time = esp_timer_get_time();
// Check timing requirements // Check timing requirements
if (!_timer_signal()) if (!_timer_signal())
@ -406,7 +459,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
if (rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config) != DSHOT_OK) if (rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config) != DSHOT_OK)
{ {
result.error_message = RX_RMT_RECEIVER_ERROR; result.msg = RECEIVER_FAILED;
return result; return result;
} }
} }
@ -426,8 +479,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
// Disable RMT RX for sending // Disable RMT RX for sending
if (rmt_disable(_rmt_rx_channel) != DSHOT_OK) if (rmt_disable(_rmt_rx_channel) != DSHOT_OK)
{ {
result.error_message = RX_RMT_RECEIVER_ERROR; result.msg = RECEIVER_FAILED;
_dshot_log(RX_RMT_RECEIVER_ERROR);
return result; return result;
} }
} }
@ -435,8 +487,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
// Perform RMT transmission // Perform RMT transmission
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config) != DSHOT_OK) if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config) != DSHOT_OK)
{ {
result.error_message = TRANSMITTER_ERROR; result.msg = TRANSMISSION_FAILED;
_dshot_log(TRANSMITTER_ERROR);
return result; return result;
} }
@ -445,18 +496,16 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
{ {
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
{ {
result.error_message = RX_RMT_RECEIVER_ERROR; result.msg = RECEIVER_FAILED;
_dshot_log(RX_RMT_RECEIVER_ERROR);
return result; return result;
} }
} }
// Update timestamp and calculate execution time // Update timestamp and calculate execution time
_timer_reset(); _timer_reset();
uint64_t end_time = esp_timer_get_time();
result.success = true; result.success = true;
result.error_message = TRANSMISSION_SUCCESS; result.msg = TRANSMISSION_SUCCESS;
return result; return result;
} }
@ -555,10 +604,10 @@ void DShotRMT::printDShotInfo(Stream &output) const
// Current DShot mode // Current DShot mode
output.printf("Current Mode: DSHOT%d\n", output.printf("Current Mode: DSHOT%d\n",
_mode == DSHOT150 ? 150 : _mode == DSHOT150 ? 150 :
_mode == DSHOT300 ? 300 : _mode == DSHOT300 ? 300 :
_mode == DSHOT600 ? 600 : _mode == DSHOT600 ? 600 :
_mode == DSHOT1200 ? 1200 : 0); _mode == DSHOT1200 ? 1200 : 0);
output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO"); output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO");

View File

@ -15,19 +15,20 @@
#include <driver/rmt_rx.h> #include <driver/rmt_rx.h>
// DShot Protocol Constants // DShot Protocol Constants
constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
constexpr auto DSHOT_THROTTLE_MIN = 48; static constexpr auto DSHOT_THROTTLE_MIN = 48;
constexpr auto DSHOT_THROTTLE_MAX = 2047; static constexpr auto DSHOT_THROTTLE_MAX = 2047;
constexpr auto DSHOT_BITS_PER_FRAME = 16; static constexpr auto DSHOT_BITS_PER_FRAME = 16;
constexpr auto DSHOT_SWITCH_TIME = 30; // Time in us between TX and RX static constexpr auto DSHOT_SWITCH_TIME = 30; // Time in us - add some padding to RX - TX switching
constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
constexpr auto DSHOT_RX_TIMEOUT_MS = 2; static constexpr auto DSHOT_RX_TIMEOUT_MS = 2; // Never reached, just a timeeout
static constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14;
// RMT Configuration Constants // RMT Configuration Constants
constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution
constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
constexpr auto RMT_BUFFER_SYMBOLS = 64; constexpr auto RMT_BUFFER_SYMBOLS = 64;
constexpr auto RMT_QUEUE_DEPTH = 1; constexpr auto RMT_QUEUE_DEPTH = 1;
@ -36,7 +37,7 @@ constexpr auto RMT_QUEUE_DEPTH = 1;
constexpr uint32_t DSHOT_PULSE_MIN = 3000; constexpr uint32_t DSHOT_PULSE_MIN = 3000;
constexpr uint32_t DSHOT_PULSE_MAX = 60000; constexpr uint32_t DSHOT_PULSE_MAX = 60000;
// DShot Mode Enumeration // DShot Modes
typedef enum typedef enum
{ {
DSHOT_OFF, DSHOT_OFF,
@ -46,7 +47,7 @@ typedef enum
DSHOT1200 DSHOT1200
} dshot_mode_t; } dshot_mode_t;
// DShot Packet Structure // DShot Packet
typedef struct typedef struct
{ {
uint16_t throttle_value : 11; uint16_t throttle_value : 11;
@ -54,7 +55,7 @@ typedef struct
uint16_t checksum : 4; uint16_t checksum : 4;
} dshot_packet_t; } dshot_packet_t;
// DShot Timing Configuration Structure // DShot Timing Configuration
typedef struct typedef struct
{ {
uint32_t frame_length_us; uint32_t frame_length_us;
@ -65,25 +66,29 @@ typedef struct
uint16_t ticks_zero_low; uint16_t ticks_zero_low;
} dshot_timing_t; } dshot_timing_t;
// Command execution result structure // Error handling
typedef struct typedef struct
{ {
bool success; bool success;
const char *error_message; const char *msg;
} dshot_result_t; } dshot_result_t;
// DShot telemetry result structure // DShot telemetry result
typedef struct typedef struct
{ {
bool success; bool success;
uint16_t erpm; uint16_t erpm;
uint32_t motor_rpm; uint16_t motor_rpm;
const char *error_message; const char *msg;
} dshot_telemetry_result_t; } dshot_telemetry_result_t;
// Naming convention // Naming convention
typedef dshotCommands_e dshot_commands_t; typedef dshotCommands_e dshot_commands_t;
// --- HELPERS ---
void printDShotResult(dshot_result_t &result, Stream &output = Serial);
void printDShotTelemetry(dshot_telemetry_result_t &result, Stream &output = Serial);
// //
class DShotRMT class DShotRMT
{ {
@ -106,21 +111,12 @@ public:
// Send DShot command (0-47) // Send DShot command (0-47)
dshot_result_t sendCommand(uint16_t command); dshot_result_t sendCommand(uint16_t command);
// Get telemetry data (bidirectional mode only)
dshot_telemetry_result_t getTelemetry(uint8_t magnet_count = 14);
// Get eRPM only (bidirectional mode only)
uint16_t getERPM();
// --- GETTERS --- // --- GETTERS ---
gpio_num_t getGPIO() const { return _gpio; } gpio_num_t getGPIO() const { return _gpio; }
uint16_t getDShotPacket() const { return _parsed_packet; } uint16_t getDShotPacket() const { return _parsed_packet; }
bool is_bidirectional() const { return _is_bidirectional; } bool is_bidirectional() const { return _is_bidirectional; }
dshot_mode_t getMode() const { return _mode; } dshot_mode_t getMode() const { return _mode; }
dshot_telemetry_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
// Get execution statistics
uint32_t getTotalTransmissions() const { return _total_transmissions; }
uint32_t getFailedTransmissions() const { return _failed_transmissions; }
// --- INFO --- // --- INFO ---
void printDShotInfo(Stream &output = Serial) const; void printDShotInfo(Stream &output = Serial) const;
@ -141,7 +137,7 @@ public:
return result.success; return result.success;
} }
[[deprecated("Use getTelemetry() instead")]] [[deprecated("Use getTelemetry() instead")]]
uint32_t getMotorRPM(uint8_t magnet_count) uint32_t getMotorRPM(uint8_t magnet_count)
{ {
auto result = getTelemetry(magnet_count); auto result = getTelemetry(magnet_count);
@ -158,7 +154,6 @@ private:
// --- TIMING & STATE VARIABLES --- // --- TIMING & STATE VARIABLES ---
uint64_t _last_transmission_time; uint64_t _last_transmission_time;
uint16_t _last_erpm;
uint16_t _parsed_packet; uint16_t _parsed_packet;
dshot_packet_t _packet; dshot_packet_t _packet;
@ -178,9 +173,9 @@ private:
rmt_receive_config_t _receive_config; rmt_receive_config_t _receive_config;
// --- INITS --- // --- INITS ---
bool _initTXChannel(); dshot_result_t _initTXChannel();
bool _initRXChannel(); dshot_result_t _initRXChannel();
bool _initDShotEncoder(); dshot_result_t _initDShotEncoder();
// --- PACKET MANAGEMENT --- // --- PACKET MANAGEMENT ---
dshot_packet_t _buildDShotPacket(const uint16_t value); dshot_packet_t _buildDShotPacket(const uint16_t value);
@ -192,6 +187,9 @@ private:
bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); bool IRAM_ATTR _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);
// --- HELPERS ---
uint16_t _getERPM();
// --- TIMING CONTROL --- // --- TIMING CONTROL ---
bool IRAM_ATTR _timer_signal(); bool IRAM_ATTR _timer_signal();
bool _timer_reset(); bool _timer_reset();
@ -204,27 +202,27 @@ private:
// --- DSHOT DEFAULTS --- // --- DSHOT DEFAULTS ---
static constexpr auto DSHOT_TELEMETRY_INVALID = (0xffff); static constexpr auto DSHOT_TELEMETRY_INVALID = (0xffff);
// --- ERROR HANDLING & LOGGING ---
void _dshot_log(const char *msg, Stream &output = Serial) const { output.println(msg); }
// --- CONSTANTS & ERROR MESSAGES --- // --- CONSTANTS & ERROR MESSAGES ---
static constexpr bool DSHOT_OK = 0; static constexpr bool DSHOT_OK = 0;
static constexpr bool DSHOT_ERROR = 1; static constexpr bool DSHOT_ERROR = 1;
static constexpr char *NEW_LINE = " "; static constexpr char *NONE = "";
static constexpr char *UNKNOWN_ERROR = "Unknown Error!"; static constexpr char *UNKNOWN_ERROR = "Unknown Error!";
static constexpr char *TX_INIT_FAILED = "Failed to initialize TX channel!"; static constexpr char *INIT_SUCCESS = "SignalGeneratorRMT initialized successfully";
static constexpr char *RX_INIT_FAILED = "Failed to initialize RX channel!"; static constexpr char *INIT_FAILED = "SignalGeneratorRMT init failed!";
static constexpr char *ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!"; static constexpr char *TX_INIT_SUCCESS = "TX RMT channel initialized successfully";
static constexpr char *CRC_CHECK_FAILED = "RX CRC Check failed!"; static constexpr char *TX_INIT_FAILED = "TX RMT channel init failed!";
static constexpr char *THROTTLE_NOT_IN_RANGE = "Throttle value not in range (48 - 2047)!"; static constexpr char *RX_INIT_SUCCESS = "RX RMT channel initialized successfully";
static constexpr char *COMMAND_NOT_VALID = "Not a valid DShot Command (0 - 47)!"; static constexpr char *RX_INIT_FAILED = "RX RMT channel init failed!";
static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot support not enabled!"; static constexpr char *RX_BUFFER_FAILED = "RX RMT buffer init failed!";
static constexpr char *RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!"; static constexpr char *ENCODER_INIT_SUCCESS = "RMT encoder initialized successfully";
static constexpr char *PACKET_BUILD_ERROR = "Value too big for DShot Packet!"; static constexpr char *ENCODER_INIT_FAILED = "RMT encoder init failed!";
static constexpr char *TRANSMITTER_ERROR = "RMT TX Transmitter Error!"; static constexpr char *TRANSMISSION_SUCCESS = "Transmission successfully";
static constexpr char *INIT_SUCCESS = "DShotRMT initialized successfully"; static constexpr char *TRANSMISSION_FAILED = "Transmission failed!";
static constexpr char *TELEMETRY_SUCCESS = "Telemetry read successfully"; static constexpr char *RECEIVER_FAILED = "RMT receiver failed!";
static constexpr char *TELEMETRY_TIMEOUT = "Telemetry read timeout"; static constexpr char *THROTTLE_NOT_IN_RANGE = "Throttle not in range! (48 - 2047)";
static constexpr char *TRANSMISSION_SUCCESS = "Transmission successful"; static constexpr char *COMMAND_NOT_VALID = "Command not valid! (0 - 47)";
static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot not enabled!";
static constexpr char *TELEMETRY_SUCCESS = "Valid Telemetric Frame received!";
static constexpr char *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
}; };

View File

@ -37,7 +37,7 @@ void printTelemetryResult(const dshot_telemetry_result_t &result)
} }
else else
{ {
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.error_message); USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.msg);
} }
} }
@ -270,7 +270,7 @@ void printResult(const dshot_result_t &result)
} }
else else
{ {
USB_SERIAL.printf("FAILED - %s \n", result.error_message); USB_SERIAL.printf("FAILED - %s \n", result.msg);
} }
} }

View File

@ -53,7 +53,7 @@ void loop()
static bool continuous_throttle = true; static bool continuous_throttle = true;
// Time Measurement // Time Measurement
static uint32_t last_stats_print = 0; static uint64_t last_stats_print = 0;
// Handle serial input // Handle serial input
if (USB_SERIAL.available() > 0) if (USB_SERIAL.available() > 0)
@ -73,23 +73,24 @@ void loop()
motor01.sendThrottle(throttle); motor01.sendThrottle(throttle);
} }
// Print motor stats every 5 seconds in continuous mode // Print motor stats every 3 seconds in continuous mode
if (continuous_throttle && (millis() - last_stats_print >= 5000)) if (continuous_throttle && (esp_timer_get_time() - last_stats_print >= 3000000))
{ {
motor01.printDShotInfo(); motor01.printDShotInfo();
USB_SERIAL.println(" "); USB_SERIAL.println(" ");
USB_SERIAL.println("Type 'help' to show Menu");
// Get Motor RPM if bidirectional // Get Motor RPM if bidirectional
if (IS_BIDIRECTIONAL) if (IS_BIDIRECTIONAL)
{ {
dshot_telemetry_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_telemetry_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
printTelemetryResult(telem_result); printDShotTelemetry(telem_result);
} }
USB_SERIAL.println("Type 'help' to show Menu");
// Time Stamp // Time Stamp
last_stats_print = millis(); last_stats_print = esp_timer_get_time();
} }
} }
@ -114,32 +115,6 @@ void printMenu()
USB_SERIAL.println("*******************************************"); USB_SERIAL.println("*******************************************");
} }
// Helper to print command results
void printCommandResult(const dshot_result_t &result, const String &operation)
{
if (result.success)
{
USB_SERIAL.printf("%s: SUCCESS\n", operation.c_str());
}
else
{
USB_SERIAL.printf("%s: FAILED - %s\n", operation.c_str(), result.error_message);
}
}
// Helper to print telemetry results
void printTelemetryResult(const dshot_telemetry_result_t &result)
{
if (result.success)
{
USB_SERIAL.printf("Telemetry: eRPM=%u, Motor RPM=%u (%s)\n", result.erpm, result.motor_rpm, result.error_message);
}
else
{
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.error_message);
}
}
// //
void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle) void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle)
{ {
@ -149,17 +124,16 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
throttle = 0; throttle = 0;
continuous_throttle = true; // kill motor for sure continuous_throttle = true; // kill motor for sure
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP); dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
printCommandResult(result, "Stop Motor"); printDShotResult(result);
} }
else if (input == "info") else if (input == "info")
{ {
continuous_throttle = false;
motor01.printDShotInfo(); motor01.printDShotInfo();
} }
else if (input == "rpm" && IS_BIDIRECTIONAL) else if (input == "rpm" && IS_BIDIRECTIONAL)
{ {
dshot_telemetry_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); dshot_telemetry_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
printTelemetryResult(result); printDShotTelemetry(result);
} }
else if (input.startsWith("cmd ")) else if (input.startsWith("cmd "))
{ {
@ -171,7 +145,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);
printCommandResult(result, "DShot Command " + String(cmd_num)); printDShotResult(result);
} }
else else
{ {
@ -193,7 +167,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);
printCommandResult(result, "Set Throttle " + String(throttle)); printDShotResult(result);
} }
else else
{ {