clean up
This commit is contained in:
parent
7985030df6
commit
7a5c425fa8
20
README.md
20
README.md
|
|
@ -28,6 +28,14 @@ Here's an example of the output from the `dshot300` example sketch:
|
||||||
- **Lightweight:** The core library has no external dependencies.
|
- **Lightweight:** The core library has no external dependencies.
|
||||||
- **Arduino and ESP-IDF Compatible:** Can be used in both Arduino and ESP-IDF projects.
|
- **Arduino and ESP-IDF Compatible:** Can be used in both Arduino and ESP-IDF projects.
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
The library is architected around a single C++ class, `DShotRMT`. It abstracts the ESP32's RMT (Remote Control) peripheral, which is a hardware timer peripheral capable of generating and receiving precisely timed signals.
|
||||||
|
|
||||||
|
1. **Signal Generation (TX):** The library uses an RMT 'bytes_encoder'. This encoder is configured with the specific pulse durations for DShot '0' and '1' bits based on the selected speed (e.g., DSHOT300, DSHOT600). When a user calls `sendThrottle()`, the library constructs a 16-bit DShot frame (11-bit throttle, 1-bit telemetry request, 4-bit CRC) and hands it to the RMT encoder. The RMT hardware then autonomously generates the correct electrical signal on the specified GPIO pin.
|
||||||
|
|
||||||
|
2. **Bidirectional Telemetry (RX):** For bidirectional communication, the library configures a second RMT channel in receive mode on the same GPIO. An interrupt service routine (`_on_rx_done`) is registered. When the ESC sends back a telemetry signal, the RMT peripheral captures it and triggers the interrupt. The interrupt code decodes the GCR-encoded signal, validates its CRC, and stores the resulting eRPM value in a thread-safe `atomic` variable. The main application can then poll for this data using the `getTelemetry()` method.
|
||||||
|
|
||||||
## ⏱️ DShot Timing Information
|
## ⏱️ DShot Timing Information
|
||||||
|
|
||||||
The DShot protocol defines specific timing characteristics for each mode. The following table outlines the bit length, T1H (high time for a '1' bit), T0H (high time for a '0' bit), and frame length for the supported DShot modes:
|
The DShot protocol defines specific timing characteristics for each mode. The following table outlines the bit length, T1H (high time for a '1' bit), T0H (high time for a '0' bit), and frame length for the supported DShot modes:
|
||||||
|
|
@ -119,18 +127,14 @@ The main class is `DShotRMT`. Here are the most important methods:
|
||||||
- `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)`: 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(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)`.
|
- `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.
|
- `sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us)`: Sends a custom DShot command to the ESC. Advanced feature, use with caution.
|
||||||
- `getESCInfo()`: Sends a command to the ESC to request ESC information.
|
- `getTelemetry()`: Retrieves telemetry data from the ESC. If bidirectional DShot is enabled, this function will return the last received telemetry data.
|
||||||
- `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal.
|
- `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal.
|
||||||
- `saveESCSettings()`: Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory.
|
- `saveESCSettings()`: Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory.
|
||||||
- `printDShotResult(dshot_result_t &result, Stream &output = Serial)`: Prints the result of a DShot operation to the specified output stream.
|
|
||||||
- `printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)`: Prints detailed DShot signal information for a given DShotRMT instance.
|
|
||||||
- `printCpuInfo(Stream &output = Serial)`: Prints detailed CPU information.
|
|
||||||
- `setMotorMagnetCount(uint16_t magnet_count)`: Sets the motor magnet count for RPM calculation.
|
|
||||||
- `getMode()`: Gets the current DShot mode.
|
- `getMode()`: Gets the current DShot mode.
|
||||||
- `isBidirectional()`: Checks if bidirectional DShot is enabled.
|
- `isBidirectional()`: Checks if bidirectional DShot is enabled.
|
||||||
- `getEncodedFrameValue()`: Gets the last encoded DShot frame value.
|
|
||||||
- `getThrottleValue()`: Gets the last transmitted throttle value.
|
- `getThrottleValue()`: Gets the last transmitted throttle value.
|
||||||
|
- `getEncodedFrameValue()`: Gets the last encoded DShot frame value.
|
||||||
|
|
||||||
## ⚙️ ESP-IDF Integration
|
## ⚙️ ESP-IDF Integration
|
||||||
|
|
||||||
|
|
@ -146,4 +150,4 @@ Contributions are welcome! Please fork the repository, create a feature branch,
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||||
|
|
@ -65,7 +65,7 @@ void loop()
|
||||||
|
|
||||||
if (input.length() > 0)
|
if (input.length() > 0)
|
||||||
{
|
{
|
||||||
handleSerialInput(input, throttle, continuous_throttle);
|
handleSerialInput(input, throttle, continuous_throttle, motor01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,14 +118,14 @@ void printMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle)
|
void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle, DShotRMT &session)
|
||||||
{
|
{
|
||||||
if (input == "0")
|
if (input == "0")
|
||||||
{
|
{
|
||||||
// Stop motor
|
// Stop motor
|
||||||
throttle = 0;
|
throttle = 0;
|
||||||
continuous_throttle = true;
|
continuous_throttle = true;
|
||||||
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
dshot_result_t result = session.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||||
printDShotResult(result);
|
printDShotResult(result);
|
||||||
}
|
}
|
||||||
else if (input == "info")
|
else if (input == "info")
|
||||||
|
|
|
||||||
23
keywords.txt
23
keywords.txt
|
|
@ -8,6 +8,18 @@
|
||||||
|
|
||||||
DShotRMT KEYWORD1
|
DShotRMT KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
begin KEYWORD2
|
||||||
|
sendThrottle KEYWORD2
|
||||||
|
sendThrottlePercent KEYWORD2
|
||||||
|
sendCommand KEYWORD2
|
||||||
|
sendCustomCommand KEYWORD2
|
||||||
|
getTelemetry KEYWORD2
|
||||||
|
setMotorSpinDirection KEYWORD2
|
||||||
|
saveESCSettings KEYWORD2
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Constants (LITERAL1)
|
# Constants (LITERAL1)
|
||||||
|
|
@ -53,15 +65,9 @@ DSHOT_CMD_LED2_OFF LITERAL1
|
||||||
DSHOT_CMD_LED3_OFF LITERAL1
|
DSHOT_CMD_LED3_OFF LITERAL1
|
||||||
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF LITERAL1
|
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF LITERAL1
|
||||||
DSHOT_CMD_SILENT_MODE_ON_OFF LITERAL1
|
DSHOT_CMD_SILENT_MODE_ON_OFF LITERAL1
|
||||||
DSHOT_CMD_MAX LITERAL1
|
|
||||||
|
|
||||||
# DShot Command Types
|
|
||||||
DSHOT_CMD_TYPE_INLINE LITERAL1
|
|
||||||
DSHOT_CMD_TYPE_BLOCKING LITERAL1
|
|
||||||
|
|
||||||
# Protocol Constants
|
# Protocol Constants
|
||||||
DSHOT_BITS_PER_FRAME LITERAL1
|
DSHOT_BITS_PER_FRAME LITERAL1
|
||||||
DSHOT_SWITCH_TIME LITERAL1
|
|
||||||
DSHOT_NULL_PACKET LITERAL1
|
DSHOT_NULL_PACKET LITERAL1
|
||||||
DSHOT_RX_TIMEOUT_MS LITERAL1
|
DSHOT_RX_TIMEOUT_MS LITERAL1
|
||||||
GCR_BITS_PER_FRAME LITERAL1
|
GCR_BITS_PER_FRAME LITERAL1
|
||||||
|
|
@ -69,12 +75,9 @@ GCR_BITS_PER_FRAME LITERAL1
|
||||||
# RMT Constants
|
# RMT Constants
|
||||||
DSHOT_CLOCK_SRC_DEFAULT LITERAL1
|
DSHOT_CLOCK_SRC_DEFAULT LITERAL1
|
||||||
DSHOT_RMT_RESOLUTION LITERAL1
|
DSHOT_RMT_RESOLUTION LITERAL1
|
||||||
RMT_BUFFER_SIZE LITERAL1
|
|
||||||
RMT_BUFFER_SYMBOLS LITERAL1
|
RMT_BUFFER_SYMBOLS LITERAL1
|
||||||
RMT_QUEUE_DEPTH LITERAL1
|
RMT_QUEUE_DEPTH LITERAL1
|
||||||
DSHOT_PULSE_MIN LITERAL1
|
|
||||||
DSHOT_PULSE_MAX LITERAL1
|
|
||||||
|
|
||||||
# Status Constants
|
# Status Constants
|
||||||
DSHOT_OK LITERAL1
|
DSHOT_OK LITERAL1
|
||||||
DSHOT_ERROR LITERAL1
|
DSHOT_ERROR LITERAL1
|
||||||
110
src/DShotRMT.cpp
110
src/DShotRMT.cpp
|
|
@ -8,6 +8,27 @@
|
||||||
|
|
||||||
#include "DShotRMT.h"
|
#include "DShotRMT.h"
|
||||||
|
|
||||||
|
// Configuration Constants
|
||||||
|
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
|
||||||
|
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
|
||||||
|
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
|
||||||
|
static constexpr auto DSHOT_PADDING_US = 20; // Pause between frames
|
||||||
|
static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer frame
|
||||||
|
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_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;
|
||||||
|
static constexpr auto DSHOT_TELEMETRY_BIT_POSITION = 11;
|
||||||
|
static constexpr auto DSHOT_CRC_BIT_SHIFT = 4;
|
||||||
|
|
||||||
|
// Command Constants
|
||||||
|
static constexpr auto DEFAULT_CMD_DELAY_US = 10;
|
||||||
|
static constexpr auto DEFAULT_CMD_REPEAT_COUNT = 1;
|
||||||
|
static constexpr auto SETTINGS_COMMAND_REPEATS = 10;
|
||||||
|
static constexpr auto SETTINGS_COMMAND_DELAY_US = 5;
|
||||||
|
|
||||||
// Constructor with GPIO number
|
// Constructor with GPIO number
|
||||||
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
||||||
: _gpio(gpio),
|
: _gpio(gpio),
|
||||||
|
|
@ -30,28 +51,7 @@ DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, ui
|
||||||
// Destructor
|
// Destructor
|
||||||
DShotRMT::~DShotRMT()
|
DShotRMT::~DShotRMT()
|
||||||
{
|
{
|
||||||
// Cleanup TX channel
|
_cleanupRmtResources();
|
||||||
if (_rmt_tx_channel)
|
|
||||||
{
|
|
||||||
rmt_disable(_rmt_tx_channel);
|
|
||||||
rmt_del_channel(_rmt_tx_channel);
|
|
||||||
_rmt_tx_channel = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup RX channel
|
|
||||||
if (_rmt_rx_channel)
|
|
||||||
{
|
|
||||||
rmt_disable(_rmt_rx_channel);
|
|
||||||
rmt_del_channel(_rmt_rx_channel);
|
|
||||||
_rmt_rx_channel = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup encoder
|
|
||||||
if (_dshot_encoder)
|
|
||||||
{
|
|
||||||
rmt_del_encoder(_dshot_encoder);
|
|
||||||
_dshot_encoder = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize DShotRMT
|
// Initialize DShotRMT
|
||||||
|
|
@ -91,7 +91,9 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
||||||
{
|
{
|
||||||
// A throttle value of 0 is a disarm command
|
// A throttle value of 0 is a disarm command
|
||||||
if (throttle == 0)
|
if (throttle == 0)
|
||||||
{
|
{
|
||||||
|
// just to be sure
|
||||||
|
_last_throttle = 0;
|
||||||
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,19 +101,19 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
||||||
_last_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
|
_last_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
|
||||||
|
|
||||||
_packet = _buildDShotPacket(_last_throttle);
|
_packet = _buildDShotPacket(_last_throttle);
|
||||||
return _sendDShotFrame(_packet);
|
return _sendPacket(_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send throttle value as a percentage
|
// Send throttle value as a percentage
|
||||||
dshot_result_t DShotRMT::sendThrottlePercent(float percent)
|
dshot_result_t DShotRMT::sendThrottlePercent(float percent)
|
||||||
{
|
{
|
||||||
if (percent < 0.0f || percent > 100.0f)
|
if (percent < DSHOT_PERCENT_MIN || percent > DSHOT_PERCENT_MAX)
|
||||||
{
|
{
|
||||||
return {false, DSHOT_PERCENT_NOT_IN_RANGE};
|
return {false, DSHOT_PERCENT_NOT_IN_RANGE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map percent to DShot throttle range
|
// Map percent to DShot throttle range
|
||||||
uint16_t throttle = static_cast<uint16_t>(DSHOT_THROTTLE_MIN + ((DSHOT_THROTTLE_MAX - DSHOT_THROTTLE_MIN) / 100.0f) * percent);
|
uint16_t throttle = static_cast<uint16_t>(DSHOT_THROTTLE_MIN + ((DSHOT_THROTTLE_MAX - DSHOT_THROTTLE_MIN) / DSHOT_PERCENT_MAX) * percent);
|
||||||
return sendThrottle(throttle);
|
return sendThrottle(throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,11 +238,45 @@ dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed)
|
||||||
return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
|
return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends a raw DShot command to the ESC.
|
dshot_result_t DShotRMT::sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us)
|
||||||
dshot_result_t DShotRMT::sendRawCommand(uint16_t command_value)
|
|
||||||
{
|
{
|
||||||
_packet = _buildDShotPacket(command_value);
|
// Validate the integer command value before casting
|
||||||
return _sendDShotFrame(_packet);
|
if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX_VALUE)
|
||||||
|
{
|
||||||
|
return {false, DSHOT_COMMAND_NOT_VALID};
|
||||||
|
}
|
||||||
|
|
||||||
|
dshot_result_t result = {false, DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
||||||
|
|
||||||
|
bool all_successful = true;
|
||||||
|
|
||||||
|
// Send command multiple times with delay
|
||||||
|
for (uint16_t i = 0; i < repeat_count; i++)
|
||||||
|
{
|
||||||
|
dshot_result_t single_result = _sendRawDshotFrame(command_value);
|
||||||
|
|
||||||
|
if (!single_result.success)
|
||||||
|
{
|
||||||
|
all_successful = false;
|
||||||
|
result.result_code = single_result.result_code;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add delay between repetitions (except for last repetition)
|
||||||
|
if (i < repeat_count - 1)
|
||||||
|
{
|
||||||
|
delayMicroseconds(delay_us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.success = all_successful;
|
||||||
|
|
||||||
|
if (result.success)
|
||||||
|
{
|
||||||
|
result.result_code = DSHOT_COMMAND_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use with caution
|
// Use with caution
|
||||||
|
|
@ -255,13 +291,19 @@ bool DShotRMT::_isValidCommand(dshotCommands_e command) const
|
||||||
return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX);
|
return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dshot_result_t DShotRMT::_sendRawDshotFrame(uint16_t value)
|
||||||
|
{
|
||||||
|
_packet = _buildDShotPacket(value);
|
||||||
|
return _sendPacket(_packet);
|
||||||
|
}
|
||||||
|
|
||||||
// Executes a single DShot command by building and sending a DShot frame.
|
// Executes a single DShot command by building and sending a DShot frame.
|
||||||
dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command)
|
dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command)
|
||||||
{
|
{
|
||||||
uint64_t start_time = esp_timer_get_time();
|
uint64_t start_time = esp_timer_get_time();
|
||||||
|
|
||||||
_packet = _buildDShotPacket(static_cast<uint16_t>(command));
|
_packet = _buildDShotPacket(static_cast<uint16_t>(command));
|
||||||
dshot_result_t result = _sendDShotFrame(_packet);
|
dshot_result_t result = _sendPacket(_packet);
|
||||||
|
|
||||||
uint64_t end_time = esp_timer_get_time();
|
uint64_t end_time = esp_timer_get_time();
|
||||||
_last_command_timestamp = end_time;
|
_last_command_timestamp = end_time;
|
||||||
|
|
@ -325,7 +367,7 @@ void DShotRMT::_preCalculateRMTTicks()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Frame Processing Functions
|
// Private Frame Processing Functions
|
||||||
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
dshot_result_t DShotRMT::_sendPacket(const dshot_packet_t &packet)
|
||||||
{
|
{
|
||||||
// Ensure enough time has passed since the last transmission
|
// Ensure enough time has passed since the last transmission
|
||||||
if (!_isFrameIntervalElapsed())
|
if (!_isFrameIntervalElapsed())
|
||||||
|
|
@ -362,7 +404,9 @@ 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
|
||||||
|
|
||||||
// TODO: Find out, why this is needed
|
// In bidirectional mode, the RMT RX channel must be disabled before transmitting.
|
||||||
|
// This is to prevent the receiver from picking up the transmitted signal, which would cause a loopback issue.
|
||||||
|
// The ESP32's RMT peripheral TX and RX channels on the same pin are not fully independent.
|
||||||
if (_is_bidirectional)
|
if (_is_bidirectional)
|
||||||
{
|
{
|
||||||
// Disable RMT RX for sending
|
// Disable RMT RX for sending
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,14 @@ public:
|
||||||
// Sends a DShot command 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);
|
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);
|
* @brief Sends a custom DShot command to the ESC. Advanced feature, use with caution.
|
||||||
|
* @param command_value The raw command value (0-47).
|
||||||
|
* @param repeat_count The number of times to send the command.
|
||||||
|
* @param delay_us The delay in microseconds between repetitions.
|
||||||
|
* @return dshot_result_t The result of the operation.
|
||||||
|
*/
|
||||||
|
dshot_result_t sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us);
|
||||||
|
|
||||||
// Retrieves telemetry data from the ESC.
|
// Retrieves telemetry data from the ESC.
|
||||||
dshot_result_t getTelemetry();
|
dshot_result_t getTelemetry();
|
||||||
|
|
@ -75,6 +81,7 @@ public:
|
||||||
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
dshot_result_t _sendRawDshotFrame(uint16_t value);
|
||||||
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 bool IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
||||||
|
|
||||||
// DShot Configuration Parameters
|
// DShot Configuration Parameters
|
||||||
|
|
@ -115,7 +122,7 @@ private:
|
||||||
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; // Combines packet data into a 16-bit DShot frame value
|
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
|
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
|
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
|
dshot_result_t _sendPacket(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
|
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
|
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
|
void _recordFrameTransmissionTime(); // Records the current time as the last frame transmission time
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ static constexpr uint16_t DSHOT_FRAME_LENGTH = 16; // 11 throttle bits
|
||||||
static constexpr uint16_t DSHOT_BITS_PER_FRAME = 16;
|
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_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_THROTTLE_MIN = 48; // Minimum throttle value for motor spin
|
||||||
|
static constexpr float DSHOT_PERCENT_MIN = 0.0f;
|
||||||
|
static constexpr float DSHOT_PERCENT_MAX = 100.0f;
|
||||||
static constexpr uint16_t DSHOT_CMD_MIN = 0; // Minimum command value
|
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_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_TELEMETRY_BIT_MASK = 0x0800; // Bit mask for telemetry request bit (11th bit)
|
||||||
|
|
@ -136,30 +138,11 @@ static constexpr int DSHOT_OK = 0;
|
||||||
static constexpr int DSHOT_ERROR = 1;
|
static constexpr int DSHOT_ERROR = 1;
|
||||||
|
|
||||||
// Configuration Constants
|
// Configuration Constants
|
||||||
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
|
|
||||||
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
|
|
||||||
static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
||||||
static constexpr auto DSHOT_RMT_RESOLUTION = 8000000; // 8 MHz resolution
|
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
|
static constexpr auto RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / 1000000; // RMT Ticks per microsecond
|
||||||
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
|
|
||||||
static constexpr auto DSHOT_PADDING_US = 20; // Pause between frames
|
|
||||||
static constexpr auto RMT_BUFFER_SYMBOLS = 64;
|
static constexpr auto RMT_BUFFER_SYMBOLS = 64;
|
||||||
static constexpr auto RMT_QUEUE_DEPTH = 1;
|
static constexpr auto RMT_QUEUE_DEPTH = 1;
|
||||||
static constexpr auto GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer frame
|
|
||||||
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_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;
|
|
||||||
static constexpr auto DSHOT_TELEMETRY_BIT_POSITION = 11;
|
|
||||||
static constexpr auto DSHOT_CRC_BIT_SHIFT = 4;
|
|
||||||
|
|
||||||
// Command Constants
|
|
||||||
static constexpr auto DEFAULT_CMD_DELAY_US = 10;
|
|
||||||
static constexpr auto DEFAULT_CMD_REPEAT_COUNT = 1;
|
|
||||||
static constexpr auto SETTINGS_COMMAND_REPEATS = 10;
|
|
||||||
static constexpr auto SETTINGS_COMMAND_DELAY_US = 5;
|
|
||||||
|
|
||||||
// Timing parameters for each DShot mode
|
// Timing parameters for each DShot mode
|
||||||
const dshot_timing_us_t DSHOT_TIMING_US[] = {
|
const dshot_timing_us_t DSHOT_TIMING_US[] = {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "dshot_definitions.h"
|
||||||
|
|
||||||
// Forward declaration of the DShotRMT class to break circular dependency
|
// Forward declaration of the DShotRMT class to break circular dependency
|
||||||
class DShotRMT;
|
class DShotRMT;
|
||||||
|
|
@ -28,9 +29,9 @@ static constexpr char ENCODING_SUCCESS[] = "Packet encoded successfully";
|
||||||
static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully";
|
static constexpr char TRANSMISSION_SUCCESS[] = "Transmission successfully";
|
||||||
static constexpr char TRANSMISSION_FAILED[] = "Transmission failed!";
|
static constexpr char TRANSMISSION_FAILED[] = "Transmission failed!";
|
||||||
static constexpr char RECEIVER_FAILED[] = "RMT receiver 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 THROTTLE_NOT_IN_RANGE[] = "Throttle not in range!";
|
||||||
static constexpr char PERCENT_NOT_IN_RANGE[] = "Percent not in range! (0.0 - 100.0)";
|
static constexpr char PERCENT_NOT_IN_RANGE[] = "Percent not in range!";
|
||||||
static constexpr char COMMAND_NOT_VALID[] = "Command not valid! (0 - 47)";
|
static constexpr char COMMAND_NOT_VALID[] = "Command not valid!";
|
||||||
static constexpr char BIDIR_NOT_ENABLED[] = "Bidirectional DShot not enabled!";
|
static constexpr char BIDIR_NOT_ENABLED[] = "Bidirectional DShot not enabled!";
|
||||||
static constexpr char TELEMETRY_SUCCESS[] = "Valid Telemetric Frame received!";
|
static constexpr char TELEMETRY_SUCCESS[] = "Valid Telemetric Frame received!";
|
||||||
static constexpr char TELEMETRY_FAILED[] = "No valid Telemetric Frame received!";
|
static constexpr char TELEMETRY_FAILED[] = "No valid Telemetric Frame received!";
|
||||||
|
|
@ -100,6 +101,24 @@ inline const char *get_result_code_str(dshot_msg_code_t code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to get DShot mode string
|
||||||
|
inline const char *get_dshot_mode_str(dshot_mode_t mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case DSHOT150:
|
||||||
|
return "DSHOT150";
|
||||||
|
case DSHOT300:
|
||||||
|
return "DSHOT300";
|
||||||
|
case DSHOT600:
|
||||||
|
return "DSHOT600";
|
||||||
|
case DSHOT1200:
|
||||||
|
return "DSHOT1200";
|
||||||
|
default:
|
||||||
|
return "DSHOT_OFF";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper to quick print DShot result codes
|
// Helper to quick print DShot result codes
|
||||||
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
|
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
|
||||||
{
|
{
|
||||||
|
|
@ -118,25 +137,7 @@ inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
|
||||||
inline void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)
|
inline void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)
|
||||||
{
|
{
|
||||||
output.println("\n === DShot Signal Info === ");
|
output.println("\n === DShot Signal Info === ");
|
||||||
|
output.printf("Current Mode: %s\n", get_dshot_mode_str(dshot_rmt.getMode()));
|
||||||
uint16_t dshot_mode_val = 0;
|
|
||||||
switch (dshot_rmt.getMode())
|
|
||||||
{
|
|
||||||
case DSHOT150:
|
|
||||||
dshot_mode_val = 150;
|
|
||||||
break;
|
|
||||||
case DSHOT300:
|
|
||||||
dshot_mode_val = 300;
|
|
||||||
break;
|
|
||||||
case DSHOT600:
|
|
||||||
dshot_mode_val = 600;
|
|
||||||
break;
|
|
||||||
case DSHOT1200:
|
|
||||||
dshot_mode_val = 1200;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.printf("Current Mode: DSHOT%d\n", dshot_mode_val);
|
|
||||||
output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO");
|
output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO");
|
||||||
output.printf("Current Packet: ");
|
output.printf("Current Packet: ");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue