From 1adde68072a132070ec6574469da660e3b424bcc Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Sat, 19 Jul 2025 16:41:04 +0200 Subject: [PATCH] ...unified ...now featuring a much more "clean" appearance ^^ --- .github/workflows/esp32.yml | 38 +++++----- DShotRMT.cpp | 100 ++++++++++++------------- DShotRMT.h | 39 +++++----- README.md | 129 +++++++++++++++++++++------------ examples/dshot300/dshot300.ino | 116 +++++++++++++++-------------- 5 files changed, 226 insertions(+), 196 deletions(-) diff --git a/.github/workflows/esp32.yml b/.github/workflows/esp32.yml index 07b482b..e165bd0 100644 --- a/.github/workflows/esp32.yml +++ b/.github/workflows/esp32.yml @@ -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: 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: Install 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 - 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 + - name: Install DShotRMT as library + run: | + 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 + \ No newline at end of file diff --git a/DShotRMT.cpp b/DShotRMT.cpp index d51f38f..0d00a66 100644 --- a/DShotRMT.cpp +++ b/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; } diff --git a/DShotRMT.h b/DShotRMT.h index 1d7b0af..a512525 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -15,24 +15,24 @@ #include // --- 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, @@ -65,21 +65,21 @@ public: // Receives and decodes the latest value from ESC, if available 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; diff --git a/README.md b/README.md index fa1c7d4..a35443c 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,70 @@ [![Arduino CI](https://github.com/derdoktor667/DShotRMT/actions/workflows/esp32.yml/badge.svg?event=push)](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 + +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,80 +72,74 @@ 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. ![DShotRMT](https://raw.githubusercontent.com/derdoktor667/DShotRMT/refs/heads/main/img/dshot300.png) --- -## 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/) +- [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/) - [ESP32 Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) --- -## 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) diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 77f2d78..368ca62 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -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,85 +32,83 @@ void printRPMPeriodically(uint16_t throttle); // Reads throttle value from serial input uint16_t readSerialThrottle(); -// void setup() { - // Start the USB Serial Port - USB_SERIAL.begin(USB_SERIAL_BAUD); + // Start the USB Serial Port + USB_SERIAL.begin(USB_SERIAL_BAUD); - // Initialize DShot Signal - motor01.begin(); + // Initialize DShot Signal + motor01.begin(); - // Arm ESC with minimum throttle - motor01.setThrottle(DSHOT_THROTTLE_MIN); + // Arm ESC with minimum throttle + motor01.setThrottle(DSHOT_THROTTLE_MIN); - USB_SERIAL.println("**********************"); - USB_SERIAL.println("DShotRMT Demo started."); - USB_SERIAL.println("Enter a throttle value (48โ€“2047):"); + USB_SERIAL.println("**********************"); + USB_SERIAL.println("DShotRMT Demo started."); + USB_SERIAL.println("Enter a throttle value (48โ€“2047):"); } -// void loop() { - // Read value input from Serial - uint16_t throttle_input = readSerialThrottle(); + // Read value input from Serial + uint16_t throttle_input = readSerialThrottle(); - // Send the value to the ESC - motor01.setThrottle(throttle_input); + // Send the value to the ESC + motor01.setThrottle(throttle_input); - // Print RPM if BiDirectional DShot is enabled - if (IS_BIDIRECTIONAL) - { - printRPMPeriodically(throttle_input); - } + // Print RPM if BiDirectional DShot is enabled + if (IS_BIDIRECTIONAL) + { + printRPMPeriodically(throttle_input); + } } // Reads throttle value from serial input uint16_t readSerialThrottle() { - static uint16_t last_throttle = DSHOT_THROTTLE_MIN; + static uint16_t last_throttle = DSHOT_THROTTLE_MIN; - if (USB_SERIAL.available() > 0) - { - String input = USB_SERIAL.readStringUntil('\n'); - int throttle_input = input.toInt(); - - // Clamp the value to the DShot range - throttle_input = constrain(throttle_input, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); - - if (throttle_input < DSHOT_THROTTLE_MIN || throttle_input > DSHOT_THROTTLE_MAX) + if (USB_SERIAL.available() > 0) { - USB_SERIAL.println("Invalid input. Please enter a value between 48 and 2047"); - } - else - { - last_throttle = throttle_input; - USB_SERIAL.print("Throttle set to: "); - USB_SERIAL.println(last_throttle); + String input = USB_SERIAL.readStringUntil('\n'); + int throttle_input = input.toInt(); + + // Clamp the value to the DShot range + throttle_input = constrain(throttle_input, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); + + if (throttle_input < DSHOT_THROTTLE_MIN || throttle_input > DSHOT_THROTTLE_MAX) + { + USB_SERIAL.println("Invalid input. Please enter a value between 48 and 2047."); + } + else + { + last_throttle = throttle_input; + USB_SERIAL.print("Throttle set to: "); + USB_SERIAL.println(last_throttle); + } + + USB_SERIAL.println("*********************************"); + USB_SERIAL.println("Enter a throttle value (48โ€“2047):"); } - USB_SERIAL.println("*********************************"); - USB_SERIAL.println("Enter a throttle value (48โ€“2047):"); - } - - return last_throttle; + return last_throttle; } // Prints RPM and throttle every 2 seconds void printRPMPeriodically(uint16_t throttle) { - static unsigned long last_print_time = 0; - unsigned long now = millis(); + static unsigned long last_print_time = 0; + unsigned long now = millis(); - if (now - last_print_time >= 2000) - { - last_print_time = now; + if (now - last_print_time >= 2000) + { + last_print_time = now; - uint32_t rpm = motor01.getMotorRPM(MOTOR01_MAGNET_COUNT); + uint32_t rpm = motor01.getMotorRPM(MOTOR01_MAGNET_COUNT); - USB_SERIAL.print("Throttle: "); - USB_SERIAL.print(throttle); - USB_SERIAL.print(" | RPM: "); - USB_SERIAL.println(rpm); - } + USB_SERIAL.print("Throttle: "); + USB_SERIAL.print(throttle); + USB_SERIAL.print(" | RPM: "); + USB_SERIAL.println(rpm); + } }