commit
af12b203c7
|
|
@ -1,32 +1,30 @@
|
|||
name: DShotRMT Example Sketch
|
||||
name: Build DShotRMT Example Sketch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@main
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Repository as Library
|
||||
run: |
|
||||
mkdir -p "$HOME/Arduino/libraries"
|
||||
ln -s "$PWD" "$HOME/Arduino/libraries/."
|
||||
|
||||
- name: Install Arduino CLI
|
||||
- name: Set up Arduino CLI
|
||||
run: |
|
||||
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
|
||||
arduino-cli core update-index
|
||||
arduino-cli core install esp32:esp32
|
||||
|
||||
- name: Compile Sketch
|
||||
- name: Install DShotRMT as library
|
||||
run: |
|
||||
arduino-cli compile --fqbn esp32:esp32:esp32 ${{ github.workspace }}/examples/dshot300
|
||||
env:
|
||||
ARDUINO_LIBRARY_PATH: ${{ github.workspace }}/libraries
|
||||
ARDUINO_DATA_PATH: ${{ github.workspace }}/arduino-data
|
||||
mkdir -p $HOME/Arduino/libraries
|
||||
ln -s $PWD $HOME/Arduino/libraries/DShotRMT
|
||||
|
||||
- name: Compile dshot300.ino example
|
||||
run: |
|
||||
arduino-cli compile --fqbn esp32:esp32:esp32 examples/dshot300/dshot300.ino
|
||||
|
||||
100
DShotRMT.cpp
100
DShotRMT.cpp
|
|
@ -11,13 +11,14 @@
|
|||
// --- DShotRMT Class ---
|
||||
// This class provides an abstraction for sending and optionally receiving DShot frames.
|
||||
// It uses ESP32's RMT peripheral for precise timing control, including BiDirectional RX.
|
||||
|
||||
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool isBidirectional, uint8_t pauseDuration)
|
||||
: _gpio(gpio), _mode(mode), _isBidirectional(isBidirectional), _pauseDuration(pauseDuration) {}
|
||||
|
||||
// Sets up RMT TX and RX channels as well as encoder configuration
|
||||
// Initializes RMT TX and RX channels and encoder configuration
|
||||
void DShotRMT::begin()
|
||||
{
|
||||
// RX RMT Channel Configuration (for BiDirectional DShot)
|
||||
// Configure RX RMT Channel for BiDirectional DShot
|
||||
if (_isBidirectional)
|
||||
{
|
||||
_rmt_rx_channel_config = {
|
||||
|
|
@ -42,7 +43,7 @@ void DShotRMT::begin()
|
|||
_receive_config.signal_range_max_ns = 5000;
|
||||
}
|
||||
|
||||
// TX RMT Channel Configuration
|
||||
// Configure TX RMT Channel
|
||||
_rmt_tx_channel_config = {
|
||||
.gpio_num = _gpio,
|
||||
.clk_src = DSHOT_CLOCK_SRC_DEFAULT,
|
||||
|
|
@ -51,7 +52,7 @@ void DShotRMT::begin()
|
|||
.trans_queue_depth = 10,
|
||||
};
|
||||
|
||||
// Configure transmission looping
|
||||
// Transmission configuration
|
||||
_transmit_config.loop_count = 0;
|
||||
_transmit_config.flags.eot_level = _isBidirectional;
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ void DShotRMT::begin()
|
|||
return;
|
||||
}
|
||||
|
||||
// Use a copy encoder to send raw symbols
|
||||
// Create copy encoder for raw symbol transmission
|
||||
if (!_dshot_encoder)
|
||||
{
|
||||
rmt_copy_encoder_config_t enc_cfg = {};
|
||||
|
|
@ -78,112 +79,109 @@ void DShotRMT::begin()
|
|||
}
|
||||
}
|
||||
|
||||
// Encodes and transmits a valid DShot Throttle value (48 - 2047)
|
||||
// Encodes and transmits a valid DShot throttle value (48 - 2047)
|
||||
void DShotRMT::setThrottle(uint16_t throttle)
|
||||
{
|
||||
// Safety first - double check input range and 11 bit "translation"
|
||||
throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0b0000011111111111;
|
||||
// Clamp input range and mask to 11 bits
|
||||
throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0x7FF;
|
||||
|
||||
_lastThrottle = throttle;
|
||||
|
||||
// Convert throttle value to DShot Paket Format
|
||||
// Convert throttle value to DShot packet format
|
||||
_tx_packet = assambleDShotPaket(_lastThrottle);
|
||||
|
||||
// Encode RMT symbols
|
||||
size_t count = 0;
|
||||
encodeDShotTX(_tx_packet, _tx_symbols, count);
|
||||
|
||||
// Send the packet
|
||||
// Transmit the packet
|
||||
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, count * sizeof(rmt_symbol_word_t), &_transmit_config) != 0)
|
||||
{
|
||||
Serial.println("Failed to transmit DShot packet");
|
||||
return;
|
||||
}
|
||||
|
||||
// Take a break
|
||||
// Pause between frames
|
||||
esp_rom_delay_us(_pauseDuration);
|
||||
}
|
||||
|
||||
// --- Get eRPM from ESC ---
|
||||
// Receives and decodes a response frame from ESC containing eRPM info
|
||||
uint32_t DShotRMT::getERPM()
|
||||
{
|
||||
if (_isBidirectional)
|
||||
{
|
||||
if (_rmt_rx_channel == nullptr)
|
||||
Serial.println("No bidirectional DShot support.");
|
||||
return _last_erpm;
|
||||
|
||||
// Attempt to receive a new frame
|
||||
// Try to receive a new frame
|
||||
if (!rmt_receive(_rmt_rx_channel, _rx_symbols, sizeof(_rx_symbols), &_receive_config))
|
||||
Serial.println("No valid DShot frame received");
|
||||
return _last_erpm;
|
||||
|
||||
_last_erpm = decodeDShotRX(_rx_symbols, DSHOT_BITS_PER_FRAME);
|
||||
return _last_erpm;
|
||||
}
|
||||
|
||||
// Nothing to do here
|
||||
// No RX possible in non-bidirectional mode
|
||||
return _last_erpm;
|
||||
}
|
||||
|
||||
// Translate eRPM value to RPM taking magnet count as parameter
|
||||
// Converts eRPM value to RPM using magnet count
|
||||
uint32_t DShotRMT::getMotorRPM(uint8_t magnet_count)
|
||||
{
|
||||
uint8_t pole_count = magnet_count / 2;
|
||||
|
||||
if (pole_count == 0)
|
||||
pole_count = 1;
|
||||
|
||||
uint32_t rpm = getERPM() / pole_count;
|
||||
return rpm;
|
||||
return getERPM() / pole_count;
|
||||
}
|
||||
|
||||
// Calculate CRC for DShot Paket
|
||||
// Calculates CRC for DShot packet
|
||||
uint16_t DShotRMT::calculateCRC(uint16_t dshot_packet)
|
||||
{
|
||||
uint16_t _packet = (dshot_packet << 1) | (_isBidirectional ? 1 : 0);
|
||||
uint16_t packet = (dshot_packet << 1) | (_isBidirectional ? 1 : 0);
|
||||
|
||||
// Clear container before new calculation
|
||||
// Reset CRC container
|
||||
_packet_crc = DSHOT_NULL_PACKET;
|
||||
|
||||
// CRC calculation for DShot (4 bits)
|
||||
_packet_crc = ((_packet ^ (_packet >> 4) ^ (_packet >> 8)) & 0b0000000000001111);
|
||||
_packet_crc = ((packet ^ (packet >> 4) ^ (packet >> 8)) & 0xF);
|
||||
|
||||
// CRC is inverted for biDirectional DShot
|
||||
// CRC is inverted for bidirectional DShot
|
||||
if (_isBidirectional)
|
||||
_packet_crc = (~_packet_crc) & 0b0000000000001111;
|
||||
_packet_crc = (~_packet_crc) & 0xF;
|
||||
|
||||
return _packet_crc;
|
||||
}
|
||||
|
||||
// Assamble DShot Paket (11 bit throttle + 1 bit telemetry request + 4 bit crc)
|
||||
// Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC)
|
||||
uint16_t DShotRMT::assambleDShotPaket(uint16_t value)
|
||||
{
|
||||
// Dummy conversion to 11 bits
|
||||
uint16_t _value = value & 0b0000011111111111;
|
||||
uint16_t throttle = value & 0x7FF;
|
||||
|
||||
// Clear container
|
||||
// Reset packet container
|
||||
_tx_packet = DSHOT_NULL_PACKET;
|
||||
|
||||
// Assemble raw DShot packet and add checksum
|
||||
_packet_crc = calculateCRC(_value);
|
||||
_packet_crc = calculateCRC(throttle);
|
||||
|
||||
_tx_packet = (_value << 1) | (_isBidirectional ? 1 : 0);
|
||||
_tx_packet = (throttle << 1) | (_isBidirectional ? 1 : 0);
|
||||
_tx_packet = (_tx_packet << 4) | _packet_crc;
|
||||
|
||||
return _tx_packet;
|
||||
}
|
||||
|
||||
// --- Encode DShot TX Frame ---
|
||||
// Converts a 16-bit packet into a valid DShot Frame for RMT
|
||||
// Converts a 16-bit packet into a valid DShot frame for RMT
|
||||
void DShotRMT::encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols, size_t &count)
|
||||
{
|
||||
// Always start encoding from the top
|
||||
count = 0;
|
||||
|
||||
uint32_t ticks_per_bit = 0;
|
||||
uint32_t ticks_zero_high = 0;
|
||||
uint32_t ticks_one_high = 0;
|
||||
|
||||
// Select timing based on DShot mode
|
||||
switch (_mode)
|
||||
{
|
||||
case DSHOT150:
|
||||
|
|
@ -217,10 +215,10 @@ void DShotRMT::encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols,
|
|||
uint32_t ticks_zero_low = ticks_per_bit - ticks_zero_high;
|
||||
uint32_t ticks_one_low = ticks_per_bit - ticks_one_high;
|
||||
|
||||
// Fill the 16 DShot-Bits Array with selected timings
|
||||
// Fill the 16 DShot bits array with selected timings
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
bool bit = (dshot_packet >> i) & 0b0000000000000001;
|
||||
bool bit = (dshot_packet >> i) & 0x1;
|
||||
if (_isBidirectional)
|
||||
{
|
||||
symbols[count].level0 = 0;
|
||||
|
|
@ -242,35 +240,33 @@ void DShotRMT::encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols,
|
|||
// Decodes a response frame from ESC containing eRPM info
|
||||
uint16_t DShotRMT::decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t count)
|
||||
{
|
||||
// Container for received frame
|
||||
uint16_t _rec_frame = DSHOT_NULL_PACKET;
|
||||
uint16_t received_frame = DSHOT_NULL_PACKET;
|
||||
|
||||
// Fill the Frame bit by bit
|
||||
// Build the frame bit by bit
|
||||
for (size_t i = 0; i < DSHOT_BITS_PER_FRAME && i < count; ++i)
|
||||
{
|
||||
bool bit = (symbols[i].duration0 < symbols[i].duration1);
|
||||
_rec_frame = (_rec_frame << 1) | bit;
|
||||
received_frame = (received_frame << 1) | bit;
|
||||
}
|
||||
|
||||
// Cut the received CRC for checking
|
||||
uint16_t _temp = _rec_frame >> 4;
|
||||
|
||||
// Store the received CRC
|
||||
uint8_t crc_recv = _rec_frame & 0b0000000000001111;
|
||||
|
||||
// Calculate CRC for received frame again
|
||||
uint8_t crc_calc = (_temp ^ (_temp >> 4) ^ (_temp >> 8)) & 0b0000000000001111;
|
||||
// Extract CRC and payload
|
||||
uint16_t payload = received_frame >> 4;
|
||||
uint8_t crc_received = received_frame & 0xF;
|
||||
|
||||
// Calculate CRC for received frame
|
||||
uint8_t crc_calculated = (payload ^ (payload >> 4) ^ (payload >> 8)) & 0xF;
|
||||
if (_isBidirectional)
|
||||
crc_calc = (~crc_calc) & 0b0000000000001111;
|
||||
crc_calculated = (~crc_calculated) & 0xF;
|
||||
|
||||
// Checking CRC
|
||||
if (crc_recv != crc_calc)
|
||||
// Check CRC
|
||||
if (crc_received != crc_calculated)
|
||||
{
|
||||
Serial.println("RX - CRC check failed.");
|
||||
return _last_erpm;
|
||||
}
|
||||
|
||||
// Cut "telemetric" bit leaving "raw" value
|
||||
uint16_t raw = _temp >> 1;
|
||||
// Remove telemetry bit, keep raw value
|
||||
uint16_t raw = payload >> 1;
|
||||
|
||||
return _last_erpm = raw;
|
||||
}
|
||||
|
|
|
|||
37
DShotRMT.h
37
DShotRMT.h
|
|
@ -15,24 +15,24 @@
|
|||
#include <driver/rmt_rx.h>
|
||||
|
||||
// --- DShot Protocol Constants ---
|
||||
static constexpr auto DSHOT_THROTTLE_FAILSAVE = 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 uint16_t DSHOT_THROTTLE_FAILSAVE = 0;
|
||||
static constexpr uint16_t DSHOT_THROTTLE_MIN = 48;
|
||||
static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047;
|
||||
static constexpr uint8_t DSHOT_BITS_PER_FRAME = 16;
|
||||
|
||||
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
|
||||
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
|
||||
static constexpr auto NO_ERPM_SIGNAL = 0;
|
||||
static constexpr uint16_t DSHOT_NULL_PACKET = 0x0000;
|
||||
static constexpr uint16_t DSHOT_FULL_PACKET = 0xFFFF;
|
||||
static constexpr uint16_t NO_ERPM_SIGNAL = 0;
|
||||
|
||||
// RMT configuration parameters
|
||||
static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
||||
static constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz Clock
|
||||
static constexpr rmt_clock_source_t DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
||||
static constexpr uint32_t DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz Clock
|
||||
|
||||
static constexpr auto TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
|
||||
static constexpr auto RX_BUFFER_SIZE = 32; // Padding for RX decoding
|
||||
static constexpr size_t TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
|
||||
static constexpr size_t RX_BUFFER_SIZE = 32; // Padding for RX decoding
|
||||
|
||||
// DShot Packet
|
||||
typedef struct dshot_packet_s
|
||||
// DShot Packet structure
|
||||
typedef struct
|
||||
{
|
||||
uint16_t throttle_value : 11;
|
||||
bool telemetric_request : 1;
|
||||
|
|
@ -40,7 +40,7 @@ typedef struct dshot_packet_s
|
|||
} dshot_packet_t;
|
||||
|
||||
// --- DShot Mode Selection ---
|
||||
typedef enum dshot_mode_e
|
||||
typedef enum
|
||||
{
|
||||
DSHOT_OFF,
|
||||
DSHOT150,
|
||||
|
|
@ -66,20 +66,20 @@ public:
|
|||
uint32_t getERPM();
|
||||
uint32_t getMotorRPM(uint8_t magnet_count);
|
||||
|
||||
// Accessors for GPIO and DShot Settings
|
||||
// Accessors for GPIO and DShot settings
|
||||
gpio_num_t getGPIO() const { return _gpio; }
|
||||
dshot_mode_t getDShotMode() const { return _mode; }
|
||||
uint8_t getPauseDuration() const { return _pauseDuration; }
|
||||
void setPauseDuration(uint8_t pauseDuration) { _pauseDuration = pauseDuration; }
|
||||
|
||||
private:
|
||||
// Calculates the checksum for throttle value
|
||||
// Calculates the checksum for a DShot packet
|
||||
uint16_t calculateCRC(uint16_t dshot_packet);
|
||||
|
||||
// Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit crc)
|
||||
// Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC)
|
||||
uint16_t assambleDShotPaket(uint16_t value);
|
||||
|
||||
// Converts a 16-bit DShot packet into RMT symbols and appends pause
|
||||
// Converts a 16-bit DShot packet into RMT symbols
|
||||
void encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols, size_t &count);
|
||||
|
||||
// Decodes the ESC answer
|
||||
|
|
@ -96,7 +96,6 @@ private:
|
|||
uint16_t _rx_packet = DSHOT_NULL_PACKET;
|
||||
uint16_t _tx_packet = DSHOT_NULL_PACKET;
|
||||
uint8_t _packet_crc = 0;
|
||||
dshot_packet_t _dshot_packet = {};
|
||||
|
||||
// --- RMT Channel Handles ---
|
||||
rmt_channel_handle_t _rmt_rx_channel = nullptr;
|
||||
|
|
|
|||
125
README.md
125
README.md
|
|
@ -1,25 +1,70 @@
|
|||
[](https://github.com/derdoktor667/DShotRMT/actions/workflows/esp32.yml)
|
||||
|
||||
## DShotRMT - ESP32 Library (Rewrite for ESP-IDF 5)
|
||||
# DShotRMT - ESP32 Library (Rewrite for ESP-IDF 5)
|
||||
|
||||
This is a complete rewrite of the original DShotRMT library to support the new ESP-IDF 5 RMT encoder API (`rmt_tx.h` / `rmt_rx.h`).
|
||||
The library sends continuous DShot frames with a pause between them and supports all standard DShot modes (150, 300, 600).
|
||||
A modern, robust C++ library for generating DShot signals on the ESP32 using the new ESP-IDF 5 RMT encoder API (`rmt_tx.h` / `rmt_rx.h`).
|
||||
Supports all standard DShot modes (150, 300, 600) and features continuous frame transmission with configurable pause.
|
||||
**Now with BiDirectional DShot support!**
|
||||
|
||||
### Now with BiDirectional DShot Support!!!
|
||||
|
||||
The old Version without encoding (rmt.h) is still available by using "oldAPI" Branch.
|
||||
> The legacy version (using the old `rmt.h` API) is still available in the `oldAPI` branch.
|
||||
|
||||
---
|
||||
|
||||
## The DShot Protocol
|
||||
## 🚀 Features
|
||||
|
||||
The DShot protocol transmits 16-bit packets to brushless ESCs:
|
||||
- **All DShot Modes:** DSHOT150, DSHOT300 (default), DSHOT600
|
||||
- **BiDirectional DShot:** Experimental support for telemetry and RPM feedback
|
||||
- **Continuous Frames:** Hardware-timed, CPU-independent signal generation
|
||||
- **Configurable Pause:** Ensures ESCs can reliably detect frame boundaries
|
||||
- **Simple API:** Easy integration into your Arduino or ESP-IDF project
|
||||
|
||||
- 11-bit throttle value
|
||||
- 1-bit telemetry request
|
||||
- 4-bit checksum
|
||||
---
|
||||
|
||||
Data is transmitted MSB-first. Pulse timing depends on the selected DShot mode.
|
||||
## 📦 Installation
|
||||
|
||||
Clone this repository and add it to your Arduino libraries or ESP-IDF components.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/derdoktor667/DShotRMT.git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
```cpp
|
||||
#include <DShotRMT.h>
|
||||
|
||||
constexpr gpio_num_t MOTOR_PIN = GPIO_NUM_17;
|
||||
constexpr dshot_mode_t MODE = DSHOT300;
|
||||
constexpr bool BIDIRECTIONAL = true;
|
||||
|
||||
DShotRMT motor(MOTOR_PIN, MODE, BIDIRECTIONAL);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
motor.begin();
|
||||
motor.setThrottle(1000); // Set throttle value (48–2047)
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Optionally read RPM if bidirectional mode is enabled
|
||||
uint32_t rpm = motor.getMotorRPM(14); // 14 magnets
|
||||
Serial.println(rpm);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 DShot Protocol Overview
|
||||
|
||||
DShot transmits 16-bit packets to brushless ESCs:
|
||||
|
||||
- **11 bits:** Throttle value
|
||||
- **1 bit:** Telemetry request
|
||||
- **4 bits:** Checksum (CRC)
|
||||
|
||||
Data is sent MSB-first. Pulse timing depends on the selected DShot mode.
|
||||
|
||||
| DSHOT | Bitrate | TH1 | TH0 | Bit Time (µs) | Frame Time (µs) |
|
||||
|-------|-------------|-------|--------|---------------|-----------------|
|
||||
|
|
@ -27,65 +72,59 @@ Data is transmitted MSB-first. Pulse timing depends on the selected DShot mode.
|
|||
| 300 | 300 kbit/s | 2.50 | 1.25 | 3.33 | ~53.28 |
|
||||
| 600 | 600 kbit/s | 1.25 | 0.625 | 1.67 | ~26.72 |
|
||||
|
||||
Each frame is followed by a pause. This helps ESCs detect separate frames.
|
||||
Each frame is followed by a pause to help ESCs detect separate frames.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Checksum Calculation
|
||||
## 🔒 Checksum Calculation
|
||||
|
||||
The checksum is calculated over the first 12 bits (throttle + bit):
|
||||
The checksum is calculated over the first 12 bits (throttle + telemetry):
|
||||
|
||||
```c
|
||||
crc = (value ^ (value >> 4) ^ (value >> 8)) & 0x0F;
|
||||
```
|
||||
|
||||
### Bidirectional DSHOT
|
||||
Bidirectional DSHOT is also known as inverted DSHOT, because the signal level is inverted, so 1 is low and a 0 is high. This is done in order to let the ESC know, that we are operating in bidirectional mode and that it should be sending back telemetry packages.
|
||||
|
||||
#### Calculating the Bidirectional CRC
|
||||
The calculation of the checksum is basically the same as before, but the inverted:
|
||||
Bidirectional DSHOT (sometimes called "inverted DSHOT") inverts the signal level:
|
||||
A logical '1' is low, and a '0' is high. This signals the ESC to send telemetry packets back.
|
||||
|
||||
**Bidirectional CRC:**
|
||||
|
||||
```c
|
||||
crc = (~(value ^ (value >> 4) ^ (value >> 8))) & 0x0F;
|
||||
```
|
||||
|
||||
...biDirectional DShot is experimental. Further Hardware testing needed.
|
||||
> **Note:** Bidirectional DShot is experimental. Further hardware testing is needed.
|
||||
|
||||
---
|
||||
|
||||
## RMT on the ESP32
|
||||
|
||||
The RMT (Remote Control) is a peripheral designed to generate accurate and stable signals to control external devices such as LEDs, motors, and other peripherals. It is well suited for generating the DShot signals in a high-performance and accurate way on the ESP32 platform.
|
||||
|
||||
### Advantages:
|
||||
## 🛠️ ESP32 RMT Peripheral
|
||||
|
||||
The RMT (Remote Control) peripheral generates accurate, hardware-timed signals for controlling external devices.
|
||||
Perfect for DShot:
|
||||
- Hardware-timed pulses
|
||||
- CPU-independent signal generation
|
||||
- CPU-independent
|
||||
- Loop mode with inter-frame pause
|
||||
- Reliable under system load
|
||||
|
||||
---
|
||||
|
||||
## About This Library
|
||||
## 📝 API Reference
|
||||
|
||||
This C++ library provides a simple class to generate DShot signals using any RMT-capable GPIO.
|
||||
It uses a `copy_encoder` to continuously send a prebuilt symbol buffer. New throttle values are applied only when they change.
|
||||
- `DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool isBidirectional, uint8_t pauseDuration = 120)`
|
||||
- `void begin()`
|
||||
- `void setThrottle(uint16_t throttle)`
|
||||
- `uint32_t getERPM()`
|
||||
- `uint32_t getMotorRPM(uint8_t magnet_count)`
|
||||
|
||||
### Supported Modes (optional BiDirectional):
|
||||
|
||||
- DSHOT150
|
||||
- DSHOT300 (default)
|
||||
- DSHOT600
|
||||
|
||||
### Frame Structure:
|
||||
|
||||
- 16-bit DShot data
|
||||
- 21-bit times worth of pause
|
||||
See [examples/dshot300/dshot300.ino](examples/dshot300/dshot300.ino) for a full demo.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
## 📖 References
|
||||
|
||||
- [DSHOT – the missing Handbook](https://brushlesswhoop.com/dshot-and-bidirectional-dshot/)
|
||||
- [DSHOT in the Dark](https://dmrlawson.co.uk/index.php/2017/12/04/dshot-in-the-dark/)
|
||||
|
|
@ -93,14 +132,14 @@ It uses a `copy_encoder` to continuously send a prebuilt symbol buffer. New thro
|
|||
|
||||
---
|
||||
|
||||
## License
|
||||
## 📄 License
|
||||
|
||||
MIT License – see LICENSE
|
||||
MIT License – see [LICENSE](LICENSE)
|
||||
|
||||
---
|
||||
|
||||
## Author
|
||||
## 👤 Author
|
||||
|
||||
Wastl Kraus
|
||||
**Wastl Kraus**
|
||||
GitHub: [@derdoktor667](https://github.com/derdoktor667)
|
||||
Website: [wir-sind-die-matrix.de](https://wir-sind-die-matrix.de)
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@
|
|||
|
||||
// USB serial port settings
|
||||
constexpr auto &USB_SERIAL = Serial0;
|
||||
constexpr auto USB_SERIAL_BAUD = 115200;
|
||||
constexpr uint32_t USB_SERIAL_BAUD = 115200;
|
||||
|
||||
// Motor configuration
|
||||
constexpr auto MOTOR01_PIN = GPIO_NUM_17;
|
||||
constexpr auto DSHOT_MODE = DSHOT300;
|
||||
constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_17;
|
||||
constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||
|
||||
// BiDirectional DShot Support (default: false)
|
||||
constexpr auto IS_BIDIRECTIONAL = false;
|
||||
constexpr bool IS_BIDIRECTIONAL = false;
|
||||
|
||||
// Motor Magnet count for RPM calculation
|
||||
constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||
// Motor magnet count for RPM calculation
|
||||
constexpr uint8_t MOTOR01_MAGNET_COUNT = 14;
|
||||
|
||||
// Setup Motor Pin, DShot Mode and optional BiDirectional Support
|
||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
||||
|
|
@ -32,7 +32,6 @@ void printRPMPeriodically(uint16_t throttle);
|
|||
// Reads throttle value from serial input
|
||||
uint16_t readSerialThrottle();
|
||||
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
// Start the USB Serial Port
|
||||
|
|
@ -49,7 +48,6 @@ void setup()
|
|||
USB_SERIAL.println("Enter a throttle value (48–2047):");
|
||||
}
|
||||
|
||||
//
|
||||
void loop()
|
||||
{
|
||||
// Read value input from Serial
|
||||
|
|
@ -80,7 +78,7 @@ uint16_t readSerialThrottle()
|
|||
|
||||
if (throttle_input < DSHOT_THROTTLE_MIN || throttle_input > DSHOT_THROTTLE_MAX)
|
||||
{
|
||||
USB_SERIAL.println("Invalid input. Please enter a value between 48 and 2047");
|
||||
USB_SERIAL.println("Invalid input. Please enter a value between 48 and 2047.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue