From 75d465fcf8e8f8be61d782d26f23748b95fe7dc6 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Thu, 27 Nov 2025 23:58:19 +0100 Subject: [PATCH] re-enabled bidirectional support --- README.md | 2 ++ examples/dshot300/dshot300.ino | 10 ++++---- library.properties | 6 ++--- src/DShotRMT.cpp | 42 +++++++++++++++++++++++++++++++--- src/dshot_definitions.h | 3 ++- src/dshot_init.cpp | 22 ++++-------------- 6 files changed, 55 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 75697ae..548e652 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ 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. +### Bidirectional DShot re-enabled for testing. + The legacy version using the old `rmt.h` API is available in the `oldAPI` branch. --- diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 34af55d..ac2e705 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -6,7 +6,6 @@ * @license MIT */ -#include #include // USB serial port settings @@ -15,18 +14,17 @@ static constexpr auto USB_SERIAL_BAUD = 115200; // Motor configuration - Pin number or GPIO_PIN static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27; -// static constexpr auto MOTOR01_PIN = 17; +// static constexpr auto MOTOR01_PIN = 27; // Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200) static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) -// Note: Bidirectional DShot is currently not officially supported -// due to instability and external hardware requirements. -static constexpr auto IS_BIDIRECTIONAL = true; +// re-enabled for testing +static constexpr auto IS_BIDIRECTIONAL = false; // Motor magnet count for RPM calculation -static constexpr auto MOTOR01_MAGNET_COUNT = 14; +// static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); diff --git a/library.properties b/library.properties index 24e98af..be0ea08 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=DShotRMT -version=0.9.0 +version=0.9.1 author=Wastl Kraus maintainer=Wastl Kraus license=MIT -sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S. -paragraph=This library can control a BlHeli_S by using encoded DShot commands. +sentence=DShotRMT Library supporting all DShot Types and speeds. Bidirectional support re-enabled. Tested with BlHeli_S. +paragraph=This library can control a BlHeli_S by using encoded DShot commands. Bidirectional support re-enabled. category=Signal Input/Output url=https://github.com/derdoktor667/DShotRMT architectures=esp32 diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 48f369e..56cbe8f 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -119,7 +119,7 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent) 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) + if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX_VALUE) { return {false, DSHOT_COMMAND_NOT_VALID}; } @@ -134,6 +134,7 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command) switch (command) { + case DSHOT_CMD_MOTOR_STOP: case DSHOT_CMD_SAVE_SETTINGS: case DSHOT_CMD_SPIN_DIRECTION_NORMAL: case DSHOT_CMD_SPIN_DIRECTION_REVERSED: @@ -332,6 +333,24 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) return {true, DSHOT_NONE}; } + if (_is_bidirectional) + { + // 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(_rmt_rx_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK) + { + return {false, DSHOT_RECEIVER_FAILED}; + } + } + + // Now let's prepare the actual frame _encoded_frame_value = _buildDShotFrameValue(packet); // Byte-swap the 16-bit value for correct transmission order (ESP32 is little-endian, DShot is MSB first) @@ -342,20 +361,37 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) 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; + + // TODO: Find out, why this is needed + if (_is_bidirectional) + { + // Disable RMT RX for sending + if (rmt_disable(_rmt_rx_channel) != DSHOT_OK) + { + return {false, DSHOT_RECEIVER_FAILED}; + } + } if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK) { return {false, DSHOT_TRANSMISSION_FAILED}; } + // Re-enable RMT RX + if (_is_bidirectional) + { + if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) + { + return {false, DSHOT_RECEIVER_FAILED}; + } + } + _recordFrameTransmissionTime(); // Reset the timer for the next frame return {true, DSHOT_TRANSMISSION_SUCCESS}; } // 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. uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) const { diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index 6f787fd..cf6317f 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -127,7 +127,8 @@ 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_SILENT_MODE_ON_OFF = 31, + DSHOT_CMD_MAX_VALUE = 47 }; // Custom status codes diff --git a/src/dshot_init.cpp b/src/dshot_init.cpp index 4f2f6c3..f11937e 100644 --- a/src/dshot_init.cpp +++ b/src/dshot_init.cpp @@ -17,11 +17,12 @@ dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch .resolution_hz = DSHOT_RMT_RESOLUTION, .mem_block_symbols = RMT_BUFFER_SYMBOLS, .trans_queue_depth = RMT_QUEUE_DEPTH, - }; + .flags = { + .invert_out = is_bidirectional ? 1 : 0, + .init_level = is_bidirectional ? 0 : 1}}; 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) { @@ -61,20 +62,6 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch return {false, 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); - - 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}; - } - return {true, DSHOT_RX_INIT_SUCCESS}; } @@ -94,6 +81,7 @@ dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_t .duration1 = rmt_ticks.t1l_ticks, .level1 = idle_level, }, + .flags = { .msb_first = 1 // DShot is MSB first }}; @@ -104,4 +92,4 @@ dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_t } return {true, DSHOT_ENCODER_INIT_SUCCESS}; -} +} \ No newline at end of file