From 51c1d345e8e2ad7b4b0ad9d8d6d9b7a1500629ed Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Wed, 1 Oct 2025 13:44:25 +0200 Subject: [PATCH 1/6] fix examples --- examples/web_client/web_client.ino | 6 +++--- examples/web_control/web_control.ino | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index 147a69e..3117d55 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -177,7 +177,7 @@ void loop() // Get Motor RPM if bidirectional and armed if (IS_BIDIRECTIONAL && isArmed) { - dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t telem_result = motor01.getTelemetry(); printDShotResult(telem_result); } @@ -194,7 +194,7 @@ void loop() if (IS_BIDIRECTIONAL && isArmed) { - dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t telem_result = motor01.getTelemetry(); if (telem_result.success && telem_result.motor_rpm > 0) { current_rpm = String(telem_result.motor_rpm); @@ -494,7 +494,7 @@ void handleSerialInput(const String &input) { if (isArmed) { - dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t result = motor01.getTelemetry(); printDShotResult(result); } else diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index d78edb2..6a0728b 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -145,7 +145,7 @@ void loop() // Get Motor RPM if bidirectional and armed if (IS_BIDIRECTIONAL && isArmed) { - dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t telem_result = motor01.getTelemetry(); printDShotResult(telem_result); } @@ -160,7 +160,7 @@ void loop() if (IS_BIDIRECTIONAL && isArmed) { - dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t telem_result = motor01.getTelemetry(); if (telem_result.success && telem_result.motor_rpm > 0) { current_rpm = String(telem_result.motor_rpm); @@ -263,7 +263,7 @@ void handleSerialInput(const String &input) { if (isArmed) { - dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT); + dshot_result_t result = motor01.getTelemetry(); printDShotResult(result); } else From a763786fbff3ea65d7c034e00143f55dd8b38c2f Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Wed, 1 Oct 2025 16:50:24 +0200 Subject: [PATCH 2/6] more clean up --- README.md | 5 +- src/DShotRMT.cpp | 169 +++++++++++++------------------------------ src/DShotRMT.h | 26 ++----- src/dshot_config.cpp | 98 +++++++++++++++++++++++++ src/dshot_config.h | 16 ++++ 5 files changed, 174 insertions(+), 140 deletions(-) create mode 100644 src/dshot_config.cpp create mode 100644 src/dshot_config.h diff --git a/README.md b/README.md index c3421a6..c5f7b46 100644 --- a/README.md +++ b/README.md @@ -108,8 +108,9 @@ The main class is `DShotRMT`. Here are the most important methods: - `begin()`: Initializes the DShot RMT channels and encoder. - `sendThrottlePercent(float percent)`: Sends a throttle value as a percentage (0.0-100.0) to the ESC. - `sendThrottle(uint16_t throttle)`: Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command. -- `sendCommand(uint16_t command)`: Sends a single DShot command (0-47) to the ESC. -- `sendCommand(dshotCommands_e dshot_command, uint16_t repeat_count = DEFAULT_CMD_REPEAT_COUNT, uint16_t delay_us = DEFAULT_CMD_DELAY_US)`: Sends a DShot command multiple times with a delay between repetitions. This is a blocking function. +- `sendCommand(dshotCommands_e command)`: Sends a DShot command (0-47) 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 (0-47) to the ESC with a specified repeat count and delay. This is a blocking function. +- `sendCommand(uint16_t command_value)`: Sends a DShot command (0-47) 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. - `getESCInfo()`: Sends a command to the ESC to request ESC information. - `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal. diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 91896aa..52fcf2a 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -14,7 +14,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, ui _mode(mode), _is_bidirectional(is_bidirectional), _motor_magnet_count(magnet_count), - _dshot_timing(DSHOT_TIMING_US[static_cast(mode)]) + _dshot_timing(DSHOT_TIMING_US[_mode]) { // Pre-calculate timing and bit positions for performance _preCalculateRMTTicks(); @@ -61,24 +61,27 @@ DShotRMT::~DShotRMT() // Initialize DShotRMT dshot_result_t DShotRMT::begin() { - if (!_initTXChannel().success) + dshot_result_t result = init_rmt_tx_channel(_gpio, &_rmt_tx_channel, _is_bidirectional); + if (!result.success) { - return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; + return result; } if (_is_bidirectional) { - if (!_initRXChannel().success) + result = init_rmt_rx_channel(_gpio, &_rmt_rx_channel, &_rx_event_callbacks, this); + if (!result.success) { // Cleanup previously allocated TX channel on failure rmt_disable(_rmt_tx_channel); rmt_del_channel(_rmt_tx_channel); _rmt_tx_channel = nullptr; - return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; + return result; } } - if (!_initDShotEncoder().success) + result = init_dshot_encoder(&_dshot_encoder, _rmt_ticks, _pulse_level, _idle_level); + if (!result.success) { // Cleanup previously allocated channels on failure rmt_disable(_rmt_tx_channel); @@ -91,8 +94,7 @@ dshot_result_t DShotRMT::begin() rmt_del_channel(_rmt_rx_channel); _rmt_rx_channel = nullptr; } - - return {false, dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED}; + return result; } return {true, dshot_msg_code_t::DSHOT_INIT_SUCCESS}; @@ -127,21 +129,45 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent) return sendThrottle(throttle); } -// Send DShot command to ESC -dshot_result_t DShotRMT::sendCommand(dshotCommands_e command) +// Sends a DShot command (0-47) to the ESC by accepting an integer value. +dshot_result_t DShotRMT::sendCommand(uint16_t command_value) { - _packet = _buildDShotPacket(static_cast(command)); - return _sendDShotFrame(_packet); + // Validate the integer command value before casting + if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX) + { + return {false, dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID}; + } + return sendCommand(static_cast(command_value)); } +// Sends a DShot command (0-47) to the ESC. +dshot_result_t DShotRMT::sendCommand(dshotCommands_e command) +{ + uint16_t repeat_count = DEFAULT_CMD_REPEAT_COUNT; + uint16_t delay_us = DEFAULT_CMD_DELAY_US; -// Send full DShot commands for setup etc -// This is a blocking function that uses delayMicroseconds for repetitions. -dshot_result_t DShotRMT::_sendCommandInternal(dshotCommands_e dshot_command, uint16_t repeat_count, uint16_t delay_us) + switch (command) + { + case DSHOT_CMD_SAVE_SETTINGS: + case DSHOT_CMD_SPIN_DIRECTION_NORMAL: + case DSHOT_CMD_SPIN_DIRECTION_REVERSED: + repeat_count = SETTINGS_COMMAND_REPEATS; + delay_us = SETTINGS_COMMAND_DELAY_US; + break; + default: + // For other commands, use default repeat and delay + break; + } + + return sendCommand(command, repeat_count, delay_us); +} + +// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay. +dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us) { dshot_result_t result = {false, dshot_msg_code_t::DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; - if (!_isValidCommand(dshot_command)) + if (!_isValidCommand(command)) { result.result_code = dshot_msg_code_t::DSHOT_INVALID_COMMAND; return result; @@ -152,7 +178,7 @@ dshot_result_t DShotRMT::_sendCommandInternal(dshotCommands_e dshot_command, uin // Send command multiple times with delay for (uint16_t i = 0; i < repeat_count; i++) { - dshot_result_t single_result = _executeCommand(dshot_command); + dshot_result_t single_result = _executeCommand(command); if (!single_result.success) { @@ -168,7 +194,6 @@ dshot_result_t DShotRMT::_sendCommandInternal(dshotCommands_e dshot_command, uin } } - // result.success = all_successful; if (result.success) @@ -221,15 +246,13 @@ dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed) // Use command as a yes / no switch dshotCommands_e command = reversed ? dshotCommands_e::DSHOT_CMD_SPIN_DIRECTION_REVERSED : dshotCommands_e::DSHOT_CMD_SPIN_DIRECTION_NORMAL; - return _sendCommandInternal(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); + return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); } - - // Use with caution dshot_result_t DShotRMT::saveESCSettings() { - return _sendCommandInternal(dshotCommands_e::DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); + return sendCommand(dshotCommands_e::DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); } // Simple check @@ -252,103 +275,7 @@ dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command) return result; } -// Private Initialization Functions -dshot_result_t DShotRMT::_initTXChannel() -{ - _tx_channel_config.gpio_num = _gpio; - _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; - _rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation - _rmt_tx_config.flags.eot_level = _is_bidirectional ? 1 : 0; - - if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; - } - - if (rmt_enable(_rmt_tx_channel) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; - } - - return {true, dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS}; -} - -dshot_result_t DShotRMT::_initRXChannel() -{ - // Double check if bidirectional mode is enabled - if (!_is_bidirectional) - { - return {true, dshot_msg_code_t::DSHOT_NONE}; - } - - _rx_channel_config.gpio_num = _gpio; - _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - - // Filter for pulses that are within a reasonable range for DShot telemetry - _rmt_rx_config.signal_range_min_ns = DSHOT_PULSE_MIN_NS; - _rmt_rx_config.signal_range_max_ns = DSHOT_PULSE_MAX_NS; - - if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; - } - - // Register the callback function that will be triggered when a frame is received - _rx_event_callbacks.on_recv_done = _on_rx_done; - if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, this) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED}; - } - - if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; - } - - // Start the receiver to wait for incoming telemetry data - rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME]; - size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); - if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_rmt_rx_config) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_RECEIVER_FAILED}; - } - - return {true, dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS}; -} - -// -dshot_result_t DShotRMT::_initDShotEncoder() -{ - rmt_bytes_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) - { - return {false, dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED}; - } - - return {true, dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS}; -} // Private Packet Management Functions dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) const @@ -422,7 +349,11 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) // The DShot frame is 16 bits, which is 2 bytes size_t tx_size_bytes = sizeof(swapped_value); - if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK) + rmt_transmit_config_t tx_config = {}; // Initialize all members to zero + tx_config.loop_count = 0; // No automatic loops - real-time calculation + tx_config.flags.eot_level = _is_bidirectional ? 1 : 0; + + if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK) { return {false, dshot_msg_code_t::DSHOT_TRANSMISSION_FAILED}; } diff --git a/src/DShotRMT.h b/src/DShotRMT.h index bffad6b..aee173b 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -15,6 +15,7 @@ #include "dshot_definitions.h" #include +#include "dshot_config.h" // DShotRMT Class Definition class DShotRMT @@ -41,6 +42,10 @@ public: // Sends a DShot command (0-47) to the ESC. dshot_result_t sendCommand(dshotCommands_e command); + // 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); + // Sends a DShot command (0-47) to the ESC by accepting an integer value. + dshot_result_t sendCommand(uint16_t command_value); // Retrieves telemetry data from the ESC. dshot_result_t getTelemetry(); @@ -71,19 +76,11 @@ public: const char *getDShotMsg(dshot_result_t &result) const { return (_get_result_code_str(result.result_code)); } // Deprecated Methods - // Deprecated. Use sendThrottle() instead. - [[deprecated("Use sendThrottle() instead")]] - dshot_result_t sendValue(uint16_t value) { return sendThrottle(value); } - - // Deprecated. Use sendCommand() instead. - [[deprecated("Use sendCommand() instead")]] - dshot_result_t sendCommand(uint16_t command) { return sendCommand(static_cast(command)); } private: // --- UTILITY METHODS --- bool _isValidCommand(dshotCommands_e command) const; dshot_result_t _executeCommand(dshotCommands_e command); - dshot_result_t _sendCommandInternal(dshotCommands_e dshot_command, uint16_t repeat_count, uint16_t delay_us); // Core Configuration Variables gpio_num_t _gpio; @@ -108,22 +105,13 @@ private: rmt_channel_handle_t _rmt_rx_channel = nullptr; rmt_encoder_handle_t _dshot_encoder = nullptr; - // RMT Configuration Structures - rmt_tx_channel_config_t _tx_channel_config{}; - rmt_rx_channel_config_t _rx_channel_config{}; - rmt_transmit_config_t _rmt_tx_config{}; - rmt_receive_config_t _rmt_rx_config{}; + // Bidirectional / Telemetry Variables rmt_rx_event_callbacks_t _rx_event_callbacks{}; std::atomic _last_erpm_atomic{0}; std::atomic _telemetry_ready_flag_atomic{false}; - // Private Initialization Functions - dshot_result_t _initTXChannel(); - dshot_result_t _initRXChannel(); - dshot_result_t _initDShotEncoder(); - // Private Packet Management Functions dshot_packet_t _buildDShotPacket(const uint16_t &value) const; uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; @@ -143,4 +131,4 @@ private: }; // Include utility functions after class definition -#include "dshot_utils.h" +#include "dshot_utils.h" \ No newline at end of file diff --git a/src/dshot_config.cpp b/src/dshot_config.cpp new file mode 100644 index 0000000..54024a7 --- /dev/null +++ b/src/dshot_config.cpp @@ -0,0 +1,98 @@ +#include "dshot_config.h" + +// Function to initialize the RMT TX channel +dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, bool is_bidirectional) +{ + rmt_tx_channel_config_t tx_channel_config = { + .gpio_num = gpio, + .clk_src = DSHOT_CLOCK_SRC_DEFAULT, + .resolution_hz = DSHOT_RMT_RESOLUTION, + .mem_block_symbols = RMT_BUFFER_SYMBOLS, + .trans_queue_depth = RMT_QUEUE_DEPTH, + }; + + rmt_transmit_config_t rmt_tx_config = {}; // Initialize all members to zero + rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation + rmt_tx_config.flags.eot_level = is_bidirectional ? 1 : 0; + + if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; + } + + if (rmt_enable(*out_channel) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; + } + + return {true, dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS}; +} + +// Function to initialize the RMT RX channel +dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, rmt_rx_event_callbacks_t *rx_event_callbacks, void *user_data) +{ + rmt_rx_channel_config_t rx_channel_config = { + .gpio_num = gpio, + .clk_src = DSHOT_CLOCK_SRC_DEFAULT, + .resolution_hz = DSHOT_RMT_RESOLUTION, + .mem_block_symbols = RMT_BUFFER_SYMBOLS, + }; + + rmt_receive_config_t rmt_rx_config = { + .signal_range_min_ns = DSHOT_PULSE_MIN_NS, + .signal_range_max_ns = DSHOT_PULSE_MAX_NS, + }; + + if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; + } + + if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED}; + } + + if (rmt_enable(*out_channel) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; + } + + // Start the receiver to wait for incoming telemetry data + rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME]; + size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); + if (rmt_receive(*out_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_RECEIVER_FAILED}; + } + + return {true, dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS}; +} + +// Function to initialize the DShot RMT encoder +dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_ticks_t &rmt_ticks, uint16_t pulse_level, uint16_t idle_level) +{ + rmt_bytes_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, out_encoder) != DSHOT_OK) + { + return {false, dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED}; + } + + return {true, dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS}; +} diff --git a/src/dshot_config.h b/src/dshot_config.h new file mode 100644 index 0000000..a50e2d5 --- /dev/null +++ b/src/dshot_config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "dshot_definitions.h" + +// Function to initialize the RMT TX channel +dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, bool is_bidirectional); + +// Function to initialize the RMT RX channel +dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, rmt_rx_event_callbacks_t *rx_event_callbacks, void *user_data); + +// Function to initialize the DShot RMT encoder +dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_ticks_t &rmt_ticks, uint16_t pulse_level, uint16_t idle_level); From 30d87aae4fb1a305723911b6976987dfbf6527df Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Thu, 2 Oct 2025 00:31:24 +0200 Subject: [PATCH 3/6] more simplifications --- DShotRMT.h | 3 + examples/dshot300/dshot300.ino | 2 +- .../throttle_percent/throttle_percent.ino | 2 +- examples/web_client/web_client.ino | 19 +-- examples/web_control/web_control.ino | 4 +- src/DShotRMT.cpp | 34 ++--- src/DShotRMT.h | 123 +++++++----------- src/dshot_config.cpp | 25 ++-- src/dshot_definitions.h | 89 +------------ src/dshot_utils.h | 87 +++++++++++++ src/{ => web_utilities}/ota_update.h | 0 src/{ => web_utilities}/web_content.h | 0 12 files changed, 181 insertions(+), 207 deletions(-) rename src/{ => web_utilities}/ota_update.h (100%) rename src/{ => web_utilities}/web_content.h (100%) diff --git a/DShotRMT.h b/DShotRMT.h index 29d290d..f7e8113 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -6,5 +6,8 @@ * @license MIT */ +#pragma once + +#include #include "src/DShotRMT.h" \ No newline at end of file diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 85c5f26..8d3ae5c 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -29,7 +29,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance -DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); +DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, DEFAULT_MOTOR_MAGNET_COUNT); // void setup() diff --git a/examples/throttle_percent/throttle_percent.ino b/examples/throttle_percent/throttle_percent.ino index 5903a7b..0ae022d 100644 --- a/examples/throttle_percent/throttle_percent.ino +++ b/examples/throttle_percent/throttle_percent.ino @@ -28,7 +28,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance -DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT); +DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, DEFAULT_MOTOR_MAGNET_COUNT); // Forward declaration void handleSerialInput(const String &input); diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index 3117d55..fda65ef 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -20,8 +20,8 @@ #include #include -#include -#include +#include "web_utilities/ota_update.h" +#include "web_utilities/web_content.h" #include #include @@ -51,7 +51,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance -DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); +DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT); // Web Server Configuration AsyncWebServer server(80); @@ -235,11 +235,11 @@ void setupOTA() // Serve OTA update page server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request) - { request->send_P(200, "text/html", ota_html); }); + { request->send_P(200, "text/html", ota_html); }); // Handle OTA update upload server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request) - { + { bool shouldReboot = !Update.hasError(); AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", @@ -254,7 +254,8 @@ void setupOTA() ESP.restart(); } else { USB_SERIAL.println("OTA Update failed!"); - } }, handleOTAUpload); + } + }, handleOTAUpload); USB_SERIAL.println("OTA Update ready at: /update"); } @@ -344,7 +345,7 @@ void printWiFiStatus() { USB_SERIAL.println(); USB_SERIAL.println("***********************************************"); - USB_SERIAL.println(" --- WIFI INFO --- "); + USB_SERIAL.println(" --- WIFI INFO ---"); USB_SERIAL.println("***********************************************"); USB_SERIAL.printf("SSID: %s\n", WiFi.SSID().c_str()); USB_SERIAL.printf("IP Address: %s\n", WiFi.localIP().toString().c_str()); @@ -393,7 +394,7 @@ void printMenu() { USB_SERIAL.println(" "); USB_SERIAL.println("***********************************************"); - USB_SERIAL.println(" --- DShotRMT Web Client Demo --- "); + USB_SERIAL.println(" --- DShotRMT Web Client Demo ---"); USB_SERIAL.println("***********************************************"); if (wifi_connected) @@ -684,4 +685,4 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp case WS_EVT_ERROR: break; } -} +} \ No newline at end of file diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index 6a0728b..cac075b 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -19,7 +19,7 @@ #include #include -#include +#include "web_utilities/web_content.h" #include #include @@ -50,7 +50,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance -DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); +DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT); // Web Server Configuration AsyncWebServer server(80); diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 52fcf2a..34504d5 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -6,7 +6,7 @@ * @license MIT */ -#include +#include "DShotRMT.h" // Constructor with GPIO number DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count) @@ -62,6 +62,7 @@ DShotRMT::~DShotRMT() dshot_result_t DShotRMT::begin() { dshot_result_t result = init_rmt_tx_channel(_gpio, &_rmt_tx_channel, _is_bidirectional); + if (!result.success) { return result; @@ -81,6 +82,7 @@ dshot_result_t DShotRMT::begin() } result = init_dshot_encoder(&_dshot_encoder, _rmt_ticks, _pulse_level, _idle_level); + if (!result.success) { // Cleanup previously allocated channels on failure @@ -97,7 +99,7 @@ dshot_result_t DShotRMT::begin() return result; } - return {true, dshot_msg_code_t::DSHOT_INIT_SUCCESS}; + return {true, DSHOT_INIT_SUCCESS}; } // Send throttle value @@ -106,7 +108,7 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle) // A throttle value of 0 is a disarm command if (throttle == 0) { - return sendCommand(dshotCommands_e::DSHOT_CMD_MOTOR_STOP); + return sendCommand(DSHOT_CMD_MOTOR_STOP); } // Constrain throttle to the valid DShot range @@ -121,7 +123,7 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent) { if (percent < 0.0f || percent > 100.0f) { - return {false, dshot_msg_code_t::DSHOT_PERCENT_NOT_IN_RANGE}; + return {false, DSHOT_PERCENT_NOT_IN_RANGE}; } // Map percent to DShot throttle range @@ -135,7 +137,7 @@ dshot_result_t DShotRMT::sendCommand(uint16_t command_value) // Validate the integer command value before casting if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX) { - return {false, dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID}; + return {false, DSHOT_COMMAND_NOT_VALID}; } return sendCommand(static_cast(command_value)); } @@ -165,11 +167,11 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command) // Sends a DShot command (0-47) to the ESC with a specified repeat count and delay. dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us) { - dshot_result_t result = {false, dshot_msg_code_t::DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; + dshot_result_t result = {false, DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; if (!_isValidCommand(command)) { - result.result_code = dshot_msg_code_t::DSHOT_INVALID_COMMAND; + result.result_code = DSHOT_INVALID_COMMAND; return result; } @@ -198,7 +200,7 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_co if (result.success) { - result.result_code = dshot_msg_code_t::DSHOT_COMMAND_SUCCESS; + result.result_code = DSHOT_COMMAND_SUCCESS; } return result; @@ -207,11 +209,11 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_co // Get telemetry data dshot_result_t DShotRMT::getTelemetry() { - dshot_result_t result = {false, dshot_msg_code_t::DSHOT_TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; + dshot_result_t result = {false, DSHOT_TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; if (!_is_bidirectional) { - result.result_code = dshot_msg_code_t::DSHOT_BIDIR_NOT_ENABLED; + result.result_code = DSHOT_BIDIR_NOT_ENABLED; return result; } @@ -233,7 +235,7 @@ dshot_result_t DShotRMT::getTelemetry() result.success = true; result.erpm = erpm; result.motor_rpm = motor_rpm; - result.result_code = dshot_msg_code_t::DSHOT_TELEMETRY_SUCCESS; + result.result_code = DSHOT_TELEMETRY_SUCCESS; } } @@ -275,8 +277,6 @@ dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command) return result; } - - // Private Packet Management Functions dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) const { @@ -338,7 +338,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) // Ensure enough time has passed since the last transmission if (!_isFrameIntervalElapsed()) { - return {true, dshot_msg_code_t::DSHOT_NONE}; + return {true, DSHOT_NONE}; } _encoded_frame_value = _buildDShotFrameValue(packet); @@ -350,17 +350,17 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) size_t tx_size_bytes = sizeof(swapped_value); 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 tx_config.flags.eot_level = _is_bidirectional ? 1 : 0; if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_TRANSMISSION_FAILED}; + return {false, DSHOT_TRANSMISSION_FAILED}; } _recordFrameTransmissionTime(); // Reset the timer for the next frame - return {true, dshot_msg_code_t::DSHOT_TRANSMISSION_SUCCESS}; + return {true, DSHOT_TRANSMISSION_SUCCESS}; } // This function needs to be fast, as it generates the RMT symbols just before sending diff --git a/src/DShotRMT.h b/src/DShotRMT.h index aee173b..374049d 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -1,134 +1,107 @@ /** * @file DShotRMT.h - * @brief Optimized DShot signal generation using ESP32 RMT with bidirectional support + * @brief DShot signal generation using ESP32 RMT with bidirectional support * @author Wastl Kraus - * @date 2025-09-18 + * @date 2025-06-11 * @license MIT */ #pragma once -#include +#include +#include #include #include -#include #include "dshot_definitions.h" -#include #include "dshot_config.h" -// DShotRMT Class Definition +// class DShotRMT { public: - // Constructor for DShotRMT with GPIO number. - explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); + // Constructor with GPIO number + DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count); - // Constructor for DShotRMT with Arduino pin number. - DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); + // Constructor using pin number + DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count); // Destructor ~DShotRMT(); - // Public Core Functions - // Initializes the DShot RMT channels and encoder. + // Initialize DShotRMT dshot_result_t begin(); - // Sends a throttle value as a percentage (0.0-100.0) to the ESC. - dshot_result_t sendThrottlePercent(float percent); - - // Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command. + // Send throttle value dshot_result_t sendThrottle(uint16_t throttle); - // Sends a DShot command (0-47) to the ESC. - dshot_result_t sendCommand(dshotCommands_e command); - // 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); + // Send throttle value as a percentage + dshot_result_t sendThrottlePercent(float percent); + // Sends a DShot command (0-47) to the ESC by accepting an integer value. dshot_result_t sendCommand(uint16_t command_value); - // Retrieves telemetry data from the ESC. + // Sends a DShot command (0-47) to the ESC. + dshot_result_t sendCommand(dshotCommands_e command); + + // 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); + + // Get telemetry data dshot_result_t getTelemetry(); - // Sets the motor spin direction. 'true' for reversed, 'false' for normal. + // Reverse motor direction directly dshot_result_t setMotorSpinDirection(bool reversed); - // Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory. + // Use with caution dshot_result_t saveESCSettings(); - // Public Utility & Info Functions - // Sets the motor magnet count for RPM calculation. - void setMotorMagnetCount(uint16_t magnet_count); - - // Gets the current DShot mode. + // Getters for DShot info dshot_mode_t getMode() const { return _mode; } - - // Checks if bidirectional DShot is enabled. bool isBidirectional() const { return _is_bidirectional; } - - // Gets the encoded frame value. + uint16_t getThrottleValue() const { return _last_throttle; } uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } - // Gets the last transmitted throttle value. - uint16_t getThrottleValue() const { return _packet.throttle_value; } - - // Testing return "verbose" messages - const char *getDShotMsg(dshot_result_t &result) const { return (_get_result_code_str(result.result_code)); } - - // Deprecated Methods - private: - // --- UTILITY METHODS --- - bool _isValidCommand(dshotCommands_e command) const; - dshot_result_t _executeCommand(dshotCommands_e command); - - // Core Configuration Variables gpio_num_t _gpio; dshot_mode_t _mode; bool _is_bidirectional; uint16_t _motor_magnet_count; - const dshot_timing_us_t &_dshot_timing; - uint64_t _frame_timer_us = 0; - - // Timing & Packet Variables - rmt_ticks_t _rmt_ticks{}; - uint16_t _last_throttle = dshotCommands_e::DSHOT_CMD_MOTOR_STOP; - uint64_t _last_transmission_time_us = 0; - uint64_t _last_command_timestamp = 0; - uint16_t _encoded_frame_value = 0; - dshot_packet_t _packet{}; - uint16_t _pulse_level = 1; // DShot protocol: Signal is idle-low, so pulses start by going HIGH. - uint16_t _idle_level = 0; // DShot protocol: Signal returns to LOW after the high pulse. - - // RMT Hardware Handles + dshot_timing_us_t _dshot_timing; rmt_channel_handle_t _rmt_tx_channel = nullptr; rmt_channel_handle_t _rmt_rx_channel = nullptr; rmt_encoder_handle_t _dshot_encoder = nullptr; + 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; + // Telemetry related + std::atomic _last_erpm_atomic = 0; + std::atomic _telemetry_ready_flag_atomic = false; + rmt_rx_event_callbacks_t _rx_event_callbacks = { + .on_recv_done = _on_rx_done, + }; - - // Bidirectional / Telemetry Variables - rmt_rx_event_callbacks_t _rx_event_callbacks{}; - std::atomic _last_erpm_atomic{0}; - std::atomic _telemetry_ready_flag_atomic{false}; - - // Private Packet Management Functions + // Private helper functions + bool _isValidCommand(dshotCommands_e command) const; + dshot_result_t _executeCommand(dshotCommands_e command); 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; void _preCalculateRMTTicks(); - - // Private Frame Processing Functions dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; - - // Private Timing Control Functions - bool _isFrameIntervalElapsed() const; + uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; + bool IRAM_ATTR _isFrameIntervalElapsed() const; void _recordFrameTransmissionTime(); // 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 IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); }; -// Include utility functions after class definition -#include "dshot_utils.h" \ No newline at end of file +#include "dshot_utils.h" // Workround for util functions \ No newline at end of file diff --git a/src/dshot_config.cpp b/src/dshot_config.cpp index 54024a7..d2b44c4 100644 --- a/src/dshot_config.cpp +++ b/src/dshot_config.cpp @@ -12,20 +12,20 @@ dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch }; rmt_transmit_config_t rmt_tx_config = {}; // Initialize all members to zero - rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation + rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation rmt_tx_config.flags.eot_level = is_bidirectional ? 1 : 0; if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; + return {false, DSHOT_TX_INIT_FAILED}; } if (rmt_enable(*out_channel) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED}; + return {false, DSHOT_TX_INIT_FAILED}; } - return {true, dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS}; + return {true, DSHOT_TX_INIT_SUCCESS}; } // Function to initialize the RMT RX channel @@ -43,19 +43,16 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch .signal_range_max_ns = DSHOT_PULSE_MAX_NS, }; - if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) - { - return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; - } + return {false, DSHOT_RX_INIT_FAILED}; if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED}; + return {false, DSHOT_CALLBACK_REGISTERING_FAILED}; } if (rmt_enable(*out_channel) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED}; + return {false, DSHOT_RX_INIT_FAILED}; } // Start the receiver to wait for incoming telemetry data @@ -63,10 +60,10 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); if (rmt_receive(*out_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_RECEIVER_FAILED}; + return {false, DSHOT_RECEIVER_FAILED}; } - return {true, dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS}; + return {true, DSHOT_RX_INIT_SUCCESS}; } // Function to initialize the DShot RMT encoder @@ -91,8 +88,8 @@ dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_t if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK) { - return {false, dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED}; + return {false, DSHOT_ENCODER_INIT_FAILED}; } - return {true, dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS}; + return {true, DSHOT_ENCODER_INIT_SUCCESS}; } diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index d8db6cb..4335c53 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -43,7 +43,7 @@ typedef struct rmt_ticks } rmt_ticks_t; // Enum class for specific error and success codes -enum class dshot_msg_code_t +enum dshot_msg_code_t { DSHOT_NONE = 0, DSHOT_UNKNOWN, @@ -161,90 +161,3 @@ const dshot_timing_us_t DSHOT_TIMING_US[] = { {0.83, 0.67}, // DSHOT1200 {0.00, 0.00} // DSHOT_MODE_MAX (dummy entry) }; - -// Error Messages -static constexpr char NONE[] = ""; -static constexpr char UNKNOWN_ERROR[] = "Unknown Error!"; -static constexpr char INIT_SUCCESS[] = "SignalGeneratorRMT initialized successfully"; -static constexpr char INIT_FAILED[] = "SignalGeneratorRMT init failed!"; -static constexpr char TX_INIT_SUCCESS[] = "TX RMT channel initialized successfully"; -static constexpr char TX_INIT_FAILED[] = "TX RMT channel init failed!"; -static constexpr char RX_INIT_SUCCESS[] = "RX RMT channel initialized successfully"; -static constexpr char RX_INIT_FAILED[] = "RX RMT channel init failed!"; -static constexpr char ENCODER_INIT_SUCCESS[] = "RMT encoder initialized successfully"; -static constexpr char ENCODER_INIT_FAILED[] = "RMT encoder init failed!"; -static constexpr char ENCODING_SUCCESS[] = "Packet encoded successfully"; -static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully"; -static constexpr char TRANSMISSION_FAILED[] = "Transmission 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 PERCENT_NOT_IN_RANGE[] = "Percent not in range! (0.0 - 100.0)"; -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!"; -static constexpr char INVALID_MAGNET_COUNT[] = "Invalid motor magnet count!"; -static constexpr char TIMING_CORRECTION[] = "Timing correction!"; -static constexpr char CALLBACK_REGISTERING_FAILED[] = "RMT RX Callback registering failed!"; -static constexpr char INVALID_COMMAND[] = "Invalid command!"; -static constexpr char COMMAND_SUCCESS[] = "DShot command sent successfully"; - -// Helper to get result code string -inline const char *_get_result_code_str(dshot_msg_code_t code) -{ - switch (code) - { - case dshot_msg_code_t::DSHOT_NONE: - return NONE; - case dshot_msg_code_t::DSHOT_UNKNOWN: - return UNKNOWN_ERROR; - case dshot_msg_code_t::DSHOT_TX_INIT_FAILED: - return TX_INIT_FAILED; - case dshot_msg_code_t::DSHOT_RX_INIT_FAILED: - return RX_INIT_FAILED; - case dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED: - return ENCODER_INIT_FAILED; - case dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED: - return CALLBACK_REGISTERING_FAILED; - case dshot_msg_code_t::DSHOT_RECEIVER_FAILED: - return RECEIVER_FAILED; - case dshot_msg_code_t::DSHOT_TRANSMISSION_FAILED: - return TRANSMISSION_FAILED; - case dshot_msg_code_t::DSHOT_THROTTLE_NOT_IN_RANGE: - return THROTTLE_NOT_IN_RANGE; - case dshot_msg_code_t::DSHOT_PERCENT_NOT_IN_RANGE: - return PERCENT_NOT_IN_RANGE; - case dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID: - return COMMAND_NOT_VALID; - case dshot_msg_code_t::DSHOT_BIDIR_NOT_ENABLED: - return BIDIR_NOT_ENABLED; - case dshot_msg_code_t::DSHOT_TELEMETRY_FAILED: - return TELEMETRY_FAILED; - case dshot_msg_code_t::DSHOT_INVALID_MAGNET_COUNT: - return INVALID_MAGNET_COUNT; - case dshot_msg_code_t::DSHOT_INVALID_COMMAND: - return INVALID_COMMAND; - case dshot_msg_code_t::DSHOT_TIMING_CORRECTION: - return TIMING_CORRECTION; - case dshot_msg_code_t::DSHOT_INIT_FAILED: - return INIT_FAILED; - case dshot_msg_code_t::DSHOT_INIT_SUCCESS: - return INIT_SUCCESS; - case dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS: - return TX_INIT_SUCCESS; - case dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS: - return RX_INIT_SUCCESS; - case dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS: - return ENCODER_INIT_SUCCESS; - case dshot_msg_code_t::DSHOT_ENCODING_SUCCESS: - return ENCODING_SUCCESS; - case dshot_msg_code_t::DSHOT_TRANSMISSION_SUCCESS: - return TRANSMISSION_SUCCESS; - case dshot_msg_code_t::DSHOT_TELEMETRY_SUCCESS: - return TELEMETRY_SUCCESS; - case dshot_msg_code_t::DSHOT_COMMAND_SUCCESS: - return COMMAND_SUCCESS; - default: - return UNKNOWN_ERROR; - } -} \ No newline at end of file diff --git a/src/dshot_utils.h b/src/dshot_utils.h index a1abc18..623afe3 100644 --- a/src/dshot_utils.h +++ b/src/dshot_utils.h @@ -3,6 +3,93 @@ #include #include "DShotRMT.h" // Include the full class definition +// Error Messages +static constexpr char NONE[] = ""; +static constexpr char UNKNOWN_ERROR[] = "Unknown Error!"; +static constexpr char INIT_SUCCESS[] = "SignalGeneratorRMT initialized successfully"; +static constexpr char INIT_FAILED[] = "SignalGeneratorRMT init failed!"; +static constexpr char TX_INIT_SUCCESS[] = "TX RMT channel initialized successfully"; +static constexpr char TX_INIT_FAILED[] = "TX RMT channel init failed!"; +static constexpr char RX_INIT_SUCCESS[] = "RX RMT channel initialized successfully"; +static constexpr char RX_INIT_FAILED[] = "RX RMT channel init failed!"; +static constexpr char ENCODER_INIT_SUCCESS[] = "RMT encoder initialized successfully"; +static constexpr char ENCODER_INIT_FAILED[] = "RMT encoder init failed!"; +static constexpr char ENCODING_SUCCESS[] = "Packet encoded successfully"; +static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully"; +static constexpr char TRANSMISSION_FAILED[] = "Transmission 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 PERCENT_NOT_IN_RANGE[] = "Percent not in range! (0.0 - 100.0)"; +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!"; +static constexpr char INVALID_MAGNET_COUNT[] = "Invalid motor magnet count!"; +static constexpr char TIMING_CORRECTION[] = "Timing correction!"; +static constexpr char CALLBACK_REGISTERING_FAILED[] = "RMT RX Callback registering failed!"; +static constexpr char INVALID_COMMAND[] = "Invalid command!"; +static constexpr char COMMAND_SUCCESS[] = "DShot command sent successfully"; + +// Helper to get result code string +inline const char *_get_result_code_str(dshot_msg_code_t code) +{ + switch (code) + { + case DSHOT_NONE: + return NONE; + case DSHOT_UNKNOWN: + return UNKNOWN_ERROR; + case DSHOT_TX_INIT_FAILED: + return TX_INIT_FAILED; + case DSHOT_RX_INIT_FAILED: + return RX_INIT_FAILED; + case DSHOT_ENCODER_INIT_FAILED: + return ENCODER_INIT_FAILED; + case DSHOT_CALLBACK_REGISTERING_FAILED: + return CALLBACK_REGISTERING_FAILED; + case DSHOT_RECEIVER_FAILED: + return RECEIVER_FAILED; + case DSHOT_TRANSMISSION_FAILED: + return TRANSMISSION_FAILED; + case DSHOT_THROTTLE_NOT_IN_RANGE: + return THROTTLE_NOT_IN_RANGE; + case DSHOT_PERCENT_NOT_IN_RANGE: + return PERCENT_NOT_IN_RANGE; + case DSHOT_COMMAND_NOT_VALID: + return COMMAND_NOT_VALID; + case DSHOT_BIDIR_NOT_ENABLED: + return BIDIR_NOT_ENABLED; + case DSHOT_TELEMETRY_FAILED: + return TELEMETRY_FAILED; + case DSHOT_INVALID_MAGNET_COUNT: + return INVALID_MAGNET_COUNT; + case DSHOT_INVALID_COMMAND: + return INVALID_COMMAND; + case DSHOT_TIMING_CORRECTION: + return TIMING_CORRECTION; + case DSHOT_INIT_FAILED: + return INIT_FAILED; + case DSHOT_INIT_SUCCESS: + return INIT_SUCCESS; + case DSHOT_TX_INIT_SUCCESS: + return TX_INIT_SUCCESS; + case DSHOT_RX_INIT_SUCCESS: + return RX_INIT_SUCCESS; + case DSHOT_ENCODER_INIT_SUCCESS: + return ENCODER_INIT_SUCCESS; + case DSHOT_ENCODING_SUCCESS: + return ENCODING_SUCCESS; + case DSHOT_TRANSMISSION_SUCCESS: + return TRANSMISSION_SUCCESS; + case DSHOT_TELEMETRY_SUCCESS: + return TELEMETRY_SUCCESS; + case DSHOT_COMMAND_SUCCESS: + return COMMAND_SUCCESS; + default: + return UNKNOWN_ERROR; + } +} + // Helper to quick print DShot result codes inline void printDShotResult(dshot_result_t &result, Stream &output = Serial) { diff --git a/src/ota_update.h b/src/web_utilities/ota_update.h similarity index 100% rename from src/ota_update.h rename to src/web_utilities/ota_update.h diff --git a/src/web_content.h b/src/web_utilities/web_content.h similarity index 100% rename from src/web_content.h rename to src/web_utilities/web_content.h From da7719a1663b13e4adb6dbe67f45b4a990b9fc0c Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Thu, 2 Oct 2025 14:45:03 +0200 Subject: [PATCH 4/6] public definitions --- src/DShotRMT.h | 10 ++++++++-- src/dshot_definitions.h | 8 +------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 374049d..ca1eade 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -16,15 +16,21 @@ #include "dshot_definitions.h" #include "dshot_config.h" +// DShot Protocol Constants +static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; +static constexpr auto DSHOT_THROTTLE_MIN = 48; +static constexpr auto DSHOT_BITS_PER_FRAME = 16; +static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; + // class DShotRMT { public: // Constructor with GPIO number - DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count); + DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); // Constructor using pin number - DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count); + DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); // Destructor ~DShotRMT(); diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index 4335c53..11a4ccc 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -114,13 +114,6 @@ enum dshotCommands_e DSHOT_CMD_MAX = 47 }; -// DShot Protocol Constants -static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; -static constexpr auto DSHOT_THROTTLE_MIN = 48; -static constexpr auto DSHOT_THROTTLE_MAX = 2047; -static constexpr auto DSHOT_BITS_PER_FRAME = 16; -static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; - // Custom status codes static constexpr int DSHOT_OK = 0; static constexpr int DSHOT_ERROR = 1; @@ -140,6 +133,7 @@ static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer fra 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_THROTTLE_MAX = 2047; 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; From 6e3006c4c879104c69541aa74e13ae62847be04e Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Sat, 4 Oct 2025 21:41:25 +0200 Subject: [PATCH 5/6] prepare release 0.9.0 --- LICENSE | 4 +-- README.md | 28 ++++++++++++++----- .../throttle_percent/throttle_percent.ino | 2 +- examples/web_client/web_client.ino | 7 ++--- examples/web_control/web_control.ino | 6 ++-- library.properties | 2 +- src/DShotRMT.h | 15 +++++++--- src/dshot_definitions.h | 21 ++++++++------ src/{dshot_config.cpp => dshot_init.cpp} | 24 ++++++++++++---- src/{dshot_config.h => dshot_init.h} | 11 ++++++-- src/dshot_utils.h | 12 +++++++- src/web_utilities/ota_update.h | 2 +- src/web_utilities/web_content.h | 2 +- 13 files changed, 94 insertions(+), 42 deletions(-) rename src/{dshot_config.cpp => dshot_init.cpp} (91%) rename src/{dshot_config.h => dshot_init.h} (78%) diff --git a/LICENSE b/LICENSE index e77c526..433d0ec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Wastl Kraus +Copyright (c) 2023 derdoktor667 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index c5f7b46..ac47120 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # DShotRMT - ESP32 RMT DShot Driver [![Arduino CI](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml/badge.svg)](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml) +[![Arduino Library](https://img.shields.io/badge/Arduino-Library-blue.svg)](https://www.arduinolibraries.com/libraries/dshot-rmt) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -An Arduino IDElibrary for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT encoder API** (`rmt_tx.h` / `rmt_rx.h`). This library specifically leverages the official `rmt_bytes_encoder` API for an efficient, hardware-timed, and maintainable implementation. It provides a simple way to control brushless motors in both Arduino and ESP-IDF projects. +An Arduino IDE library for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT Encoder API** (`rmt_tx.h` / `rmt_rx.h`). This library specifically leverages the official `rmt_bytes_encoder` API for an efficient, hardware-timed and maintainable implementation. It provides a simple way to control BLHeli ESCs in both Arduino and ESP-IDF projects. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch. +--- + ### DShot300 Example Output Here's an example of the output from the `dshot300` example sketch: @@ -15,7 +19,7 @@ Here's an example of the output from the `dshot300` example sketch: ## 🚀 Core Features - **Multiple DShot Modes:** Supports DSHOT150, DSHOT300, DSHOT600, and DSHOT1200. -- **Bidirectional DShot Support:** Implemented, but note that official support is limited due to potential instability and external hardware requirements. Use with caution. +- **Bidirectional DShot Support:** Implemented, but note that official support is limited due to potential instability and external hardware requirements. Use with caution (and pull-up). - **Hardware-Timed Signals:** Precise signal generation using the ESP32 RMT peripheral, ensuring stable and reliable motor control. - **Simple API:** Easy-to-use C++ class with intuitive methods like `sendThrottlePercent()`. - **Error Handling:** Provides detailed feedback on operation success or failure via `dshot_result_t`. @@ -43,11 +47,11 @@ The DShot protocol defines specific timing characteristics for each mode. The fo ## ⚡ Quick Start -Here's a basic example of how to use the `DShotRMT` library to control a motor. Please use example sketches for more detailes: +Here's a basic example of how to use the `DShotRMT` library to control a motor. Note that `DShotRMT.h` now includes all necessary dependencies, so you only need to include this single header. Please use example sketches for more detailes: ```cpp #include -#include // Include the DShotRMT library +#include // Define the GPIO pin connected to the motor ESC const gpio_num_t MOTOR_PIN = GPIO_NUM_27; @@ -65,14 +69,13 @@ void setup() { printCpuInfo(Serial); Serial.println("Motor initialized. Ramping up to 25% throttle..."); - -} + } void loop() { // Ramp up to 25% throttle over 2.5 seconds for (int i = 0; i <= 25; i++) { motor.sendThrottlePercent(i); - delay(100); + delay(200); } Serial.println("Stopping motor."); @@ -80,6 +83,9 @@ void loop() { // Print DShot Info printDShotInfo(motor, Serial); + + // Take a break before next bench run + delay(3000); } ``` @@ -124,6 +130,14 @@ The main class is `DShotRMT`. Here are the most important methods: - `getEncodedFrameValue()`: Gets the last encoded DShot frame value. - `getThrottleValue()`: Gets the last transmitted throttle value. +## ⚙️ ESP-IDF Integration + +This library is built upon the ESP-IDF framework, specifically leveraging its RMT (Remote Control Peripheral) module for precise signal generation. For detailed information on the underlying ESP-IDF components and their usage, please refer to the official ESP-IDF documentation: + +* [ESP-IDF v5.5.1 Documentation](https://docs.espressif.com/projects/esp-idf/en/v5.5.1/) + +--- + ## 🤝 Contributing Contributions are welcome! Please fork the repository, create a feature branch, and submit a pull request. diff --git a/examples/throttle_percent/throttle_percent.ino b/examples/throttle_percent/throttle_percent.ino index 0ae022d..5bf6dfa 100644 --- a/examples/throttle_percent/throttle_percent.ino +++ b/examples/throttle_percent/throttle_percent.ino @@ -1,6 +1,6 @@ /** * @file throttle_percent.ino - * @brief Demo sketch for DShotRMT library using percentage throttle. + * @brief Example sketch for DShotRMT library demonstrating throttle control by percentage * @author Wastl Kraus * @date 2025-09-20 * @license MIT diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index fda65ef..7bae523 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -1,6 +1,6 @@ /** * @file web_client.ino - * @brief DShotRMT Web Control as WiFi Client + * @brief Example sketch for DShotRMT library demonstrating web control via a client * @author Wastl Kraus * @date 2025-09-11 * @license MIT @@ -16,10 +16,9 @@ ******************************************************************/ #include -#include -#include - #include +#include +#include #include "web_utilities/ota_update.h" #include "web_utilities/web_content.h" diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index cac075b..0a88df2 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -1,6 +1,6 @@ /** * @file web_control.ino - * @brief Demo sketch for DShotRMT library + * @brief Example sketch for DShotRMT library demonstrating web control via an access point * @author Wastl Kraus * @date 2025-09-09 * @license MIT @@ -16,9 +16,9 @@ ******************************************************************/ #include -#include - #include +#include +#include #include "web_utilities/web_content.h" #include diff --git a/library.properties b/library.properties index 36cdefa..24e98af 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DShotRMT -version=0.8.9 +version=0.9.0 author=Wastl Kraus maintainer=Wastl Kraus license=MIT diff --git a/src/DShotRMT.h b/src/DShotRMT.h index ca1eade..a76819c 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -9,12 +9,14 @@ #pragma once #include +#include + #include -#include #include +#include #include "dshot_definitions.h" -#include "dshot_config.h" +#include "dshot_init.h" // DShot Protocol Constants static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; @@ -69,17 +71,22 @@ public: uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } private: + // Configuration gpio_num_t _gpio; dshot_mode_t _mode; bool _is_bidirectional; uint16_t _motor_magnet_count; dshot_timing_us_t _dshot_timing; + + // RMT Handles & Config rmt_channel_handle_t _rmt_tx_channel = nullptr; rmt_channel_handle_t _rmt_rx_channel = nullptr; rmt_encoder_handle_t _dshot_encoder = nullptr; rmt_ticks_t _rmt_ticks; uint16_t _pulse_level = 1; // Default to high uint16_t _idle_level = 0; // Default to low + + // Timing & State uint64_t _last_transmission_time_us = 0; uint64_t _frame_timer_us = 0; uint16_t _last_throttle = 0; @@ -87,7 +94,7 @@ private: uint16_t _encoded_frame_value = 0; uint64_t _last_command_timestamp = 0; - // Telemetry related + // Telemetry std::atomic _last_erpm_atomic = 0; std::atomic _telemetry_ready_flag_atomic = false; rmt_rx_event_callbacks_t _rx_event_callbacks = { @@ -110,4 +117,4 @@ private: 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); }; -#include "dshot_utils.h" // Workround for util functions \ No newline at end of file +#include "dshot_utils.h" // Include for helper functions \ No newline at end of file diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index 11a4ccc..e01cbe2 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -1,10 +1,15 @@ +/** + * @file dshot_definitions.h + * @brief Defines DShot protocol constants, data structures, and command enums for DShotRMT library + * @author Wastl Kraus + * @date 2025-10-04 + * @license MIT + */ + #pragma once -#include -#include -#include -#include -#include +#include +#include // Defines the available DShot communication speeds. enum dshot_mode_t @@ -13,8 +18,7 @@ enum dshot_mode_t DSHOT150, DSHOT300, DSHOT600, - DSHOT1200, - DSHOT_MODE_MAX + DSHOT1200 }; // Represents the 16-bit DShot data packet sent to the ESC. @@ -152,6 +156,5 @@ const dshot_timing_us_t DSHOT_TIMING_US[] = { {6.67, 5.00}, // DSHOT150 {3.33, 2.50}, // DSHOT300 {1.67, 1.25}, // DSHOT600 - {0.83, 0.67}, // DSHOT1200 - {0.00, 0.00} // DSHOT_MODE_MAX (dummy entry) + {0.83, 0.67} // DSHOT1200 }; diff --git a/src/dshot_config.cpp b/src/dshot_init.cpp similarity index 91% rename from src/dshot_config.cpp rename to src/dshot_init.cpp index d2b44c4..4f2f6c3 100644 --- a/src/dshot_config.cpp +++ b/src/dshot_init.cpp @@ -1,4 +1,12 @@ -#include "dshot_config.h" +/** + * @file dshot_init.cpp + * @brief RMT configuration and initialization functions for DShotRMT library + * @author Wastl Kraus + * @date 2025-10-04 + * @license MIT + */ + +#include "dshot_init.h" // Function to initialize the RMT TX channel dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, bool is_bidirectional) @@ -38,12 +46,10 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch .mem_block_symbols = RMT_BUFFER_SYMBOLS, }; - rmt_receive_config_t rmt_rx_config = { - .signal_range_min_ns = DSHOT_PULSE_MIN_NS, - .signal_range_max_ns = DSHOT_PULSE_MAX_NS, - }; - + if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) + { return {false, DSHOT_RX_INIT_FAILED}; + } if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK) { @@ -58,6 +64,12 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch // Start the receiver to wait for incoming telemetry data rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME]; size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); + + rmt_receive_config_t rmt_rx_config = { + .signal_range_min_ns = DSHOT_PULSE_MIN_NS, + .signal_range_max_ns = DSHOT_PULSE_MAX_NS, + }; + if (rmt_receive(*out_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK) { return {false, DSHOT_RECEIVER_FAILED}; diff --git a/src/dshot_config.h b/src/dshot_init.h similarity index 78% rename from src/dshot_config.h rename to src/dshot_init.h index a50e2d5..7003f5e 100644 --- a/src/dshot_config.h +++ b/src/dshot_init.h @@ -1,8 +1,15 @@ +/** + * @file dshot_init.h + * @brief RMT configuration and initialization function declarations for DShotRMT library + * @author Wastl Kraus + * @date 2025-10-04 + * @license MIT + */ + #pragma once -#include -#include #include +#include #include "dshot_definitions.h" diff --git a/src/dshot_utils.h b/src/dshot_utils.h index 623afe3..31d15bf 100644 --- a/src/dshot_utils.h +++ b/src/dshot_utils.h @@ -1,7 +1,17 @@ +/** + * @file dshot_utils.h + * @brief Utility functions for DShotRMT library + * @author Wastl Kraus + * @date 2025-10-04 + * @license MIT + */ + #pragma once #include -#include "DShotRMT.h" // Include the full class definition + +// Forward declaration of the DShotRMT class to break circular dependency +class DShotRMT; // Error Messages static constexpr char NONE[] = ""; diff --git a/src/web_utilities/ota_update.h b/src/web_utilities/ota_update.h index 1d78be7..1d85cc3 100644 --- a/src/web_utilities/ota_update.h +++ b/src/web_utilities/ota_update.h @@ -2,7 +2,7 @@ * @file ota_update.h * @brief Contains the HTML, CSS, and JavaScript for the OTA (Over-The-Air) update web page. * @author Wastl Kraus - * @date 2025-09-13 + * @date 2025-10-04 * @license MIT */ diff --git a/src/web_utilities/web_content.h b/src/web_utilities/web_content.h index d5b3a0a..c41a6e2 100644 --- a/src/web_utilities/web_content.h +++ b/src/web_utilities/web_content.h @@ -2,7 +2,7 @@ * @file web_content.h * @brief Contains the HTML, CSS, and JavaScript for the main DShot control web page. * @author Wastl Kraus - * @date 2025-09-13 + * @date 2025-10-04 * @license MIT */ From 4a2d3f0b9a103348483ab519e2929a5f5d13c274 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Thu, 9 Oct 2025 19:41:03 +0200 Subject: [PATCH 6/6] release 0.9.0 --- .github/workflows/ci.yml | 6 +- README.md | 6 +- examples/dshot300/dshot300.ino | 2 +- src/DShotRMT.cpp | 85 ++++++++++++++------------ src/DShotRMT.h | 106 +++++++++++++++++---------------- src/dshot_definitions.h | 22 +++++-- src/dshot_utils.h | 4 +- 7 files changed, 126 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f536deb..186c763 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: ~/.arduino15/packages ~/.arduino15/cache ~/Arduino/libraries - key: linux-arduino-esp32-v2-${{ hashFiles('**/library.properties') }} + key: linux-arduino-esp32-v2-v3 - name: Install Dependencies if: steps.cache-arduino.outputs.cache-hit != 'true' @@ -83,7 +83,7 @@ jobs: ~/.arduino15/packages ~/.arduino15/cache ~/Arduino/libraries - key: linux-arduino-esp32-v2-${{ hashFiles('**/library.properties') }} + key: linux-arduino-esp32-v2-v3 - name: Run Arduino Lint uses: arduino/arduino-lint-action@v2 @@ -125,7 +125,7 @@ jobs: ~/.arduino15/packages ~/.arduino15/cache ~/Arduino/libraries - key: linux-arduino-esp32-v2-${{ hashFiles('**/library.properties') }} + key: linux-arduino-esp32-v2-v3 - name: Compile Sketch run: arduino-cli compile --fqbn esp32:esp32:esp32 --library ${{ github.workspace }} "${{ matrix.sketch }}" diff --git a/README.md b/README.md index ac47120..75697ae 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,9 @@ The main class is `DShotRMT`. Here are the most important methods: - `begin()`: Initializes the DShot RMT channels and encoder. - `sendThrottlePercent(float percent)`: Sends a throttle value as a percentage (0.0-100.0) to the ESC. - `sendThrottle(uint16_t throttle)`: Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command. -- `sendCommand(dshotCommands_e command)`: Sends a DShot command (0-47) 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 (0-47) to the ESC with a specified repeat count and delay. This is a blocking function. -- `sendCommand(uint16_t command_value)`: Sends a DShot command (0-47) to the ESC by accepting an integer value. It validates the input and then calls `sendCommand(dshotCommands_e command)`. +- `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(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. - `getESCInfo()`: Sends a command to the ESC to request ESC information. - `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal. diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 8d3ae5c..85c5f26 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -29,7 +29,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance -DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, DEFAULT_MOTOR_MAGNET_COUNT); +DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); // void setup() diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 34504d5..bba62e1 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -33,21 +33,17 @@ DShotRMT::~DShotRMT() // Cleanup TX channel if (_rmt_tx_channel) { - if (rmt_disable(_rmt_tx_channel) == DSHOT_OK) - { - rmt_del_channel(_rmt_tx_channel); - _rmt_tx_channel = nullptr; - } + rmt_disable(_rmt_tx_channel); + rmt_del_channel(_rmt_tx_channel); + _rmt_tx_channel = nullptr; } // Cleanup RX channel if (_rmt_rx_channel) { - if (rmt_disable(_rmt_rx_channel) == DSHOT_OK) - { - rmt_del_channel(_rmt_rx_channel); - _rmt_rx_channel = nullptr; - } + rmt_disable(_rmt_rx_channel); + rmt_del_channel(_rmt_rx_channel); + _rmt_rx_channel = nullptr; } // Cleanup encoder @@ -65,6 +61,7 @@ dshot_result_t DShotRMT::begin() if (!result.success) { + _cleanupRmtResources(); // Clean up any allocated resources on failure return result; } @@ -73,10 +70,7 @@ dshot_result_t DShotRMT::begin() result = init_rmt_rx_channel(_gpio, &_rmt_rx_channel, &_rx_event_callbacks, this); if (!result.success) { - // Cleanup previously allocated TX channel on failure - rmt_disable(_rmt_tx_channel); - rmt_del_channel(_rmt_tx_channel); - _rmt_tx_channel = nullptr; + _cleanupRmtResources(); // Clean up any allocated resources on failure return result; } } @@ -85,17 +79,7 @@ dshot_result_t DShotRMT::begin() if (!result.success) { - // Cleanup previously allocated channels on failure - rmt_disable(_rmt_tx_channel); - rmt_del_channel(_rmt_tx_channel); - _rmt_tx_channel = nullptr; - - if (_rmt_rx_channel) - { - rmt_disable(_rmt_rx_channel); - rmt_del_channel(_rmt_rx_channel); - _rmt_rx_channel = nullptr; - } + _cleanupRmtResources(); // Clean up any allocated resources on failure return result; } @@ -260,10 +244,10 @@ dshot_result_t DShotRMT::saveESCSettings() // Simple check bool DShotRMT::_isValidCommand(dshotCommands_e command) const { - return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= dshotCommands_e::DSHOT_CMD_MAX); + return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX); } -// +// Executes a single DShot command by building and sending a DShot frame. dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command) { uint64_t start_time = esp_timer_get_time(); @@ -314,18 +298,18 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t &data) const void DShotRMT::_preCalculateRMTTicks() { - // Pre-calculate all timing values in RMT ticks to save CPU cycles later + // Pre-calculate all timing values in RMT ticks to save CPU cycles later. _rmt_ticks.bit_length_ticks = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); _rmt_ticks.t1h_ticks = static_cast(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US); - _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double of 0 + _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a '0' bit is half of a '1' bit. _rmt_ticks.t1l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t1h_ticks; _rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks; - // Calculate the minimum time required between frames - // Pause between frames is frame time in us, some padding and about 30 us is added by hardware + // Calculate the minimum time required between frames. + // Pause between frames is frame time in us, some padding and about 30 us is added by hardware. _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; - // For bidirectional, double up + // For bidirectional, double up. if (_is_bidirectional) { _frame_timer_us = (_frame_timer_us << 1); @@ -365,12 +349,12 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) // This function needs to be fast, as it generates the RMT symbols just before sending -// 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) const { uint32_t gcr_value = 0; - // Step 1: Decode RMT symbols into a 21-bit GCR (Group Code Recording) value. + // Decode RMT symbols into a 21-bit GCR (Group Code Recording) value. // The ESC sends back a signal where the duration determines the bit value. for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i) { @@ -378,23 +362,23 @@ uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) gcr_value = (gcr_value << 1) | bit_is_one; } - // Step 2: Perform GCR decoding (GCR = Value ^ (Value >> 1)) + // Perform GCR decoding (GCR = Value ^ (Value >> 1)). uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1); - // Step 3: Extract the 16-bit DShot frame from the decoded data + // Extract the 16-bit DShot frame from the decoded data. uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET); - // Step 4: Extract data and CRC from the 16-bit frame + // Extract data and CRC from the 16-bit frame. uint16_t received_data = data_and_crc >> DSHOT_CRC_BIT_SHIFT; uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK; - // Step 5: A valid response must have the telemetry request bit set to 1. This is a sanity check. + // A valid response must have the telemetry request bit set to 1. This is a sanity check. if (!((received_data >> DSHOT_TELEMETRY_BIT_POSITION) & 1)) { return DSHOT_NULL_PACKET; } - // Step 6: Calculate and validate CRC + // Calculate and validate CRC. uint16_t calculated_crc = _calculateCRC(received_data); if (received_crc != calculated_crc) { @@ -440,3 +424,26 @@ bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const return false; } + +void DShotRMT::_cleanupRmtResources() +{ + if (_rmt_tx_channel) + { + rmt_disable(_rmt_tx_channel); + rmt_del_channel(_rmt_tx_channel); + _rmt_tx_channel = nullptr; + } + + if (_rmt_rx_channel) + { + rmt_disable(_rmt_rx_channel); + rmt_del_channel(_rmt_rx_channel); + _rmt_rx_channel = nullptr; + } + + if (_dshot_encoder) + { + rmt_del_encoder(_dshot_encoder); + _dshot_encoder = nullptr; + } +} diff --git a/src/DShotRMT.h b/src/DShotRMT.h index a76819c..211cb26 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -18,18 +18,19 @@ #include "dshot_definitions.h" #include "dshot_init.h" +// Forward declaration for the RMT receive callback +class DShotRMT; +void IRAM_ATTR rmt_rx_done_callback(rmt_channel_handle_t rx_chan, const rmt_rx_done_event_data_t *edata, void *user_data); + // DShot Protocol Constants static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; -static constexpr auto DSHOT_THROTTLE_MIN = 48; -static constexpr auto DSHOT_BITS_PER_FRAME = 16; -static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; -// +// DShotRMT class for generating DShot signals and receiving telemetry. class DShotRMT { public: - // Constructor with GPIO number - DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); + // Constructor for DShotRMT. + DShotRMT(gpio_num_t gpio, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); // Constructor using pin number DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); @@ -40,28 +41,28 @@ public: // Initialize DShotRMT dshot_result_t begin(); - // Send throttle value + // Sends a raw throttle value to the ESC. dshot_result_t sendThrottle(uint16_t throttle); - // Send throttle value as a percentage + // Sends a throttle value as a percentage to the ESC. dshot_result_t sendThrottlePercent(float percent); - // Sends a DShot command (0-47) to the ESC by accepting an integer value. + // Sends a DShot command to the ESC by accepting an integer value. dshot_result_t sendCommand(uint16_t command_value); - // Sends a DShot command (0-47) to the ESC. + // Sends a DShot command to the ESC. dshot_result_t sendCommand(dshotCommands_e command); - // Sends a DShot command (0-47) 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); - // Get telemetry data + // Retrieves telemetry data from the ESC. dshot_result_t getTelemetry(); - // Reverse motor direction directly + // Sets the motor spin direction. dshot_result_t setMotorSpinDirection(bool reversed); - // Use with caution + // Sends a command to the ESC to save its current settings. dshot_result_t saveESCSettings(); // Getters for DShot info @@ -71,50 +72,53 @@ public: uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } private: - // Configuration - gpio_num_t _gpio; - dshot_mode_t _mode; - bool _is_bidirectional; - uint16_t _motor_magnet_count; - dshot_timing_us_t _dshot_timing; + 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); - // RMT Handles & Config - rmt_channel_handle_t _rmt_tx_channel = nullptr; - rmt_channel_handle_t _rmt_rx_channel = nullptr; - rmt_encoder_handle_t _dshot_encoder = nullptr; - rmt_ticks_t _rmt_ticks; - uint16_t _pulse_level = 1; // Default to high - uint16_t _idle_level = 0; // Default to low + // DShot Configuration Parameters + gpio_num_t _gpio; // GPIO pin used for DShot communication + dshot_mode_t _mode; // DShot mode (e.g., DSHOT300, DSHOT600) + bool _is_bidirectional; // True if bidirectional DShot is enabled + uint16_t _motor_magnet_count; // Number of magnets in the motor for RPM calculation + dshot_timing_us_t _dshot_timing; // DShot timing parameters in microseconds - // Timing & State - 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; + // RMT Hardware Handles and Configuration + rmt_channel_handle_t _rmt_tx_channel = nullptr; // RMT transmit channel handle + rmt_channel_handle_t _rmt_rx_channel = nullptr; // RMT receive channel handle + rmt_encoder_handle_t _dshot_encoder = nullptr; // DShot RMT encoder handle + rmt_ticks_t _rmt_ticks; // Pre-calculated RMT timing ticks + uint16_t _pulse_level = 1; // Output level for a pulse (typically high) + uint16_t _idle_level = 0; // Output level for idle (typically low) - // Telemetry - std::atomic _last_erpm_atomic = 0; - std::atomic _telemetry_ready_flag_atomic = false; + // DShot Frame Timing and State Variables + uint64_t _last_transmission_time_us = 0; // Timestamp of the last DShot frame transmission + uint64_t _frame_timer_us = 0; // Minimum time required between DShot frames + uint16_t _last_throttle = 0; // Last transmitted throttle value + dshot_packet_t _packet; // Current DShot packet being processed + uint16_t _encoded_frame_value = 0; // Last encoded 16-bit DShot frame value + uint64_t _last_command_timestamp = 0; // Timestamp of the last command sent + + // Telemetry Related Variables + std::atomic _last_erpm_atomic = 0; // Atomically stored last received eRPM value + std::atomic _telemetry_ready_flag_atomic = false; // Atomically stored flag indicating new telemetry data rmt_rx_event_callbacks_t _rx_event_callbacks = { + // RMT receive event callbacks .on_recv_done = _on_rx_done, }; - // Private helper functions - bool _isValidCommand(dshotCommands_e command) const; - dshot_result_t _executeCommand(dshotCommands_e command); - 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; - void _preCalculateRMTTicks(); - dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; - bool IRAM_ATTR _isFrameIntervalElapsed() const; - void _recordFrameTransmissionTime(); + // Private Helper Functions for DShot Protocol Logic + bool _isValidCommand(dshotCommands_e command) const; // Checks if a given DShot command is valid + dshot_result_t _executeCommand(dshotCommands_e command); // Executes a single DShot command + dshot_packet_t _buildDShotPacket(const uint16_t &value) const; // Builds a DShot packet from a value (throttle or command) + 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 + 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 + 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 + void _recordFrameTransmissionTime(); // Records the current time as the last frame transmission time - // Static Callback Functions - 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 Callback Function for RMT RX Events + void _cleanupRmtResources(); }; -#include "dshot_utils.h" // Include for helper functions \ No newline at end of file +#include "dshot_utils.h" // Include for helper functions diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index e01cbe2..5841a34 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -11,6 +11,19 @@ #include #include +// DShot protocol definitions +static constexpr uint16_t DSHOT_FRAME_LENGTH = 16; // 11 throttle bits + 1 telemetry bit + 4 CRC bits +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_MIN = 48; // Minimum throttle value for motor spin +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_TELEMETRY_BIT_MASK = 0x0800; // Bit mask for telemetry request bit (11th bit) +static constexpr uint16_t DSHOT_CRC_MASK = 0x000F; // Bit mask for CRC bits + +// Default motor magnet count for RPM calculation +static constexpr uint16_t DEFAULT_MOTOR_MAGNET_COUNT = 14; + // Defines the available DShot communication speeds. enum dshot_mode_t { @@ -114,8 +127,7 @@ enum dshotCommands_e DSHOT_CMD_LED2_OFF, DSHOT_CMD_LED3_OFF, DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF = 30, - DSHOT_CMD_SILENT_MODE_ON_OFF = 31, - DSHOT_CMD_MAX = 47 + DSHOT_CMD_SILENT_MODE_ON_OFF = 31 }; // Custom status codes @@ -125,7 +137,6 @@ static constexpr int DSHOT_ERROR = 1; // Configuration Constants static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111; -static constexpr auto DSHOT_CRC_MASK = 0b0000000000001111; static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; 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 @@ -137,7 +148,6 @@ static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer fra 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_THROTTLE_MAX = 2047; 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; @@ -156,5 +166,5 @@ const dshot_timing_us_t DSHOT_TIMING_US[] = { {6.67, 5.00}, // DSHOT150 {3.33, 2.50}, // DSHOT300 {1.67, 1.25}, // DSHOT600 - {0.83, 0.67} // DSHOT1200 -}; + {0.83, 0.67} // DSHOT1200 +}; \ No newline at end of file diff --git a/src/dshot_utils.h b/src/dshot_utils.h index 31d15bf..72202f2 100644 --- a/src/dshot_utils.h +++ b/src/dshot_utils.h @@ -41,7 +41,7 @@ static constexpr char INVALID_COMMAND[] = "Invalid command!"; static constexpr char COMMAND_SUCCESS[] = "DShot command sent successfully"; // Helper to get result code string -inline const char *_get_result_code_str(dshot_msg_code_t code) +inline const char *get_result_code_str(dshot_msg_code_t code) { switch (code) { @@ -103,7 +103,7 @@ inline const char *_get_result_code_str(dshot_msg_code_t code) // Helper to quick print DShot result codes inline void printDShotResult(dshot_result_t &result, Stream &output = Serial) { - output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", _get_result_code_str(result.result_code)); + output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", get_result_code_str(result.result_code)); // Print telemetry data if available if (result.success && (result.erpm > 0 || result.motor_rpm > 0))