re-enable bidirectional support

re-enable bidirectional support
This commit is contained in:
Wastl Kraus 2025-11-28 00:03:36 +01:00 committed by GitHub
commit 7985030df6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 64 additions and 29 deletions

View File

@ -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.
---

View File

@ -6,7 +6,6 @@
* @license MIT
*/
#include <Arduino.h>
#include <DShotRMT.h>
// USB serial port settings
@ -15,14 +14,13 @@ 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.
// re-enabled for testing
static constexpr auto IS_BIDIRECTIONAL = false;
// Motor magnet count for RPM calculation

View File

@ -1,10 +1,10 @@
name=DShotRMT
version=0.9.0
version=0.9.1
author=Wastl Kraus <wir-sind-die-matrix.de>
maintainer=Wastl Kraus <wir-sind-die-matrix.de>
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

View File

@ -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:
@ -235,6 +236,13 @@ dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed)
return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
}
// Sends a raw DShot command to the ESC.
dshot_result_t DShotRMT::sendRawCommand(uint16_t command_value)
{
_packet = _buildDShotPacket(command_value);
return _sendDShotFrame(_packet);
}
// Use with caution
dshot_result_t DShotRMT::saveESCSettings()
{
@ -325,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)
@ -335,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
{

View File

@ -56,6 +56,9 @@ public:
// 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);
// Sends a raw DShot command to the ESC.
dshot_result_t sendRawCommand(uint16_t command_value);
// Retrieves telemetry data from the ESC.
dshot_result_t getTelemetry();

View File

@ -25,7 +25,7 @@ static constexpr uint16_t DSHOT_CRC_MASK = 0x000F; // Bit mask for CRC
static constexpr uint16_t DEFAULT_MOTOR_MAGNET_COUNT = 14;
// Defines the available DShot communication speeds.
enum dshot_mode_t
enum dshot_mode_t : uint8_t
{
DSHOT_OFF,
DSHOT150,
@ -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

View File

@ -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};
}
}