re-enabled bidirectional support
This commit is contained in:
parent
5246db57ef
commit
75d465fcf8
|
|
@ -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.
|
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.
|
The legacy version using the old `rmt.h` API is available in the `oldAPI` branch.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <DShotRMT.h>
|
#include <DShotRMT.h>
|
||||||
|
|
||||||
// USB serial port settings
|
// USB serial port settings
|
||||||
|
|
@ -15,18 +14,17 @@ static constexpr auto USB_SERIAL_BAUD = 115200;
|
||||||
|
|
||||||
// Motor configuration - Pin number or GPIO_PIN
|
// Motor configuration - Pin number or GPIO_PIN
|
||||||
static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27;
|
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)
|
// Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200)
|
||||||
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||||
|
|
||||||
// BiDirectional DShot Support (default: false)
|
// BiDirectional DShot Support (default: false)
|
||||||
// Note: Bidirectional DShot is currently not officially supported
|
// re-enabled for testing
|
||||||
// due to instability and external hardware requirements.
|
static constexpr auto IS_BIDIRECTIONAL = false;
|
||||||
static constexpr auto IS_BIDIRECTIONAL = true;
|
|
||||||
|
|
||||||
// Motor magnet count for RPM calculation
|
// Motor magnet count for RPM calculation
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
// static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
name=DShotRMT
|
name=DShotRMT
|
||||||
version=0.9.0
|
version=0.9.1
|
||||||
author=Wastl Kraus <wir-sind-die-matrix.de>
|
author=Wastl Kraus <wir-sind-die-matrix.de>
|
||||||
maintainer=Wastl Kraus <wir-sind-die-matrix.de>
|
maintainer=Wastl Kraus <wir-sind-die-matrix.de>
|
||||||
license=MIT
|
license=MIT
|
||||||
sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.
|
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.
|
paragraph=This library can control a BlHeli_S by using encoded DShot commands. Bidirectional support re-enabled.
|
||||||
category=Signal Input/Output
|
category=Signal Input/Output
|
||||||
url=https://github.com/derdoktor667/DShotRMT
|
url=https://github.com/derdoktor667/DShotRMT
|
||||||
architectures=esp32
|
architectures=esp32
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent)
|
||||||
dshot_result_t DShotRMT::sendCommand(uint16_t command_value)
|
dshot_result_t DShotRMT::sendCommand(uint16_t command_value)
|
||||||
{
|
{
|
||||||
// Validate the integer command value before casting
|
// 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};
|
return {false, DSHOT_COMMAND_NOT_VALID};
|
||||||
}
|
}
|
||||||
|
|
@ -134,6 +134,7 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command)
|
||||||
|
|
||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
|
case DSHOT_CMD_MOTOR_STOP:
|
||||||
case DSHOT_CMD_SAVE_SETTINGS:
|
case DSHOT_CMD_SAVE_SETTINGS:
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_NORMAL:
|
case DSHOT_CMD_SPIN_DIRECTION_NORMAL:
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_REVERSED:
|
case DSHOT_CMD_SPIN_DIRECTION_REVERSED:
|
||||||
|
|
@ -332,6 +333,24 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
||||||
return {true, DSHOT_NONE};
|
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);
|
_encoded_frame_value = _buildDShotFrameValue(packet);
|
||||||
|
|
||||||
// Byte-swap the 16-bit value for correct transmission order (ESP32 is little-endian, DShot is MSB first)
|
// 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
|
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;
|
|
||||||
|
// 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)
|
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, DSHOT_TRANSMISSION_FAILED};
|
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
|
_recordFrameTransmissionTime(); // Reset the timer for the next frame
|
||||||
|
|
||||||
return {true, DSHOT_TRANSMISSION_SUCCESS};
|
return {true, DSHOT_TRANSMISSION_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function needs to be fast, as it generates the RMT symbols just before sending
|
// 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
|
uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,8 @@ enum dshotCommands_e
|
||||||
DSHOT_CMD_LED2_OFF,
|
DSHOT_CMD_LED2_OFF,
|
||||||
DSHOT_CMD_LED3_OFF,
|
DSHOT_CMD_LED3_OFF,
|
||||||
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF = 30,
|
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
|
// Custom status codes
|
||||||
|
|
|
||||||
|
|
@ -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,
|
.resolution_hz = DSHOT_RMT_RESOLUTION,
|
||||||
.mem_block_symbols = RMT_BUFFER_SYMBOLS,
|
.mem_block_symbols = RMT_BUFFER_SYMBOLS,
|
||||||
.trans_queue_depth = RMT_QUEUE_DEPTH,
|
.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_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)
|
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};
|
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};
|
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,
|
.duration1 = rmt_ticks.t1l_ticks,
|
||||||
.level1 = idle_level,
|
.level1 = idle_level,
|
||||||
},
|
},
|
||||||
|
|
||||||
.flags = {
|
.flags = {
|
||||||
.msb_first = 1 // DShot is MSB first
|
.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};
|
return {true, DSHOT_ENCODER_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue