From 8d0781254895f10f49438239a72d668388e455ed Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Fri, 12 Sep 2025 23:14:34 +0200 Subject: [PATCH 1/6] RMT timing rewritten --- src/DShotRMT.cpp | 73 ++++++++++++++++++++++++++---------------------- src/DShotRMT.h | 72 +++++++++++++++++++++++++---------------------- 2 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index d64cdec..7575a90 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -8,14 +8,28 @@ #include "DShotRMT.h" +// --- HELPERS --- +void printDShotResult(dshot_result_t &result, Stream &output) +{ + output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); + + // Print telemetry data if available + if (result.success && (result.erpm > 0 || result.motor_rpm > 0)) + { + output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm); + } + + output.println(); +} + // Timing parameters for each DShot mode -// Format: {frame_length_us, ticks_per_bit, ticks_one_high, ticks_one_low, ticks_zero_high, ticks_zero_low} -static constexpr dshot_timing_t DSHOT_TIMINGS[] = { - {0, 0, 0, 0, 0, 0}, // DSHOT_OFF - {128, 64, 48, 16, 24, 40}, // DSHOT150 - {64, 32, 24, 8, 12, 20}, // DSHOT300 - {32, 16, 12, 4, 6, 10}, // DSHOT600 - {16, 8, 6, 2, 3, 5} // DSHOT1200 +// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks} +static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { + {0.00, 0.00}, + {6.67, 5.00}, + {3.33, 2.50}, + {1.67, 1.25}, + {0.83, 0.67} }; // Constructor with GPIO number @@ -26,9 +40,10 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _last_erpm_atomic(0), _telemetry_ready_flag(false), _frame_timer_us(0), - _timing_config(DSHOT_TIMINGS[mode]), + _dshot_timing(DSHOT_TIMING_US[mode]), + _rmt_ticks{0}, _last_throttle(DSHOT_CMD_MOTOR_STOP), - _last_transmission_time(0), + _last_transmission_time_us(0), _parsed_packet(0), _packet{0}, _bitPositions{0}, @@ -42,8 +57,15 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _transmit_config{}, _receive_config{} { - // Calculate frame timing including switch/pause time - _frame_timer_us = _timing_config.frame_length_us + DSHOT_PAUSE_US; + // Convert DShot timings (us) to RMT ticks + _rmt_ticks.ticks_per_bit = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); + _rmt_ticks.t1h_ticks = static_cast(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US); + _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0 + _rmt_ticks.t1l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t1h_ticks; + _rmt_ticks.t0l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t0h_ticks; + + // Pause between frames is frame time in us, some padding and about 30 us is added by hardware + _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; // Double frame time for bidirectional mode (includes response time) if (_is_bidirectional) @@ -54,7 +76,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) // Constructor using pin number DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional) - : DShotRMT((gpio_num_t)pin_nr, mode, is_bidirectional) + : DShotRMT(static_cast(pin_nr), mode, is_bidirectional) { // Delegates to primary constructor with type cast } @@ -152,7 +174,6 @@ dshot_result_t DShotRMT::_initTXChannel() // Init RMT RX channel dshot_result_t DShotRMT::_initRXChannel() { - // Direct RMT symbol processing - Performance optimized _rx_event_callbacks.on_recv_done = _rmt_rx_done_callback; @@ -258,7 +279,7 @@ dshot_result_t DShotRMT::sendCommand(uint16_t command) dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) { // Result container with unified structure - dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_ERPM, NO_DSHOT_RPM}; + dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; // Check if bidirectional mode is enabled if (!_is_bidirectional) @@ -277,7 +298,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) // if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1) { - uint8_t pole_pairs = max(MIN_POLE_PAIRS, (magnet_count / MAGNETS_PER_POLE_PAIR)); + uint8_t pole_pairs = max(POLE_PAIRS_MIN, (magnet_count / MAGNETS_PER_POLE_PAIR)); uint32_t motor_rpm = (erpm / pole_pairs); result.success = true; @@ -426,9 +447,9 @@ bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001; symbols[i].level0 = _level0; - symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high; + symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks; symbols[i].level1 = _level1; - symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low; + symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks; } return DSHOT_OK; @@ -487,7 +508,7 @@ bool DShotRMT::_timer_signal() uint64_t current_time = esp_timer_get_time(); // Handle potential overflow - uint64_t elapsed = current_time - _last_transmission_time; + uint64_t elapsed = current_time - _last_transmission_time_us; return elapsed >= _frame_timer_us; } @@ -495,7 +516,7 @@ bool DShotRMT::_timer_signal() // Reset transmission timer to current time bool DShotRMT::_timer_reset() { - _last_transmission_time = esp_timer_get_time(); + _last_transmission_time_us = esp_timer_get_time(); return DSHOT_OK; } @@ -546,17 +567,3 @@ void DShotRMT::printCpuInfo(Stream &output) const output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz()); output.printf("APB Freq = %lu Hz\n", getApbFrequency()); } - -// --- HELPERS --- -void printDShotResult(dshot_result_t &result, Stream &output) -{ - output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); - - // Print telemetry data if available - if (result.success && (result.erpm > 0 || result.motor_rpm > 0)) - { - output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm); - } - - output.println(); -} diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 3a4d466..43eab78 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -20,29 +20,7 @@ static constexpr auto DSHOT_THROTTLE_FAILSAFE = 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 auto DSHOT_PAUSE_US = 30; // Additional frame pause time -static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; -static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111; -static constexpr auto DSHOT_CRC_MASK = 0b0000000000001111; -static constexpr auto DSHOT_RX_TIMEOUT_MS = 2; // Never reached, just a timeeout -static constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC) static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; -static constexpr auto MAGNETS_PER_POLE_PAIR = 2; -static constexpr auto MIN_POLE_PAIRS = 1; -static constexpr auto NO_DSHOT_ERPM = 0; -static constexpr auto NO_DSHOT_RPM = 0; - -// RMT Configuration Constants -constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; -constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution -constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; -constexpr auto RMT_BUFFER_SYMBOLS = 64; -constexpr auto RMT_QUEUE_DEPTH = 1; - -// Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us. -// The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes. -constexpr uint32_t DSHOT_PULSE_MIN = 3000; -constexpr uint32_t DSHOT_PULSE_MAX = 60000; // DShot Modes typedef enum @@ -62,16 +40,22 @@ typedef struct uint16_t checksum : 4; } dshot_packet_t; -// DShot Timing Configuration +// DShot Timings +typedef struct +{ + double bit_length_us; + double t1h_lenght_us; +} dshot_timing_us_t; + +// RMT Ticks Configuration typedef struct { - uint32_t frame_length_us; uint16_t ticks_per_bit; - uint16_t ticks_one_high; - uint16_t ticks_one_low; - uint16_t ticks_zero_high; - uint16_t ticks_zero_low; -} dshot_timing_t; + uint16_t t1h_ticks; + uint16_t t1l_ticks; + uint16_t t0h_ticks; + uint16_t t0l_ticks; +} rmt_ticks_t; // Unified DShot result structure typedef struct @@ -149,11 +133,12 @@ private: dshot_mode_t _mode; bool _is_bidirectional; uint32_t _frame_timer_us; - const dshot_timing_t &_timing_config; + rmt_ticks_t _rmt_ticks; + const dshot_timing_us_t &_dshot_timing; uint16_t _last_throttle; // --- TIMING & PACKET VARIABLES --- - uint64_t _last_transmission_time; + uint64_t _last_transmission_time_us; uint16_t _parsed_packet; dshot_packet_t _packet; uint8_t _bitPositions[DSHOT_BITS_PER_FRAME]; @@ -193,13 +178,12 @@ private: // -- CALLBACKS --- rmt_rx_event_callbacks_t _rx_event_callbacks; - volatile rmt_symbol_word_t _rx_symbols_direct[GCR_BITS_PER_FRAME]; volatile uint16_t _last_erpm_atomic; volatile bool _telemetry_ready_flag; static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); // --- DSHOT DEFAULTS --- - static constexpr auto const DSHOT_TELEMETRY_INVALID = (0xffff); + static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111; // --- CONSTANTS & ERROR MESSAGES --- static constexpr bool DSHOT_OK = 0; @@ -226,4 +210,26 @@ private: static constexpr char const *TELEMETRY_FAILED = "No valid Telemetric Frame received!"; static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!"; static constexpr char const *TIMING_CORRECTION = "Timing correction!"; + + // Configuration Constants + static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000; + static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111; + static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111; + static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; + static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution + static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond, based on the RMT resolution in MHz + static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; + static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2; // Never reached + static constexpr auto const DSHOT_PADDING_US = 3; + static constexpr auto const RMT_BUFFER_SYMBOLS = 64; + static constexpr auto const RMT_QUEUE_DEPTH = 1; + static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC) + static constexpr auto const POLE_PAIRS_MIN = 1; + static constexpr auto const MAGNETS_PER_POLE_PAIR = 2; + static constexpr auto const NO_DSHOT_TELEMETRY = 0; + + // Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us. + // The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes. + static constexpr auto const DSHOT_PULSE_MIN = 3000; + static constexpr auto const DSHOT_PULSE_MAX = 60000; }; From 7e8c7466cdc9de7943474d2aea49e71168f71380 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Sat, 13 Sep 2025 11:05:33 +0200 Subject: [PATCH 2/6] Update packet building --- src/DShotRMT.cpp | 2 +- src/DShotRMT.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 7575a90..395125e 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -312,7 +312,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) } // Build a complete DShot packet -dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) +dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) { // Init packet structure dshot_packet_t packet = {}; diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 43eab78..31cad2d 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -162,7 +162,7 @@ private: dshot_result_t _initDShotEncoder(); // --- PACKET MANAGEMENT --- - dshot_packet_t _buildDShotPacket(const uint16_t value); + dshot_packet_t _buildDShotPacket(const uint16_t &value); uint16_t _parseDShotPacket(const dshot_packet_t &packet); uint16_t _calculateCRC(const uint16_t data); void _preCalculateBitPositions(); From fbf4559da3e94de4235265a796054f918a3be84b Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Sat, 13 Sep 2025 11:54:30 +0200 Subject: [PATCH 3/6] Internal clean up Some sorting and grouping --- .github/workflows/ci.yml | 128 +++++---- examples/web_client/web_client.ino | 352 +++++++++++++++++++++++++ examples/web_control/web_control.ino | 352 +++++++++++++++++++++++++ library.properties | 2 +- src/DShotRMT.cpp | 380 ++++++++++++++------------- src/DShotRMT.h | 196 +++++++------- src/web_content.h | 361 ------------------------- 7 files changed, 1071 insertions(+), 700 deletions(-) delete mode 100644 src/web_content.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23500d5..8e95371 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ name: ESP32 Build & Quality Check + permissions: contents: read @@ -8,80 +9,106 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: # ============================================================================ # Code Quality & Linting # ============================================================================ quality-check: - name: 'Arduino Lint Check' + name: Arduino Lint Check runs-on: ubuntu-latest timeout-minutes: 10 steps: - - name: Checkout Repository - uses: actions/checkout@v5 + - uses: actions/checkout@v5 - - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v2 + - uses: arduino/setup-arduino-cli@v2 - - name: Install ESP32 core + - name: Cache Arduino Core + uses: actions/cache@v4 + with: + path: | + ~/.arduino15/packages + ~/.arduino15/cache + key: arduino-core-${{ runner.os }}-esp32-v1 + restore-keys: | + arduino-core-${{ runner.os }}- + + - name: Install ESP32 Core run: | - arduino-cli core update-index > /dev/null - arduino-cli core install esp32:esp32 > /dev/null + arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + arduino-cli core install esp32:esp32 - - name: Arduino Lint - uses: arduino/arduino-lint-action@v2 + - uses: arduino/arduino-lint-action@v2 with: path: ${{ github.workspace }} compliance: strict library-manager: update verbose: true -# ============================================================================ + # ============================================================================ # Compilation Test # ============================================================================ compile-test: - name: 'Compile Example Sketches' + name: Compile Example Sketches runs-on: ubuntu-latest timeout-minutes: 15 strategy: fail-fast: false matrix: - examples: - - "examples/dshot300/dshot300.ino" - - "examples/command_manager/command_manager.ino" - - "examples/web_control/web_control.ino" - - "examples/web_client/web_client.ino" + example: + - examples/dshot300/dshot300.ino + - examples/command_manager/command_manager.ino + - examples/web_control/web_control.ino + - examples/web_client/web_client.ino steps: - - name: Checkout Repository - uses: actions/checkout@v5 + - uses: actions/checkout@v5 - - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v2 + - uses: arduino/setup-arduino-cli@v2 + + - name: Cache Arduino Core & Libraries + uses: actions/cache@v4 + with: + path: | + ~/.arduino15/packages + ~/.arduino15/cache + ~/Arduino/libraries + key: arduino-full-${{ runner.os }}-esp32-v1 + restore-keys: | + arduino-full-${{ runner.os }}- - name: Install ESP32 Core and Dependencies run: | - arduino-cli core update-index + arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json arduino-cli core install esp32:esp32 - arduino-cli lib install "ArduinoJson" - # Workround for ESPAsyncWebServer - git clone https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer - git clone https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP - + mkdir -p ~/Arduino/libraries + + # Cached repository check + if [ ! -d ~/Arduino/libraries/ESPAsyncWebServer ]; then + git clone --depth=1 https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer + fi + + if [ ! -d ~/Arduino/libraries/AsyncTCP ]; then + git clone --depth=1 https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP + fi + - name: Compile Sketch run: | - arduino-cli compile --fqbn esp32:esp32:esp32 --library ${{ github.workspace }} ${{ matrix.examples}} + arduino-cli compile \ + --fqbn esp32:esp32:esp32 \ + --library ${{ github.workspace }} \ + ${{ matrix.example }} # ============================================================================ # Build Status Report # ============================================================================ build-summary: - name: 'Build Summary' + name: Build Summary runs-on: ubuntu-latest if: always() needs: [quality-check, compile-test] @@ -90,32 +117,21 @@ jobs: - name: Create Build Summary run: | echo "# 🔧 DShotRMT Build Report" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY + echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY - - # Quality Check Status - if [[ "${{ needs.quality-check.result }}" == "success" ]]; then - echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY - else - echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY - fi - - # Compile Test Status - if [[ "${{ needs.compile-test.result }}" == "success" ]]; then - echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY - else - echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY - fi - + + [[ "${{ needs.quality-check.result }}" == "success" ]] \ + && echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY \ + || echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY + + [[ "${{ needs.compile-test.result }}" == "success" ]] \ + && echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY \ + || echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY - - # Overall Status - if [[ "${{ needs.quality-check.result }}" == "success" && - "${{ needs.compile-test.result }}" == "success" ]]; then - echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY - echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY - else - echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY - echo "Please review the failed checks and address any issues." - fi + + [[ "${{ needs.quality-check.result }}" == "success" && "${{ needs.compile-test.result }}" == "success" ]] \ + && echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY \ + && echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY \ + || echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY \ + && echo "Please review the failed checks and address any issues." >> $GITHUB_STEP_SUMMARY diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index 79c9004..e4a3e2f 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -62,6 +62,358 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp bool connectToWiFi(); void printWiFiStatus(); +// Web Site Content +const char index_html[] PROGMEM = R"rawliteral( + + + + + + + DShotRMT Web Client + + + + +

DShotRMT Web Client

+
+ +
+
+ ARMING SWITCH + +
+
+ DISARMED +
+
+ ⚠️ Disabled when disarmed! +
+
+ + +
+
0
+ +
+ +
+ RPM: -- +
+
+ + + + + + +)rawliteral"; + // void setup() { diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index 3f1e400..2b66c8b 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -58,6 +58,358 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len); void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); void setArmingStatus(bool armed); +// Web Site Content +const char index_html[] PROGMEM = R"rawliteral( + + + + + + + DShotRMT Web Client + + + + +

DShotRMT Web Client

+
+ +
+
+ ARMING SWITCH + +
+
+ DISARMED +
+
+ ⚠️ Disabled when disarmed! +
+
+ + +
+
0
+ +
+ +
+ RPM: -- +
+
+ + + + + + +)rawliteral"; + // void setup() { diff --git a/library.properties b/library.properties index 346113b..243274d 100644 --- a/library.properties +++ b/library.properties @@ -8,5 +8,5 @@ paragraph=This library can control a BlHeli_S by using encoded DShot commands. F category=Signal Input/Output url=https://github.com/derdoktor667/DShotRMT architectures=esp32 -provides_includes=DShotRMT.h, DShotCommandManager.h, dshot_commands.h, web_content.h +provides_includes=DShotRMT.h, DShotCommandManager.h, dshot_commands.h depends=ArduinoJson \ No newline at end of file diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 395125e..aef9490 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -8,7 +8,17 @@ #include "DShotRMT.h" -// --- HELPERS --- +// Static Data & Helper Functions +// Timing parameters for each DShot mode +// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks} +static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { + {0.00, 0.00}, + {6.67, 5.00}, + {3.33, 2.50}, + {1.67, 1.25}, + {0.83, 0.67}}; + +// Helper function to print DShot results void printDShotResult(dshot_result_t &result, Stream &output) { output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); @@ -22,25 +32,14 @@ void printDShotResult(dshot_result_t &result, Stream &output) output.println(); } -// Timing parameters for each DShot mode -// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks} -static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { - {0.00, 0.00}, - {6.67, 5.00}, - {3.33, 2.50}, - {1.67, 1.25}, - {0.83, 0.67} -}; - +// Constructors & Destructor // Constructor with GPIO number DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) : _gpio(gpio), _mode(mode), _is_bidirectional(is_bidirectional), - _last_erpm_atomic(0), - _telemetry_ready_flag(false), - _frame_timer_us(0), _dshot_timing(DSHOT_TIMING_US[mode]), + _frame_timer_us(0), _rmt_ticks{0}, _last_throttle(DSHOT_CMD_MOTOR_STOP), _last_transmission_time_us(0), @@ -55,23 +54,13 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _tx_channel_config{}, _rx_channel_config{}, _transmit_config{}, - _receive_config{} + _receive_config{}, + _rx_event_callbacks{}, + _last_erpm_atomic(0), + _telemetry_ready_flag_atomic(false) { - // Convert DShot timings (us) to RMT ticks - _rmt_ticks.ticks_per_bit = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); - _rmt_ticks.t1h_ticks = static_cast(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US); - _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0 - _rmt_ticks.t1l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t1h_ticks; - _rmt_ticks.t0l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t0h_ticks; - - // Pause between frames is frame time in us, some padding and about 30 us is added by hardware - _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; - - // Double frame time for bidirectional mode (includes response time) - if (_is_bidirectional) - { - _frame_timer_us = (_frame_timer_us << 1); - } + // Configure RMT ticks for DShot timings + _configureRMTTiming(); } // Constructor using pin number @@ -81,10 +70,10 @@ DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional) // Delegates to primary constructor with type cast } -// Destructor for "better" code +// Destructor DShotRMT::~DShotRMT() { - // ...TX + // Cleanup TX channel if (_rmt_tx_channel) { if (rmt_disable(_rmt_tx_channel) == DSHOT_OK) @@ -94,7 +83,7 @@ DShotRMT::~DShotRMT() } } - // ...RX + // Cleanup RX channel if (_rmt_rx_channel) { if (rmt_disable(_rmt_rx_channel) == DSHOT_OK) @@ -104,7 +93,7 @@ DShotRMT::~DShotRMT() } } - // ...Encoder + // Cleanup encoder if (_dshot_encoder) { rmt_del_encoder(_dshot_encoder); @@ -112,10 +101,12 @@ DShotRMT::~DShotRMT() } } -// Init DShotRMT + +// Public Core Functions +// Initialize DShotRMT dshot_result_t DShotRMT::begin() { - // Init RX channel first + // Init RX channel first (for bidirectional mode) if (_is_bidirectional) { if (!_initRXChannel().success) @@ -142,104 +133,6 @@ dshot_result_t DShotRMT::begin() return {true, INIT_SUCCESS}; } -// Init RMT TX channel -dshot_result_t DShotRMT::_initTXChannel() -{ - // Configure TX channel - _tx_channel_config.gpio_num = _gpio; - _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; - - // Config RMT TX - _transmit_config.loop_count = 0; // No automatic loops - real-time calculation - _transmit_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag - - // Create RMT TX channel - if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) - { - return {false, TX_INIT_FAILED}; - } - - // - if (rmt_enable(_rmt_tx_channel) != DSHOT_OK) - { - return {false, TX_INIT_FAILED}; - } - - return {true, TX_INIT_SUCCESS}; -} - -// Init RMT RX channel -dshot_result_t DShotRMT::_initRXChannel() -{ - // Direct RMT symbol processing - Performance optimized - _rx_event_callbacks.on_recv_done = _rmt_rx_done_callback; - - // Config RMT RX - _rx_channel_config.gpio_num = _gpio; - _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - - // Config RMT RX parameters - _receive_config.signal_range_min_ns = DSHOT_PULSE_MIN; - _receive_config.signal_range_max_ns = DSHOT_PULSE_MAX; - - // Create RMT RX channel - if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) - { - return {false, RX_INIT_FAILED}; - } - - // - if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) - { - return {false, RX_INIT_FAILED}; - } - - return {true, RX_INIT_SUCCESS}; -} - -// Callback for RMT RX -bool DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) -{ - DShotRMT *instance = static_cast(user_data); - - // ISR check for valid data - if (edata && edata->num_symbols >= GCR_BITS_PER_FRAME && edata->num_symbols <= GCR_BITS_PER_FRAME) - { - - // Direct decoding - uint16_t erpm = instance->_decodeDShotFrame(edata->received_symbols); - - if (erpm != DSHOT_NULL_PACKET) - { - // Atomic writes - thread-safe - instance->_last_erpm_atomic = erpm; - instance->_telemetry_ready_flag = true; - } - } - - return false; -} - -// Initialize DShot encoder -dshot_result_t DShotRMT::_initDShotEncoder() -{ - // Create copy encoder configuration - rmt_copy_encoder_config_t encoder_config = {}; - - // Create encoder instance - if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) - { - return {false, ENCODER_INIT_FAILED}; - } - - return {true, TX_INIT_SUCCESS}; -} - // Send throttle value dshot_result_t DShotRMT::sendThrottle(uint16_t throttle) { @@ -288,14 +181,14 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) return result; } - // - if (_telemetry_ready_flag) + // Check for new telemetry data + if (_telemetry_ready_flag_atomic) { - _telemetry_ready_flag = false; + _telemetry_ready_flag_atomic = false; uint16_t erpm = _last_erpm_atomic; - // + // Calculate motor RPM from eRPM if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1) { uint8_t pole_pairs = max(POLE_PAIRS_MIN, (magnet_count / MAGNETS_PER_POLE_PAIR)); @@ -311,6 +204,131 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) return result; } +// Public Info & Debug Functions +// Print timing diagnostic information to specified stream +void DShotRMT::printDShotInfo(Stream &output) const +{ + output.println(" "); + output.println(" === DShot Signal Info === "); + + // Current DShot mode + output.printf("Current Mode: DSHOT%d\n", + _mode == DSHOT150 ? 150 : + _mode == DSHOT300 ? 300 : + _mode == DSHOT600 ? 600 : + _mode == DSHOT1200 ? 1200 : 0); + + output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO"); + + // Packet Info + output.printf("Current Packet: "); + + // Print bit by bit + for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) + { + if ((_parsed_packet >> i) & 1) + { + output.print("1"); + } + else + { + output.print("0"); + } + } + output.printf("\n"); + + output.printf("Current Value: %u\n", _packet.throttle_value); +} + +// Print CPU information +void DShotRMT::printCpuInfo(Stream &output) const +{ + output.println(" "); + output.println(" === CPU Info === "); + output.printf("Chip Model: %s\n", ESP.getChipModel()); + output.printf("Chip Revision: %d\n", ESP.getChipRevision()); + output.printf("CPU Freq = %lu MHz\n", ESP.getCpuFreqMHz()); + output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz()); + output.printf("APB Freq = %lu Hz\n", getApbFrequency()); +} + +// Private Initialization Functions +// Initialize RMT TX channel +dshot_result_t DShotRMT::_initTXChannel() +{ + // Configure TX channel + _tx_channel_config.gpio_num = _gpio; + _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; + _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; + _tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; + _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; + + // Config RMT TX + _transmit_config.loop_count = 0; // No automatic loops - real-time calculation + _transmit_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag + + // Create RMT TX channel + if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) + { + return {false, TX_INIT_FAILED}; + } + + // Enable TX channel + if (rmt_enable(_rmt_tx_channel) != DSHOT_OK) + { + return {false, TX_INIT_FAILED}; + } + + return {true, TX_INIT_SUCCESS}; +} + +// Initialize RMT RX channel +dshot_result_t DShotRMT::_initRXChannel() +{ + // Direct RMT symbol processing - Performance optimized + _rx_event_callbacks.on_recv_done = _rmt_rx_done_callback; + + // Config RMT RX + _rx_channel_config.gpio_num = _gpio; + _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; + _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; + _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; + + // Config RMT RX parameters + _receive_config.signal_range_min_ns = DSHOT_PULSE_MIN; + _receive_config.signal_range_max_ns = DSHOT_PULSE_MAX; + + // Create RMT RX channel + if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) + { + return {false, RX_INIT_FAILED}; + } + + // Enable RX channel + if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) + { + return {false, RX_INIT_FAILED}; + } + + return {true, RX_INIT_SUCCESS}; +} + +// Initialize DShot encoder +dshot_result_t DShotRMT::_initDShotEncoder() +{ + // Create copy encoder configuration + rmt_copy_encoder_config_t encoder_config = {}; + + // Create encoder instance + if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) + { + return {false, ENCODER_INIT_FAILED}; + } + + return {true, TX_INIT_SUCCESS}; +} + +// Private Packet Management Functions // Build a complete DShot packet dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) { @@ -361,7 +379,27 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data) return crc; } -// Per calculate bits - Performance optimized +// Configure RMT ticks for DShot timings +void DShotRMT::_configureRMTTiming() +{ + // Convert DShot timings (us) to RMT ticks + _rmt_ticks.ticks_per_bit = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); + _rmt_ticks.t1h_ticks = static_cast(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US); + _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0 + _rmt_ticks.t1l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t1h_ticks; + _rmt_ticks.t0l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t0h_ticks; + + // Pause between frames is frame time in us, some padding and about 30 us is added by hardware + _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; + + // Double frame time for bidirectional mode (includes response time) + if (_is_bidirectional) + { + _frame_timer_us = (_frame_timer_us << 1); + } +} + +// Precalculate bit positions for performance optimization void DShotRMT::_preCalculateBitPositions() { for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) @@ -370,6 +408,7 @@ void DShotRMT::_preCalculateBitPositions() } } +// Private Frame Processing Functions // Transmit DShot packet via RMT dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) { @@ -379,7 +418,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) return {false, TIMING_CORRECTION}; } - // Enable RMT RX before RMT TX + // Enable RMT RX before RMT TX (bidirectional mode) if (_is_bidirectional) { // Calculate transmission data size @@ -442,7 +481,7 @@ bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t // Decode MSB for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) { - // Use precalculated bit positions - Performace optimized + // Use precalculated bit positions - Performance optimized int bit_position = _bitPositions[i]; bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001; @@ -455,7 +494,7 @@ bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t return DSHOT_OK; } -// Decodes a DShot telemetry frame from received RMT symbols. +// Decode DShot telemetry frame from received RMT symbols uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) { uint32_t gcr_value = 0; @@ -476,10 +515,8 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) // The first bit of the GCR frame is a start bit and is discarded. uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET); - // Cutting 4 bits? + // Extract data (first 12 bits) and CRC (last 4 bits) uint16_t received_data = data_and_crc >> 4; - - // Masking CRC uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK; // Telemetry request bit has to be 1 @@ -502,6 +539,7 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) return received_data & DSHOT_THROTTLE_MAX; } +// Private Timing Control Functions // Check if enough time has passed for next transmission bool DShotRMT::_timer_signal() { @@ -521,49 +559,25 @@ bool DShotRMT::_timer_reset() return DSHOT_OK; } -// Print timing diagnostic information to specified stream -void DShotRMT::printDShotInfo(Stream &output) const +// Static Callback Functions +// Callback for RMT RX +bool DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) { - output.println(" "); - output.println(" === DShot Signal Info === "); + DShotRMT *instance = static_cast(user_data); - // Current DShot mode - output.printf("Current Mode: DSHOT%d\n", - _mode == DSHOT150 ? 150 : - _mode == DSHOT300 ? 300 : - _mode == DSHOT600 ? 600 : - _mode == DSHOT1200 ? 1200 : 0); - - output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO"); - - // Packet Info - output.printf("Current Packet: "); - - // Print bit by bit - for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) + // ISR check for valid data + if (edata && edata->num_symbols >= GCR_BITS_PER_FRAME && edata->num_symbols <= GCR_BITS_PER_FRAME) { - if ((_parsed_packet >> i) & 1) + // Direct decoding + uint16_t erpm = instance->_decodeDShotFrame(edata->received_symbols); + + if (erpm != DSHOT_NULL_PACKET) { - output.print("1"); - } - else - { - output.print("0"); + // Atomic writes - thread-safe + instance->_last_erpm_atomic = erpm; + instance->_telemetry_ready_flag_atomic = true; } } - output.printf("\n"); - output.printf("Current Value: %u\n", _packet.throttle_value); -} - -// Print CPU information -void DShotRMT::printCpuInfo(Stream &output) const -{ - output.println(" "); - output.println(" === CPU Info === "); - output.printf("Chip Model: %s\n", ESP.getChipModel()); - output.printf("Chip Revision: %d\n", ESP.getChipRevision()); - output.printf("CPU Freq = %lu MHz\n", ESP.getCpuFreqMHz()); - output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz()); - output.printf("APB Freq = %lu Hz\n", getApbFrequency()); + return false; } diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 31cad2d..954d354 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -10,12 +10,12 @@ #include #include -#include #include #include #include +#include -// DShot Protocol Constants +// DShot Protocol Constants & Types static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; static constexpr auto DSHOT_THROTTLE_MIN = 48; static constexpr auto DSHOT_THROTTLE_MAX = 2047; @@ -32,7 +32,7 @@ typedef enum DSHOT1200 } dshot_mode_t; -// DShot Packet +// DShot Packet Structure typedef struct { uint16_t throttle_value : 11; @@ -40,14 +40,14 @@ typedef struct uint16_t checksum : 4; } dshot_packet_t; -// DShot Timings +// DShot Timing Configuration typedef struct { double bit_length_us; double t1h_lenght_us; } dshot_timing_us_t; -// RMT Ticks Configuration +// RMT Timing Configuration typedef struct { uint16_t ticks_per_bit; @@ -57,7 +57,7 @@ typedef struct uint16_t t0l_ticks; } rmt_ticks_t; -// Unified DShot result structure +// Unified DShot Result Structure typedef struct { bool success; @@ -66,46 +66,46 @@ typedef struct uint16_t motor_rpm; } dshot_result_t; -// Naming convention +// Command Type Alias typedef dshotCommands_e dshot_commands_t; -// --- HELPERS --- +// Helper Functions void printDShotResult(dshot_result_t &result, Stream &output = Serial); // +// DShotRMT Main Class class DShotRMT { public: - // Constructor with GPIO enum + // Constructors & Destructor explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false); - - // Constructor with pin number DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional); - - // Destructor for "better" code ~DShotRMT(); + // Public Core Functions // Initialize the RMT module and DShot config dshot_result_t begin(); - + // Send throttle value (48-2047) dshot_result_t sendThrottle(uint16_t throttle); - + // Send DShot command (0-47) dshot_result_t sendCommand(uint16_t command); - - // --- GETTERS --- + + // Get telemetry data (bidirectional mode only) + dshot_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); + + // Public Getter Functions gpio_num_t getGPIO() const { return _gpio; } uint16_t getDShotPacket() const { return _parsed_packet; } bool is_bidirectional() const { return _is_bidirectional; } dshot_mode_t getMode() const { return _mode; } - dshot_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); - // --- INFO --- + // Public Info & Debug Functions void printDShotInfo(Stream &output = Serial) const; void printCpuInfo(Stream &output = Serial) const; - - // --- DEPRECATED METHODS --- + + // Deprecated Methods [[deprecated("Use sendThrottle() instead")]] bool setThrottle(uint16_t throttle) { @@ -128,67 +128,30 @@ public: } private: - // --- CONFIG --- - gpio_num_t _gpio; - dshot_mode_t _mode; - bool _is_bidirectional; - uint32_t _frame_timer_us; - rmt_ticks_t _rmt_ticks; - const dshot_timing_us_t &_dshot_timing; - uint16_t _last_throttle; - - // --- TIMING & PACKET VARIABLES --- - uint64_t _last_transmission_time_us; - uint16_t _parsed_packet; - dshot_packet_t _packet; - uint8_t _bitPositions[DSHOT_BITS_PER_FRAME]; - uint16_t _level0; - uint16_t _level1; - - // --- RMT HARDWARE HANDLES --- - rmt_channel_handle_t _rmt_tx_channel; - rmt_channel_handle_t _rmt_rx_channel; - rmt_encoder_handle_t _dshot_encoder; - - // --- RMT CONFIG STRUCTURES --- - rmt_tx_channel_config_t _tx_channel_config; - rmt_rx_channel_config_t _rx_channel_config; - rmt_transmit_config_t _transmit_config; - rmt_receive_config_t _receive_config; - - // --- INITS --- - dshot_result_t _initTXChannel(); - dshot_result_t _initRXChannel(); - dshot_result_t _initDShotEncoder(); - - // --- PACKET MANAGEMENT --- - dshot_packet_t _buildDShotPacket(const uint16_t &value); - uint16_t _parseDShotPacket(const dshot_packet_t &packet); - uint16_t _calculateCRC(const uint16_t data); - void _preCalculateBitPositions(); - - // --- FRAME PROCESSING --- - dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); - uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); - - // --- TIMING CONTROL --- - bool IRAM_ATTR _timer_signal(); - bool _timer_reset(); - - // -- CALLBACKS --- - rmt_rx_event_callbacks_t _rx_event_callbacks; - volatile uint16_t _last_erpm_atomic; - volatile bool _telemetry_ready_flag; - static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); - - // --- DSHOT DEFAULTS --- - static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111; - - // --- CONSTANTS & ERROR MESSAGES --- + // Configuration Constants static constexpr bool DSHOT_OK = 0; static constexpr bool DSHOT_ERROR = 1; + + static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000; + static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111; + static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111; + static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; + static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution + static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond + static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; + static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2; + static constexpr auto const DSHOT_PADDING_US = 3; + static constexpr auto const RMT_BUFFER_SYMBOLS = 64; + static constexpr auto const RMT_QUEUE_DEPTH = 1; + static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame + static constexpr auto const POLE_PAIRS_MIN = 1; + static constexpr auto const MAGNETS_PER_POLE_PAIR = 2; + static constexpr auto const NO_DSHOT_TELEMETRY = 0; + static constexpr auto const DSHOT_PULSE_MIN = 3000; // 3us minimum pulse + static constexpr auto const DSHOT_PULSE_MAX = 60000; // 60us maximum pulse + static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111; + // Error Messages static constexpr char const *NONE = ""; static constexpr char const *UNKNOWN_ERROR = "Unknown Error!"; static constexpr char const *INIT_SUCCESS = "SignalGeneratorRMT initialized successfully"; @@ -210,26 +173,61 @@ private: static constexpr char const *TELEMETRY_FAILED = "No valid Telemetric Frame received!"; static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!"; static constexpr char const *TIMING_CORRECTION = "Timing correction!"; + + // Core Configuration Variables + gpio_num_t _gpio; + dshot_mode_t _mode; + bool _is_bidirectional; + const dshot_timing_us_t &_dshot_timing; + uint32_t _frame_timer_us; - // Configuration Constants - static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000; - static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111; - static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111; - static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; - static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution - static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond, based on the RMT resolution in MHz - static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; - static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2; // Never reached - static constexpr auto const DSHOT_PADDING_US = 3; - static constexpr auto const RMT_BUFFER_SYMBOLS = 64; - static constexpr auto const RMT_QUEUE_DEPTH = 1; - static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC) - static constexpr auto const POLE_PAIRS_MIN = 1; - static constexpr auto const MAGNETS_PER_POLE_PAIR = 2; - static constexpr auto const NO_DSHOT_TELEMETRY = 0; + // Timing & Packet Variables + rmt_ticks_t _rmt_ticks; + uint16_t _last_throttle; + uint64_t _last_transmission_time_us; + uint16_t _parsed_packet; + dshot_packet_t _packet; + uint8_t _bitPositions[DSHOT_BITS_PER_FRAME]; + uint16_t _level0; + uint16_t _level1; + + // RMT Hardware Handles + rmt_channel_handle_t _rmt_tx_channel; + rmt_channel_handle_t _rmt_rx_channel; + rmt_encoder_handle_t _dshot_encoder; - // Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us. - // The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes. - static constexpr auto const DSHOT_PULSE_MIN = 3000; - static constexpr auto const DSHOT_PULSE_MAX = 60000; + // RMT Configuration Structures + rmt_tx_channel_config_t _tx_channel_config; + rmt_rx_channel_config_t _rx_channel_config; + rmt_transmit_config_t _transmit_config; + rmt_receive_config_t _receive_config; + + // Bidirectional / Telemetry Variables + rmt_rx_event_callbacks_t _rx_event_callbacks; + std::atomic _last_erpm_atomic; + std::atomic _telemetry_ready_flag_atomic; + + // Private Initialization Functions + dshot_result_t _initTXChannel(); + dshot_result_t _initRXChannel(); + dshot_result_t _initDShotEncoder(); + + // Private Packet Management Functions + dshot_packet_t _buildDShotPacket(const uint16_t &value); + uint16_t _parseDShotPacket(const dshot_packet_t &packet); + uint16_t _calculateCRC(const uint16_t data); + void _configureRMTTiming(); + void _preCalculateBitPositions(); + + // Private Frame Processing Functions + dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); + bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); + uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); + + // Private Timing Control Functions + bool IRAM_ATTR _timer_signal(); + bool _timer_reset(); + + // Static Callback Functions + static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); }; diff --git a/src/web_content.h b/src/web_content.h deleted file mode 100644 index 65bbe01..0000000 --- a/src/web_content.h +++ /dev/null @@ -1,361 +0,0 @@ -/** - * @file web_content.h - * @brief DShotRMT_Control Website content with Arming Switch - * @author Wastl Kraus - * @date 2025-09-09 - * @license MIT - */ - -#pragma once - -// Web Site Content -const char index_html[] PROGMEM = R"rawliteral( - - - - - - - DShotRMT Web Client - - - - -

DShotRMT Web Client

-
- -
-
- ARMING SWITCH - -
-
- DISARMED -
-
- ⚠️ Disabled when disarmed! -
-
- - -
-
0
- -
- -
- RPM: -- -
-
- - - - - - -)rawliteral"; \ No newline at end of file From 6cd84f9685aa3569da0bcd1d30eae728d1335413 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Mon, 15 Sep 2025 11:58:56 +0200 Subject: [PATCH 4/6] Cosmetics --- examples/web_client/web_client.ino | 353 +------------------------- examples/web_control/web_control.ino | 353 +------------------------- src/DShotRMT.cpp | 10 +- src/DShotRMT.h | 6 +- src/web_content.h | 360 +++++++++++++++++++++++++++ 5 files changed, 370 insertions(+), 712 deletions(-) create mode 100644 src/web_content.h diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index e4a3e2f..3bf66f7 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -62,358 +63,6 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp bool connectToWiFi(); void printWiFiStatus(); -// Web Site Content -const char index_html[] PROGMEM = R"rawliteral( - - - - - - - DShotRMT Web Client - - - - -

DShotRMT Web Client

-
- -
-
- ARMING SWITCH - -
-
- DISARMED -
-
- ⚠️ Disabled when disarmed! -
-
- - -
-
0
- -
- -
- RPM: -- -
-
- - - - - - -)rawliteral"; - // void setup() { diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index 2b66c8b..5db9332 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -58,358 +59,6 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len); void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); void setArmingStatus(bool armed); -// Web Site Content -const char index_html[] PROGMEM = R"rawliteral( - - - - - - - DShotRMT Web Client - - - - -

DShotRMT Web Client

-
- -
-
- ARMING SWITCH - -
-
- DISARMED -
-
- ⚠️ Disabled when disarmed! -
-
- - -
-
0
- -
- -
- RPM: -- -
-
- - - - - - -)rawliteral"; - // void setup() { diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index aef9490..de34797 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -286,7 +286,7 @@ dshot_result_t DShotRMT::_initTXChannel() dshot_result_t DShotRMT::_initRXChannel() { // Direct RMT symbol processing - Performance optimized - _rx_event_callbacks.on_recv_done = _rmt_rx_done_callback; + _rx_event_callbacks.on_recv_done = _on_rx_done; // Config RMT RX _rx_channel_config.gpio_num = _gpio; @@ -325,7 +325,7 @@ dshot_result_t DShotRMT::_initDShotEncoder() return {false, ENCODER_INIT_FAILED}; } - return {true, TX_INIT_SUCCESS}; + return {true, ENCODER_INIT_SUCCESS}; } // Private Packet Management Functions @@ -474,7 +474,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) } // Encode DShot packet into RMT symbol format (placed in IRAM for performance) -bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) +bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) { _parsed_packet = _parseDShotPacket(packet); @@ -541,7 +541,7 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) // Private Timing Control Functions // Check if enough time has passed for next transmission -bool DShotRMT::_timer_signal() +bool IRAM_ATTR DShotRMT::_timer_signal() { uint64_t current_time = esp_timer_get_time(); @@ -561,7 +561,7 @@ bool DShotRMT::_timer_reset() // Static Callback Functions // Callback for RMT RX -bool DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) +bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) { DShotRMT *instance = static_cast(user_data); diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 954d354..ad356b0 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -221,13 +221,13 @@ private: // Private Frame Processing Functions dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); + bool _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); // Private Timing Control Functions - bool IRAM_ATTR _timer_signal(); + bool _timer_signal(); bool _timer_reset(); // Static Callback Functions - static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); + static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); }; diff --git a/src/web_content.h b/src/web_content.h new file mode 100644 index 0000000..5a1bf28 --- /dev/null +++ b/src/web_content.h @@ -0,0 +1,360 @@ +/** + * @file web_content.h + * @brief DShot signal generation using ESP32 RMT with bidirectional support + * @author Wastl Kraus + * @date 2025-09-13 + * @license MIT + */ + +#pragma onve + +// Web Site Content +static constexpr char index_html[] = R"rawliteral( + + + + + + + DShotRMT Web Client + + + + +

DShotRMT Web Client

+
+ +
+
+ ARMING SWITCH + +
+
+ DISARMED +
+
+ ⚠️ Disabled when disarmed! +
+
+ + +
+
0
+ +
+ +
+ RPM: -- +
+
+ + + + + +)rawliteral"; From f0ff887473317540af57f2aff03f6e3b464fd5df Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Mon, 15 Sep 2025 14:40:47 +0200 Subject: [PATCH 5/6] More pause between frames Give the ESC more time for compatibility and more cosmetics. Added default config for VS Code --- .gitignore | 9 - .vscode/arduino.json | 7 + .vscode/c_cpp_properties.json | 899 +++++++++++++++++++ .vscode/settings.json | 8 + README.md | 102 +-- examples/command_manager/command_manager.ino | 7 - src/DShotCommandManager.h | 1 - src/DShotRMT.cpp | 16 +- src/DShotRMT.h | 30 +- src/web_content.h | 2 +- 10 files changed, 944 insertions(+), 137 deletions(-) create mode 100644 .vscode/arduino.json create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index b2a6323..2b2b912 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -.vs -.vscode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - # Local History for Visual Studio Code .history/ diff --git a/.vscode/arduino.json b/.vscode/arduino.json new file mode 100644 index 0000000..b6596e1 --- /dev/null +++ b/.vscode/arduino.json @@ -0,0 +1,7 @@ +{ + "configuration": "JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=all,ZigbeeMode=default", + "board": "esp32:esp32:esp32", + "sketch": "examples/dshot300/dshot300.ino", + "output": "build", + "port": "/dev/ttyUSB0" +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..8add28d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,899 @@ +{ + "version": 4, + "configurations": [ + { + "name": "ESP32", + "compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++", + "compilerArgs": [ + "-MMD", + "-w", + "-Werror=return-type", + "-iprefix" + ], + "intelliSenseMode": "linux-gcc-x64", + "includePath": [ + "~/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/**", + "~/.arduino15/packages/esp32/hardware/esp32/3.3.0/variants/esp32/**", + "~/.arduino15/packages/esp32/hardware/esp32/3.3.0/libraries/**", + "~/.arduino15/packages/esp32/hardware/esp32/3.3.0/**", + "~/.arduino15/packages/esp32/tools/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/xtensa-esp-elf/esp32/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/backward/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include-fixed/**", + "~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/**", + "~/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/include/**", + "~/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/include/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/", + "~/Arduino/libraries/**", + "${workspaceFolder}/**" + ], + "forcedInclude": [ + "~/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/Arduino.h" + ], + "cStandard": "c17", + "cppStandard": "c++20", + "defines": [ + "F_CPU=240000000L", + "ESP32", + "ARDUINO=10607", + "ARDUINO_ESP32_DEV", + "ARDUINO_ARCH_ESP32", + "ARDUINO_BOARD=\"ESP32_DEV\"", + "ARDUINO_VARIANT=\"esp32\"", + "ARDUINO_PARTITION_default", + "ARDUINO_HOST_OS=\"linux\"", + "ARDUINO_FQBN=\"esp32:esp32:esp32:JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=none,ZigbeeMode=default\"", + "ESP32=ESP32", + "CORE_DEBUG_LEVEL=0", + "ARDUINO_RUNNING_CORE=1", + "ARDUINO_EVENT_RUNNING_CORE=1", + "ARDUINO_USB_CDC_ON_BOOT=0", + "__DBL_MIN_EXP__=(-1021)", + "__XCHAL_HAVE_FP=1", + "__cpp_nontype_template_parameter_auto=201606L", + "__UINT_LEAST16_MAX__=0xffff", + "__ATOMIC_ACQUIRE=2", + "__FLT_MIN__=1.1754943508222875e-38F", + "__GCC_IEC_559_COMPLEX=0", + "__XCHAL_HAVE_PREDICTED_BRANCHES=0", + "__cpp_aggregate_nsdmi=201304L", + "__UINT_LEAST8_TYPE__=unsigned char", + "__INTMAX_C(c)=c ## LL", + "__XCHAL_HAVE_ADDX=1", + "__CHAR_BIT__=8", + "__XCHAL_DCACHE_LINESIZE=16", + "__XTENSA_MARCH_EARLIEST=260003", + "__XCHAL_DCACHE_LINEWIDTH=4", + "__UINT8_MAX__=0xff", + "__WINT_MAX__=0xffffffffU", + "__FLT32_MIN_EXP__=(-125)", + "__cpp_static_assert=201411L", + "__ORDER_LITTLE_ENDIAN__=1234", + "__SIZE_MAX__=0xffffffffU", + "__WCHAR_MAX__=0xffff", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1", + "__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)", + "__GCC_ATOMIC_CHAR_LOCK_FREE=2", + "__GCC_IEC_559=0", + "__FLT32X_DECIMAL_DIG__=17", + "__FLT_EVAL_METHOD__=0", + "__cpp_binary_literals=201304L", + "__FLT64_DECIMAL_DIG__=17", + "__cpp_noexcept_function_type=201510L", + "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2", + "__cpp_variadic_templates=200704L", + "__UINT_FAST64_MAX__=0xffffffffffffffffULL", + "__SIG_ATOMIC_TYPE__=int", + "__DBL_MIN_10_EXP__=(-307)", + "__FINITE_MATH_ONLY__=0", + "__cpp_variable_templates=201304L", + "__XCHAL_HAVE_L32R=1", + "__FLT32X_MAX_EXP__=1024", + "__GNUC_PATCHLEVEL__=0", + "__FLT32_HAS_DENORM__=1", + "__UINT_FAST8_MAX__=0xffffffffU", + "__cpp_rvalue_reference=200610L", + "__XCHAL_HAVE_LOOPS=1", + "__cpp_nested_namespace_definitions=201411L", + "__XCHAL_DEBUGLEVEL=6", + "__INT8_C(c)=c", + "__XCHAL_HAVE_DFP_RECIP=0", + "__INT_LEAST8_WIDTH__=8", + "__cpp_variadic_using=201611L", + "__UINT_LEAST64_MAX__=0xffffffffffffffffULL", + "__INT_LEAST8_MAX__=0x7f", + "__cpp_attributes=200809L", + "__cpp_capture_star_this=201603L", + "__SHRT_MAX__=0x7fff", + "__LDBL_MAX__=1.7976931348623157e+308L", + "__cpp_if_constexpr=201606L", + "__XCHAL_ICACHE_LINESIZE=16", + "__LDBL_IS_IEC_60559__=1", + "__UINT_LEAST8_MAX__=0xff", + "__GCC_ATOMIC_BOOL_LOCK_FREE=2", + "__UINTMAX_TYPE__=long long unsigned int", + "__cpp_nsdmi=200809L", + "__FLT_EVAL_METHOD_TS_18661_3__=0", + "__UINT32_MAX__=0xffffffffUL", + "__GXX_EXPERIMENTAL_CXX0X__=1", + "__LDBL_MAX_EXP__=1024", + "__WINT_MIN__=0U", + "__FLT32X_IS_IEC_60559__=1", + "__XCHAL_HAVE_THREADPTR=1", + "__INT_LEAST16_WIDTH__=16", + "__SCHAR_MAX__=0x7f", + "__WCHAR_MIN__=0", + "__XCHAL_ICACHE_LINEWIDTH=4", + "__INT64_C(c)=c ## LL", + "__GCC_ATOMIC_POINTER_LOCK_FREE=2", + "__ATOMIC_SEQ_CST=5", + "__SIZEOF_INT__=4", + "__FLT32X_MANT_DIG__=53", + "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2", + "__cpp_aligned_new=201606L", + "__XCHAL_HAVE_FP_RECIP=1", + "__FLT32_MAX_10_EXP__=38", + "__STDC_HOSTED__=1", + "__XCHAL_HAVE_MUL32=1", + "__XTENSA_EL__=1", + "__cpp_decltype_auto=201304L", + "__DBL_DIG__=15", + "__XCHAL_HAVE_MUL16=1", + "__FLT_EPSILON__=1.1920928955078125e-7F", + "__GXX_WEAK__=1", + "__SHRT_WIDTH__=16", + "__FLT32_IS_IEC_60559__=1", + "__LDBL_MIN__=2.2250738585072014e-308L", + "__DBL_IS_IEC_60559__=1", + "__cpp_threadsafe_static_init=200806L", + "__cpp_enumerator_attributes=201411L", + "__XCHAL_HAVE_MMU=0", + "__FLT32X_HAS_INFINITY__=1", + "__INT32_MAX__=0x7fffffffL", + "__XCHAL_HAVE_DIV32=1", + "__INT_WIDTH__=32", + "__XTENSA_MARCH_LATEST=260003", + "__DECIMAL_DIG__=17", + "__FLT64_EPSILON__=2.2204460492503131e-16F64", + "__INT16_MAX__=0x7fff", + "__FLT64_MIN_EXP__=(-1021)", + "__XCHAL_DCACHE_SIZE=0", + "__LDBL_HAS_QUIET_NAN__=1", + "__cpp_return_type_deduction=201304L", + "__XCHAL_HAVE_BOOLEANS=1", + "__FLT64_MANT_DIG__=53", + "__XTHAL_ABI_WINDOWED=0", + "__GNUC__=14", + "__GXX_RTTI=1", + "__FLT_HAS_DENORM__=1", + "__SIZEOF_LONG_DOUBLE__=8", + "__XCHAL_HAVE_CONST16=0", + "__BIGGEST_ALIGNMENT__=16", + "__STDC_UTF_16__=1", + "__FLT64_MAX_10_EXP__=308", + "__cpp_delegating_constructors=200604L", + "__DBL_MAX__=double(1.7976931348623157e+308L)", + "__cpp_raw_strings=200710L", + "__INT_FAST32_MAX__=0x7fffffff", + "__DBL_HAS_INFINITY__=1", + "__cpp_deduction_guides=201703L", + "__HAVE_SPECULATION_SAFE_VALUE=1", + "__cpp_fold_expressions=201603L", + "__INTPTR_WIDTH__=32", + "__UINT_LEAST32_MAX__=0xffffffffUL", + "__FLT32X_HAS_DENORM__=1", + "__INT_FAST16_TYPE__=int", + "__XCHAL_HAVE_RELEASE_SYNC=1", + "__LDBL_HAS_DENORM__=1", + "__cplusplus=201703L", + "__cpp_ref_qualifiers=200710L", + "__INT_LEAST32_MAX__=0x7fffffffL", + "__DEPRECATED=1", + "__cpp_rvalue_references=200610L", + "__DBL_MAX_EXP__=1024", + "__WCHAR_WIDTH__=16", + "__FLT32_MAX__=3.4028234663852886e+38F32", + "__GCC_ATOMIC_LONG_LOCK_FREE=2", + "__PTRDIFF_MAX__=0x7fffffff", + "__FLT32_HAS_QUIET_NAN__=1", + "__GNUG__=14", + "__LONG_LONG_MAX__=0x7fffffffffffffffLL", + "__SIZEOF_SIZE_T__=4", + "__SIZEOF_WINT_T__=4", + "__FLT32X_DIG__=15", + "__LONG_LONG_WIDTH__=64", + "__cpp_initializer_lists=200806L", + "__FLT32_MAX_EXP__=128", + "__XCHAL_HAVE_MINMAX=1", + "__cpp_hex_float=201603L", + "__XCHAL_NUM_IBREAK=2", + "__GXX_ABI_VERSION=1019", + "__FLT_MIN_EXP__=(-125)", + "__cpp_lambdas=200907L", + "__INT_FAST64_TYPE__=long long int", + "__FP_FAST_FMAF=1", + "__FLT64_DENORM_MIN__=4.9406564584124654e-324F64", + "__DBL_MIN__=double(2.2250738585072014e-308L)", + "__SIZEOF_POINTER__=4", + "__DBL_HAS_QUIET_NAN__=1", + "__FLT32X_EPSILON__=2.2204460492503131e-16F32x", + "__XSHAL_HAVE_TEXT_SECTION_LITERALS=1", + "__FLT64_MIN_10_EXP__=(-307)", + "__REGISTER_PREFIX__", + "__UINT16_MAX__=0xffff", + "__XSHAL_USE_ABSOLUTE_LITERALS=0", + "__LDBL_HAS_INFINITY__=1", + "__FLT32_MIN__=1.1754943508222875e-38F32", + "__UINT8_TYPE__=unsigned char", + "__FLT_DIG__=6", + "__NO_INLINE__=1", + "__DEC_EVAL_METHOD__=2", + "__FLT_MANT_DIG__=24", + "__LDBL_DECIMAL_DIG__=17", + "__VERSION__=\"14.2.0\"", + "__UINT64_C(c)=c ## ULL", + "__XCHAL_NUM_AREGS=64", + "__cpp_unicode_characters=201411L", + "__XCHAL_HAVE_XEA3=0", + "__GCC_ATOMIC_INT_LOCK_FREE=2", + "__XCHAL_HAVE_DENSITY=1", + "__FLT32_MANT_DIG__=24", + "__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__", + "__XCHAL_HAVE_CLAMPS=0", + "__XCHAL_HAVE_DFP_RSQRT=0", + "__cpp_aggregate_bases=201603L", + "__XCHAL_HAVE_NSA=1", + "__XCHAL_HAVE_WINDOWED=1", + "__SCHAR_WIDTH__=8", + "__INT32_C(c)=c ## L", + "__ORDER_PDP_ENDIAN__=3412", + "__INT_FAST32_TYPE__=int", + "__UINT_LEAST16_TYPE__=short unsigned int", + "__DBL_HAS_DENORM__=1", + "__XCHAL_HAVE_DEBUG=1", + "__cpp_rtti=199711L", + "__SIZE_TYPE__=unsigned int", + "__UINT64_MAX__=0xffffffffffffffffULL", + "__FLT_IS_IEC_60559__=1", + "__GNUC_WIDE_EXECUTION_CHARSET_NAME=\"UTF-16LE\"", + "__INT8_TYPE__=signed char", + "__cpp_digit_separators=201309L", + "__ELF__=1", + "__XSHAL_ABI=0", + "__xtensa__=1", + "__FLT_RADIX__=2", + "__INT_LEAST16_TYPE__=short int", + "__LDBL_EPSILON__=2.2204460492503131e-16L", + "__UINTMAX_C(c)=c ## ULL", + "__FLT32X_MIN__=2.2250738585072014e-308F32x", + "__XCHAL_HAVE_DFP_SQRT=0", + "__SIG_ATOMIC_MAX__=0x7fffffff", + "__XCHAL_HAVE_MAC16=1", + "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2", + "__USER_LABEL_PREFIX__", + "__SIZEOF_PTRDIFF_T__=4", + "__XCHAL_MMU_MIN_PTE_PAGE_SIZE=1", + "__XCHAL_DCACHE_IS_WRITEBACK=0", + "__SIZEOF_LONG__=4", + "__LDBL_DIG__=15", + "__FLT64_IS_IEC_60559__=1", + "__XCHAL_MAX_INSTRUCTION_SIZE=3", + "__FLT32X_MIN_EXP__=(-1021)", + "__INT_FAST16_MAX__=0x7fffffff", + "__GCC_CONSTRUCTIVE_SIZE=32", + "__FLT64_DIG__=15", + "__UINT_FAST32_MAX__=0xffffffffU", + "__UINT_LEAST64_TYPE__=long long unsigned int", + "__FLT_HAS_QUIET_NAN__=1", + "__FLT_MAX_10_EXP__=38", + "__FLT_HAS_INFINITY__=1", + "__GNUC_EXECUTION_CHARSET_NAME=\"UTF-8\"", + "__CHAR_UNSIGNED__=1", + "__cpp_unicode_literals=200710L", + "__UINT_FAST16_TYPE__=unsigned int", + "__INT_FAST32_WIDTH__=32", + "__CHAR16_TYPE__=short unsigned int", + "__PRAGMA_REDEFINE_EXTNAME=1", + "__SIZE_WIDTH__=32", + "__INT_LEAST16_MAX__=0x7fff", + "__INT64_MAX__=0x7fffffffffffffffLL", + "__FLT32_DENORM_MIN__=1.4012984643248171e-45F32", + "__SIG_ATOMIC_WIDTH__=32", + "__INT_LEAST64_TYPE__=long long int", + "__INT16_TYPE__=short int", + "__INT_LEAST8_TYPE__=signed char", + "__cpp_structured_bindings=201606L", + "__INT_FAST8_MAX__=0x7fffffff", + "__INTPTR_MAX__=0x7fffffff", + "__cpp_sized_deallocation=201309L", + "__cpp_guaranteed_copy_elision=201606L", + "__FLT64_HAS_QUIET_NAN__=1", + "__FLT32_MIN_10_EXP__=(-37)", + "__EXCEPTIONS=1", + "__UINT16_C(c)=c", + "__XCHAL_M_STAGE=3", + "__PTRDIFF_WIDTH__=32", + "__LDBL_MANT_DIG__=53", + "__cpp_range_based_for=201603L", + "__FLT64_HAS_INFINITY__=1", + "__STDCPP_DEFAULT_NEW_ALIGNMENT__=8", + "__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)", + "__XCHAL_ICACHE_SIZE=0", + "__cpp_nontype_template_args=201411L", + "__INTPTR_TYPE__=int", + "__UINT16_TYPE__=short unsigned int", + "__WCHAR_TYPE__=short unsigned int", + "__XCHAL_HAVE_DEPBITS=0", + "__SIZEOF_FLOAT__=4", + "__UINTPTR_MAX__=0xffffffffU", + "__INT_FAST64_WIDTH__=64", + "__cpp_decltype=200707L", + "__FLT32_DECIMAL_DIG__=9", + "__INT_FAST64_MAX__=0x7fffffffffffffffLL", + "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1", + "__FLT_NORM_MAX__=3.4028234663852886e+38F", + "__XCHAL_HAVE_DFP=0", + "__FLT32_HAS_INFINITY__=1", + "__UINT_FAST64_TYPE__=long long unsigned int", + "__cpp_inline_variables=201606L", + "__INT_MAX__=0x7fffffff", + "__XCHAL_HAVE_EXCLUSIVE=0", + "__STDCPP_THREADS__=1", + "__INT64_TYPE__=long long int", + "__XCHAL_HAVE_DFP_DIV=0", + "__FLT_MAX_EXP__=128", + "__XCHAL_INST_FETCH_WIDTH=4", + "__DBL_MANT_DIG__=53", + "__cpp_inheriting_constructors=201511L", + "__INT_LEAST64_MAX__=0x7fffffffffffffffLL", + "__FP_FAST_FMAF32=1", + "__WINT_TYPE__=unsigned int", + "__UINT_LEAST32_TYPE__=long unsigned int", + "__SIZEOF_SHORT__=2", + "__FLT32_NORM_MAX__=3.4028234663852886e+38F32", + "__LDBL_MIN_EXP__=(-1021)", + "__XCHAL_HAVE_S32C1I=1", + "__FLT64_MAX__=1.7976931348623157e+308F64", + "__WINT_WIDTH__=32", + "__cpp_template_auto=201606L", + "__INT_LEAST64_WIDTH__=64", + "__FLT32X_MAX_10_EXP__=308", + "__cpp_namespace_attributes=201411L", + "__WCHAR_UNSIGNED__=1", + "__LDBL_MAX_10_EXP__=308", + "__ATOMIC_RELAXED=0", + "__DBL_EPSILON__=double(2.2204460492503131e-16L)", + "__XCHAL_HAVE_SEXT=1", + "__INT_LEAST32_TYPE__=long int", + "__XTENSA_WINDOWED_ABI__=1", + "__UINT8_C(c)=c", + "__FLT64_MAX_EXP__=1024", + "__SIZEOF_WCHAR_T__=2", + "__XCHAL_HAVE_FP_POSTINC=1", + "__FLT64_NORM_MAX__=1.7976931348623157e+308F64", + "__INTMAX_MAX__=0x7fffffffffffffffLL", + "__INT_FAST8_TYPE__=int", + "__XCHAL_HAVE_MUL32_HIGH=1", + "__GNUC_STDC_INLINE__=1", + "__FLT64_HAS_DENORM__=1", + "__FLT32_EPSILON__=1.1920928955078125e-7F32", + "__DBL_DECIMAL_DIG__=17", + "__STDC_UTF_32__=1", + "__XCHAL_HAVE_FP_DIV=1", + "__INT_FAST8_WIDTH__=32", + "__FLT32X_MAX__=1.7976931348623157e+308F32x", + "__DBL_NORM_MAX__=double(1.7976931348623157e+308L)", + "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__", + "__GCC_DESTRUCTIVE_SIZE=32", + "__XTENSA__=1", + "__INTMAX_WIDTH__=64", + "__ORDER_BIG_ENDIAN__=4321", + "__XTHAL_ABI_CALL0=1", + "__cpp_runtime_arrays=198712L", + "__FLT32_DIG__=6", + "__UINT64_TYPE__=long long unsigned int", + "__UINT32_C(c)=c ## UL", + "__cpp_alias_templates=200704L", + "__FLT_DENORM_MIN__=1.4012984643248171e-45F", + "__INT8_MAX__=0x7f", + "__LONG_WIDTH__=32", + "__UINT_FAST32_TYPE__=unsigned int", + "__FLT32X_NORM_MAX__=1.7976931348623157e+308F32x", + "__CHAR32_TYPE__=long unsigned int", + "__FLT_MAX__=3.4028234663852886e+38F", + "__cpp_constexpr=201603L", + "__XCHAL_HAVE_FP_RSQRT=1", + "__INT32_TYPE__=long int", + "__SIZEOF_DOUBLE__=8", + "__cpp_exceptions=199711L", + "__FLT_MIN_10_EXP__=(-37)", + "__FLT64_MIN__=2.2250738585072014e-308F64", + "__INT_LEAST32_WIDTH__=32", + "__INTMAX_TYPE__=long long int", + "__XCHAL_HAVE_ABS=1", + "__FLT32X_HAS_QUIET_NAN__=1", + "__ATOMIC_CONSUME=1", + "__XCHAL_NUM_DBREAK=2", + "__XCHAL_HAVE_WIDE_BRANCHES=0", + "__GNUC_MINOR__=2", + "__INT_FAST16_WIDTH__=32", + "__UINTMAX_MAX__=0xffffffffffffffffULL", + "__FLT32X_DENORM_MIN__=4.9406564584124654e-324F32x", + "__cpp_template_template_args=201611L", + "__DBL_MAX_10_EXP__=308", + "__LDBL_DENORM_MIN__=4.9406564584124654e-324L", + "__INT16_C(c)=c", + "__STDC__=1", + "__PTRDIFF_TYPE__=int", + "__LONG_MAX__=0x7fffffffL", + "__XCHAL_HAVE_FP_SQRT=1", + "__UINT32_TYPE__=long unsigned int", + "__FLT32X_MIN_10_EXP__=(-307)", + "__UINTPTR_TYPE__=unsigned int", + "__LDBL_MIN_10_EXP__=(-307)", + "__cpp_generic_lambdas=201304L", + "__SIZEOF_LONG_LONG__=8", + "__cpp_USER_defined_literals=200809L", + "__GCC_ATOMIC_LLONG_LOCK_FREE=1", + "__FLT_DECIMAL_DIG__=9", + "__UINT_FAST16_MAX__=0xffffffffU", + "__LDBL_NORM_MAX__=1.7976931348623157e+308L", + "__GCC_ATOMIC_SHORT_LOCK_FREE=2", + "__XCHAL_HAVE_BE=0", + "__UINT_FAST8_TYPE__=unsigned int", + "__cpp_init_captures=201304L", + "__ATOMIC_ACQ_REL=4", + "__ATOMIC_RELEASE=3", + "RMT_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB", + "USBCON" + ] + }, + { + "name": "Arduino", + "compilerPath": "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++", + "compilerArgs": [ + "-MMD", + "-w", + "-Werror=return-type", + "-iprefix" + ], + "intelliSenseMode": "gcc-x64", + "includePath": [ + "/home/derdoktor667/Github/DShotRMT/examples/dshot300", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/qio_qspi/include", + "/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32", + "/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/variants/esp32", + "/home/derdoktor667/Arduino/libraries/DShotRMT/src", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/xtensa-esp-elf/esp32", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/backward", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include-fixed", + "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include" + ], + "forcedInclude": [ + "/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/Arduino.h" + ], + "cStandard": "c11", + "cppStandard": "c++11", + "defines": [ + "F_CPU=240000000L", + "ARDUINO=10607", + "ARDUINO_ESP32_DEV", + "ARDUINO_ARCH_ESP32", + "ARDUINO_BOARD=\"ESP32_DEV\"", + "ARDUINO_VARIANT=\"esp32\"", + "ARDUINO_PARTITION_default", + "ARDUINO_HOST_OS=\"linux\"", + "ARDUINO_FQBN=\"esp32:esp32:esp32:JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=all,ZigbeeMode=default\"", + "ESP32=ESP32", + "CORE_DEBUG_LEVEL=0", + "ARDUINO_RUNNING_CORE=1", + "ARDUINO_EVENT_RUNNING_CORE=1", + "ARDUINO_USB_CDC_ON_BOOT=0", + "__DBL_MIN_EXP__=(-1021)", + "__XCHAL_HAVE_FP=1", + "__cpp_nontype_template_parameter_auto=201606L", + "__UINT_LEAST16_MAX__=0xffff", + "__ATOMIC_ACQUIRE=2", + "__FLT_MIN__=1.1754943508222875e-38F", + "__GCC_IEC_559_COMPLEX=0", + "__XCHAL_HAVE_PREDICTED_BRANCHES=0", + "__cpp_aggregate_nsdmi=201304L", + "__UINT_LEAST8_TYPE__=unsigned char", + "__INTMAX_C(c)=c ## LL", + "__XCHAL_HAVE_ADDX=1", + "__CHAR_BIT__=8", + "__XCHAL_DCACHE_LINESIZE=16", + "__XTENSA_MARCH_EARLIEST=260003", + "__XCHAL_DCACHE_LINEWIDTH=4", + "__UINT8_MAX__=0xff", + "__WINT_MAX__=0xffffffffU", + "__FLT32_MIN_EXP__=(-125)", + "__cpp_static_assert=201411L", + "__ORDER_LITTLE_ENDIAN__=1234", + "__SIZE_MAX__=0xffffffffU", + "__WCHAR_MAX__=0xffff", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1", + "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1", + "__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)", + "__GCC_ATOMIC_CHAR_LOCK_FREE=2", + "__GCC_IEC_559=0", + "__FLT32X_DECIMAL_DIG__=17", + "__FLT_EVAL_METHOD__=0", + "__cpp_binary_literals=201304L", + "__FLT64_DECIMAL_DIG__=17", + "__cpp_noexcept_function_type=201510L", + "__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2", + "__cpp_variadic_templates=200704L", + "__UINT_FAST64_MAX__=0xffffffffffffffffULL", + "__SIG_ATOMIC_TYPE__=int", + "__DBL_MIN_10_EXP__=(-307)", + "__FINITE_MATH_ONLY__=0", + "__cpp_variable_templates=201304L", + "__XCHAL_HAVE_L32R=1", + "__FLT32X_MAX_EXP__=1024", + "__GNUC_PATCHLEVEL__=0", + "__FLT32_HAS_DENORM__=1", + "__UINT_FAST8_MAX__=0xffffffffU", + "__cpp_rvalue_reference=200610L", + "__XCHAL_HAVE_LOOPS=1", + "__cpp_nested_namespace_definitions=201411L", + "__XCHAL_DEBUGLEVEL=6", + "__INT8_C(c)=c", + "__XCHAL_HAVE_DFP_RECIP=0", + "__INT_LEAST8_WIDTH__=8", + "__cpp_variadic_using=201611L", + "__UINT_LEAST64_MAX__=0xffffffffffffffffULL", + "__INT_LEAST8_MAX__=0x7f", + "__cpp_attributes=200809L", + "__cpp_capture_star_this=201603L", + "__SHRT_MAX__=0x7fff", + "__LDBL_MAX__=1.7976931348623157e+308L", + "__cpp_if_constexpr=201606L", + "__XCHAL_ICACHE_LINESIZE=16", + "__LDBL_IS_IEC_60559__=1", + "__UINT_LEAST8_MAX__=0xff", + "__GCC_ATOMIC_BOOL_LOCK_FREE=2", + "__UINTMAX_TYPE__=long long unsigned int", + "__cpp_nsdmi=200809L", + "__FLT_EVAL_METHOD_TS_18661_3__=0", + "__UINT32_MAX__=0xffffffffUL", + "__GXX_EXPERIMENTAL_CXX0X__=1", + "__LDBL_MAX_EXP__=1024", + "__WINT_MIN__=0U", + "__FLT32X_IS_IEC_60559__=1", + "__XCHAL_HAVE_THREADPTR=1", + "__INT_LEAST16_WIDTH__=16", + "__SCHAR_MAX__=0x7f", + "__WCHAR_MIN__=0", + "__XCHAL_ICACHE_LINEWIDTH=4", + "__INT64_C(c)=c ## LL", + "__GCC_ATOMIC_POINTER_LOCK_FREE=2", + "__ATOMIC_SEQ_CST=5", + "__SIZEOF_INT__=4", + "__FLT32X_MANT_DIG__=53", + "__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2", + "__cpp_aligned_new=201606L", + "__XCHAL_HAVE_FP_RECIP=1", + "__FLT32_MAX_10_EXP__=38", + "__STDC_HOSTED__=1", + "__XCHAL_HAVE_MUL32=1", + "__XTENSA_EL__=1", + "__cpp_decltype_auto=201304L", + "__DBL_DIG__=15", + "__XCHAL_HAVE_MUL16=1", + "__FLT_EPSILON__=1.1920928955078125e-7F", + "__GXX_WEAK__=1", + "__SHRT_WIDTH__=16", + "__FLT32_IS_IEC_60559__=1", + "__LDBL_MIN__=2.2250738585072014e-308L", + "__DBL_IS_IEC_60559__=1", + "__cpp_threadsafe_static_init=200806L", + "__cpp_enumerator_attributes=201411L", + "__XCHAL_HAVE_MMU=0", + "__FLT32X_HAS_INFINITY__=1", + "__INT32_MAX__=0x7fffffffL", + "__XCHAL_HAVE_DIV32=1", + "__INT_WIDTH__=32", + "__XTENSA_MARCH_LATEST=260003", + "__DECIMAL_DIG__=17", + "__FLT64_EPSILON__=2.2204460492503131e-16F64", + "__INT16_MAX__=0x7fff", + "__FLT64_MIN_EXP__=(-1021)", + "__XCHAL_DCACHE_SIZE=0", + "__LDBL_HAS_QUIET_NAN__=1", + "__cpp_return_type_deduction=201304L", + "__XCHAL_HAVE_BOOLEANS=1", + "__FLT64_MANT_DIG__=53", + "__XTHAL_ABI_WINDOWED=0", + "__GNUC__=14", + "__GXX_RTTI=1", + "__FLT_HAS_DENORM__=1", + "__SIZEOF_LONG_DOUBLE__=8", + "__XCHAL_HAVE_CONST16=0", + "__BIGGEST_ALIGNMENT__=16", + "__STDC_UTF_16__=1", + "__FLT64_MAX_10_EXP__=308", + "__cpp_delegating_constructors=200604L", + "__DBL_MAX__=double(1.7976931348623157e+308L)", + "__cpp_raw_strings=200710L", + "__INT_FAST32_MAX__=0x7fffffff", + "__DBL_HAS_INFINITY__=1", + "__cpp_deduction_guides=201703L", + "__HAVE_SPECULATION_SAFE_VALUE=1", + "__cpp_fold_expressions=201603L", + "__INTPTR_WIDTH__=32", + "__UINT_LEAST32_MAX__=0xffffffffUL", + "__FLT32X_HAS_DENORM__=1", + "__INT_FAST16_TYPE__=int", + "__XCHAL_HAVE_RELEASE_SYNC=1", + "__LDBL_HAS_DENORM__=1", + "__cplusplus=201703L", + "__cpp_ref_qualifiers=200710L", + "__INT_LEAST32_MAX__=0x7fffffffL", + "__DEPRECATED=1", + "__cpp_rvalue_references=200610L", + "__DBL_MAX_EXP__=1024", + "__WCHAR_WIDTH__=16", + "__FLT32_MAX__=3.4028234663852886e+38F32", + "__GCC_ATOMIC_LONG_LOCK_FREE=2", + "__PTRDIFF_MAX__=0x7fffffff", + "__FLT32_HAS_QUIET_NAN__=1", + "__GNUG__=14", + "__LONG_LONG_MAX__=0x7fffffffffffffffLL", + "__SIZEOF_SIZE_T__=4", + "__SIZEOF_WINT_T__=4", + "__FLT32X_DIG__=15", + "__LONG_LONG_WIDTH__=64", + "__cpp_initializer_lists=200806L", + "__FLT32_MAX_EXP__=128", + "__XCHAL_HAVE_MINMAX=1", + "__cpp_hex_float=201603L", + "__XCHAL_NUM_IBREAK=2", + "__GXX_ABI_VERSION=1019", + "__FLT_MIN_EXP__=(-125)", + "__cpp_lambdas=200907L", + "__INT_FAST64_TYPE__=long long int", + "__FP_FAST_FMAF=1", + "__FLT64_DENORM_MIN__=4.9406564584124654e-324F64", + "__DBL_MIN__=double(2.2250738585072014e-308L)", + "__SIZEOF_POINTER__=4", + "__DBL_HAS_QUIET_NAN__=1", + "__FLT32X_EPSILON__=2.2204460492503131e-16F32x", + "__XSHAL_HAVE_TEXT_SECTION_LITERALS=1", + "__FLT64_MIN_10_EXP__=(-307)", + "__REGISTER_PREFIX__", + "__UINT16_MAX__=0xffff", + "__XSHAL_USE_ABSOLUTE_LITERALS=0", + "__LDBL_HAS_INFINITY__=1", + "__FLT32_MIN__=1.1754943508222875e-38F32", + "__UINT8_TYPE__=unsigned char", + "__FLT_DIG__=6", + "__NO_INLINE__=1", + "__DEC_EVAL_METHOD__=2", + "__FLT_MANT_DIG__=24", + "__LDBL_DECIMAL_DIG__=17", + "__VERSION__=\"14.2.0\"", + "__UINT64_C(c)=c ## ULL", + "__XCHAL_NUM_AREGS=64", + "__cpp_unicode_characters=201411L", + "__XCHAL_HAVE_XEA3=0", + "__GCC_ATOMIC_INT_LOCK_FREE=2", + "__XCHAL_HAVE_DENSITY=1", + "__FLT32_MANT_DIG__=24", + "__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__", + "__XCHAL_HAVE_CLAMPS=0", + "__XCHAL_HAVE_DFP_RSQRT=0", + "__cpp_aggregate_bases=201603L", + "__XCHAL_HAVE_NSA=1", + "__XCHAL_HAVE_WINDOWED=1", + "__SCHAR_WIDTH__=8", + "__INT32_C(c)=c ## L", + "__ORDER_PDP_ENDIAN__=3412", + "__INT_FAST32_TYPE__=int", + "__UINT_LEAST16_TYPE__=short unsigned int", + "__DBL_HAS_DENORM__=1", + "__XCHAL_HAVE_DEBUG=1", + "__cpp_rtti=199711L", + "__SIZE_TYPE__=unsigned int", + "__UINT64_MAX__=0xffffffffffffffffULL", + "__FLT_IS_IEC_60559__=1", + "__GNUC_WIDE_EXECUTION_CHARSET_NAME=\"UTF-16LE\"", + "__INT8_TYPE__=signed char", + "__cpp_digit_separators=201309L", + "__ELF__=1", + "__XSHAL_ABI=0", + "__xtensa__=1", + "__FLT_RADIX__=2", + "__INT_LEAST16_TYPE__=short int", + "__LDBL_EPSILON__=2.2204460492503131e-16L", + "__UINTMAX_C(c)=c ## ULL", + "__FLT32X_MIN__=2.2250738585072014e-308F32x", + "__XCHAL_HAVE_DFP_SQRT=0", + "__SIG_ATOMIC_MAX__=0x7fffffff", + "__XCHAL_HAVE_MAC16=1", + "__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2", + "__USER_LABEL_PREFIX__", + "__SIZEOF_PTRDIFF_T__=4", + "__XCHAL_MMU_MIN_PTE_PAGE_SIZE=1", + "__XCHAL_DCACHE_IS_WRITEBACK=0", + "__SIZEOF_LONG__=4", + "__LDBL_DIG__=15", + "__FLT64_IS_IEC_60559__=1", + "__XCHAL_MAX_INSTRUCTION_SIZE=3", + "__FLT32X_MIN_EXP__=(-1021)", + "__INT_FAST16_MAX__=0x7fffffff", + "__GCC_CONSTRUCTIVE_SIZE=32", + "__FLT64_DIG__=15", + "__UINT_FAST32_MAX__=0xffffffffU", + "__UINT_LEAST64_TYPE__=long long unsigned int", + "__FLT_HAS_QUIET_NAN__=1", + "__FLT_MAX_10_EXP__=38", + "__FLT_HAS_INFINITY__=1", + "__GNUC_EXECUTION_CHARSET_NAME=\"UTF-8\"", + "__CHAR_UNSIGNED__=1", + "__cpp_unicode_literals=200710L", + "__UINT_FAST16_TYPE__=unsigned int", + "__INT_FAST32_WIDTH__=32", + "__CHAR16_TYPE__=short unsigned int", + "__PRAGMA_REDEFINE_EXTNAME=1", + "__SIZE_WIDTH__=32", + "__INT_LEAST16_MAX__=0x7fff", + "__INT64_MAX__=0x7fffffffffffffffLL", + "__FLT32_DENORM_MIN__=1.4012984643248171e-45F32", + "__SIG_ATOMIC_WIDTH__=32", + "__INT_LEAST64_TYPE__=long long int", + "__INT16_TYPE__=short int", + "__INT_LEAST8_TYPE__=signed char", + "__cpp_structured_bindings=201606L", + "__INT_FAST8_MAX__=0x7fffffff", + "__INTPTR_MAX__=0x7fffffff", + "__cpp_sized_deallocation=201309L", + "__cpp_guaranteed_copy_elision=201606L", + "__FLT64_HAS_QUIET_NAN__=1", + "__FLT32_MIN_10_EXP__=(-37)", + "__EXCEPTIONS=1", + "__UINT16_C(c)=c", + "__XCHAL_M_STAGE=3", + "__PTRDIFF_WIDTH__=32", + "__LDBL_MANT_DIG__=53", + "__cpp_range_based_for=201603L", + "__FLT64_HAS_INFINITY__=1", + "__STDCPP_DEFAULT_NEW_ALIGNMENT__=8", + "__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)", + "__XCHAL_ICACHE_SIZE=0", + "__cpp_nontype_template_args=201411L", + "__INTPTR_TYPE__=int", + "__UINT16_TYPE__=short unsigned int", + "__WCHAR_TYPE__=short unsigned int", + "__XCHAL_HAVE_DEPBITS=0", + "__SIZEOF_FLOAT__=4", + "__UINTPTR_MAX__=0xffffffffU", + "__INT_FAST64_WIDTH__=64", + "__cpp_decltype=200707L", + "__FLT32_DECIMAL_DIG__=9", + "__INT_FAST64_MAX__=0x7fffffffffffffffLL", + "__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1", + "__FLT_NORM_MAX__=3.4028234663852886e+38F", + "__XCHAL_HAVE_DFP=0", + "__FLT32_HAS_INFINITY__=1", + "__UINT_FAST64_TYPE__=long long unsigned int", + "__cpp_inline_variables=201606L", + "__INT_MAX__=0x7fffffff", + "__XCHAL_HAVE_EXCLUSIVE=0", + "__STDCPP_THREADS__=1", + "__INT64_TYPE__=long long int", + "__XCHAL_HAVE_DFP_DIV=0", + "__FLT_MAX_EXP__=128", + "__XCHAL_INST_FETCH_WIDTH=4", + "__DBL_MANT_DIG__=53", + "__cpp_inheriting_constructors=201511L", + "__INT_LEAST64_MAX__=0x7fffffffffffffffLL", + "__FP_FAST_FMAF32=1", + "__WINT_TYPE__=unsigned int", + "__UINT_LEAST32_TYPE__=long unsigned int", + "__SIZEOF_SHORT__=2", + "__FLT32_NORM_MAX__=3.4028234663852886e+38F32", + "__LDBL_MIN_EXP__=(-1021)", + "__XCHAL_HAVE_S32C1I=1", + "__FLT64_MAX__=1.7976931348623157e+308F64", + "__WINT_WIDTH__=32", + "__cpp_template_auto=201606L", + "__INT_LEAST64_WIDTH__=64", + "__FLT32X_MAX_10_EXP__=308", + "__cpp_namespace_attributes=201411L", + "__WCHAR_UNSIGNED__=1", + "__LDBL_MAX_10_EXP__=308", + "__ATOMIC_RELAXED=0", + "__DBL_EPSILON__=double(2.2204460492503131e-16L)", + "__XCHAL_HAVE_SEXT=1", + "__INT_LEAST32_TYPE__=long int", + "__XTENSA_WINDOWED_ABI__=1", + "__UINT8_C(c)=c", + "__FLT64_MAX_EXP__=1024", + "__SIZEOF_WCHAR_T__=2", + "__XCHAL_HAVE_FP_POSTINC=1", + "__FLT64_NORM_MAX__=1.7976931348623157e+308F64", + "__INTMAX_MAX__=0x7fffffffffffffffLL", + "__INT_FAST8_TYPE__=int", + "__XCHAL_HAVE_MUL32_HIGH=1", + "__GNUC_STDC_INLINE__=1", + "__FLT64_HAS_DENORM__=1", + "__FLT32_EPSILON__=1.1920928955078125e-7F32", + "__DBL_DECIMAL_DIG__=17", + "__STDC_UTF_32__=1", + "__XCHAL_HAVE_FP_DIV=1", + "__INT_FAST8_WIDTH__=32", + "__FLT32X_MAX__=1.7976931348623157e+308F32x", + "__DBL_NORM_MAX__=double(1.7976931348623157e+308L)", + "__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__", + "__GCC_DESTRUCTIVE_SIZE=32", + "__XTENSA__=1", + "__INTMAX_WIDTH__=64", + "__ORDER_BIG_ENDIAN__=4321", + "__XTHAL_ABI_CALL0=1", + "__cpp_runtime_arrays=198712L", + "__FLT32_DIG__=6", + "__UINT64_TYPE__=long long unsigned int", + "__UINT32_C(c)=c ## UL", + "__cpp_alias_templates=200704L", + "__FLT_DENORM_MIN__=1.4012984643248171e-45F", + "__INT8_MAX__=0x7f", + "__LONG_WIDTH__=32", + "__UINT_FAST32_TYPE__=unsigned int", + "__FLT32X_NORM_MAX__=1.7976931348623157e+308F32x", + "__CHAR32_TYPE__=long unsigned int", + "__FLT_MAX__=3.4028234663852886e+38F", + "__cpp_constexpr=201603L", + "__XCHAL_HAVE_FP_RSQRT=1", + "__INT32_TYPE__=long int", + "__SIZEOF_DOUBLE__=8", + "__cpp_exceptions=199711L", + "__FLT_MIN_10_EXP__=(-37)", + "__FLT64_MIN__=2.2250738585072014e-308F64", + "__INT_LEAST32_WIDTH__=32", + "__INTMAX_TYPE__=long long int", + "__XCHAL_HAVE_ABS=1", + "__FLT32X_HAS_QUIET_NAN__=1", + "__ATOMIC_CONSUME=1", + "__XCHAL_NUM_DBREAK=2", + "__XCHAL_HAVE_WIDE_BRANCHES=0", + "__GNUC_MINOR__=2", + "__INT_FAST16_WIDTH__=32", + "__UINTMAX_MAX__=0xffffffffffffffffULL", + "__FLT32X_DENORM_MIN__=4.9406564584124654e-324F32x", + "__cpp_template_template_args=201611L", + "__DBL_MAX_10_EXP__=308", + "__LDBL_DENORM_MIN__=4.9406564584124654e-324L", + "__INT16_C(c)=c", + "__STDC__=1", + "__PTRDIFF_TYPE__=int", + "__LONG_MAX__=0x7fffffffL", + "__XCHAL_HAVE_FP_SQRT=1", + "__UINT32_TYPE__=long unsigned int", + "__FLT32X_MIN_10_EXP__=(-307)", + "__UINTPTR_TYPE__=unsigned int", + "__LDBL_MIN_10_EXP__=(-307)", + "__cpp_generic_lambdas=201304L", + "__SIZEOF_LONG_LONG__=8", + "__cpp_user_defined_literals=200809L", + "__GCC_ATOMIC_LLONG_LOCK_FREE=1", + "__FLT_DECIMAL_DIG__=9", + "__UINT_FAST16_MAX__=0xffffffffU", + "__LDBL_NORM_MAX__=1.7976931348623157e+308L", + "__GCC_ATOMIC_SHORT_LOCK_FREE=2", + "__XCHAL_HAVE_BE=0", + "__UINT_FAST8_TYPE__=unsigned int", + "__cpp_init_captures=201304L", + "__ATOMIC_ACQ_REL=4", + "__ATOMIC_RELEASE=3", + "USBCON" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b961c02 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "C_Cpp.errorSquiggles": "enabled", + "C_Cpp.default.compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++", + "files.associations": { + ".fantomasignore": "ignore", + "string_view": "cpp" + } +} \ No newline at end of file diff --git a/README.md b/README.md index fd1382b..44bb558 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The library requires these additional libraries for full functionality: **Core DShotRMT (always required):** - ESP32 Arduino Core -**Web Interface Example (dshot300.ino):** +**Web Interface Example (web_control.ino / web_client.ino):** ```ini lib_deps = https://github.com/derdoktor667/DShotRMT @@ -101,92 +101,6 @@ void loop() { } } ``` - -### Web Control Interface - -```cpp -#include -#include -#include - -DShotRMT motor(17, DSHOT300, false); -AsyncWebServer server(80); -AsyncWebSocket ws("/ws"); - -void setup() { - // Initialize motor - motor.begin(); - - // Create WiFi Access Point - WiFi.softAP("DShotRMT Control", "12345678"); - - // Setup web interface - ws.onEvent(onWsEvent); - server.addHandler(&ws); - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send_P(200, "text/html", index_html); - }); - server.begin(); - - // Access at http://10.10.10.1 -} - -void loop() { - // Handle WebSocket communication and motor control - ws.cleanupClients(); -} -``` - -### Advanced Command Management - -```cpp -#include -#include - -DShotRMT motor(17, DSHOT300, false); -DShotCommandManager cmdManager(motor); - -void setup() { - motor.begin(); - cmdManager.begin(); -} - -void loop() { - // High-level ESC control - cmdManager.stopMotor(); - cmdManager.activateBeacon(1); - cmdManager.setSpinDirection(false); - cmdManager.executeInitSequence(); -} -``` - -### Bidirectional DShot (RPM Telemetry) - -```cpp -#include - -// Enable bidirectional mode for telemetry -DShotRMT motor(17, DSHOT300, true); - -void setup() { - Serial.begin(115200); - motor.begin(); -} - -void loop() { - // Send throttle - motor.sendThrottle(1000); - - // Get telemetry data - dshot_telemetry_result_t telemetry = motor.getTelemetry(14); // 14 magnets - if (telemetry.success) { - Serial.printf("eRPM: %u, Motor RPM: %u\n", - telemetry.erpm, - telemetry.motor_rpm); - } -} -``` - --- ## 🌐 Web Control Interface @@ -228,7 +142,7 @@ Make sure you are using these libraries for [ESPAsyncWebServer](https://github.c The library includes comprehensive examples: -### 1. Basic DShot Control with Web Interface (`dshot300.ino`) +### 1. Basic DShot Control with Web Interface (`web_control.ino`) - **Web Control Interface:** Modern responsive web UI accessible at `http://10.10.10.1` - **WiFi Access Point:** Creates hotspot "DShotRMT Control" for wireless control - **Safety Features:** Arming/disarming system with motor safety lockout @@ -257,8 +171,8 @@ Interactive ESC control with full menu system: 0 - Emergency Stop Advanced Commands: - cmd - Send DShot command (0-47) - throttle - Set throttle (48-2047) + cmd - Send DShot command (0 - 47) + throttle - Set throttle (48 - 2047) repeat cmd count - Repeat command ``` @@ -274,6 +188,8 @@ Advanced Commands: | 300 | 300 kbit/s | 2.50 | 1.25 | 3.33 | ~53.28 | | 600 | 600 kbit/s | 1.25 | 0.625 | 1.67 | ~26.72 | +For DShot, T1H length is always double T0H length. + ### GPIO Configuration ```cpp // Using GPIO number @@ -294,14 +210,14 @@ DShotRMT motor(17, DSHOT300, true); | Command | Value | Description | Usage | |---------|-------|-------------|-------| | MOTOR_STOP | 0 | Stop motor | Always available | -| BEACON1 - 5 | 1 - 5 | Motor beeping | Motor identification | +| BEACON 1 - 5 | 1 - 5 | Motor beeping | Motor identification | | ESC_INFO | 6 | Request ESC info | Get ESC version/settings | | SPIN_DIRECTION_1/2 | 7 - 8 | Set spin direction | Motor configuration | | 3D_MODE_OFF/ON | 9 - 10 | 3D mode control | Bidirectional flight | | SAVE_SETTINGS | 12 | Save to EEPROM | Permanent configuration | | EXTENDED_TELEMETRY_ENABLE/DISABLE | 13 - 14 | Telemetry control | Data transmission | | SPIN_DIRECTION_NORMAL/REVERSED | 20 - 21 | Spin direction | Alias commands | -| LED0-3_ON/OFF | 22 - 29 | LED control | BLHeli32 only | +| LED 0-3_ON/OFF | 22 - 29 | LED control | BLHeli32 only | | AUDIO_STREAM_MODE | 30 | Audio mode toggle | KISS ESCs | | SILENT_MODE | 31 | Silent mode toggle | KISS ESCs | @@ -341,7 +257,7 @@ The library utilizes the ESP32's RMT (Remote Control) peripheral for precise sig ### Advantages - **Hardware Timing:** No CPU intervention during transmission - **Concurrent Operation:** Multiple channels can run simultaneously -- **DMA Support:** Efficient memory-to-peripheral transfers +- **DMA Support:** Efficient, automatic memory-to-peripheral transfers --- diff --git a/examples/command_manager/command_manager.ino b/examples/command_manager/command_manager.ino index 13b2228..4bc7bdd 100644 --- a/examples/command_manager/command_manager.ino +++ b/examples/command_manager/command_manager.ino @@ -308,13 +308,6 @@ void printSystemStatus() USB_SERIAL.println("\n=== System Status ==="); USB_SERIAL.printf("Current throttle: %u\n", throttle_now); USB_SERIAL.printf("Continuous mode: %s\n", throttle_now > 0 ? "ACTIVE" : "INACTIVE"); - USB_SERIAL.printf("GPIO Pin: %d\n", motor01.getGPIO()); - USB_SERIAL.printf("DShot Mode: DSHOT%d\n", - motor01.getMode() == DSHOT150 ? 150 : motor01.getMode() == DSHOT300 ? 300 - : motor01.getMode() == DSHOT600 ? 600 - : motor01.getMode() == DSHOT1200 ? 1200 - : 0); - USB_SERIAL.printf("Bidirectional: %s\n", motor01.is_bidirectional() ? "YES" : "NO"); USB_SERIAL.printf("Free heap: %u bytes\n", ESP.getFreeHeap()); USB_SERIAL.printf("Uptime: %lu seconds\n", millis() / 1000); } diff --git a/src/DShotCommandManager.h b/src/DShotCommandManager.h index 1c7dcff..9b05fc3 100644 --- a/src/DShotCommandManager.h +++ b/src/DShotCommandManager.h @@ -10,7 +10,6 @@ #include #include -#include // Command item typedef struct diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index de34797..0113eae 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -10,7 +10,7 @@ // Static Data & Helper Functions // Timing parameters for each DShot mode -// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks} +// Format: {bit_length_us, t1h_length_us} static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { {0.00, 0.00}, {6.67, 5.00}, @@ -18,7 +18,7 @@ static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { {1.67, 1.25}, {0.83, 0.67}}; -// Helper function to print DShot results +// Helper function to print DShot results and telemetry void printDShotResult(dshot_result_t &result, Stream &output) { output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); @@ -60,7 +60,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _telemetry_ready_flag_atomic(false) { // Configure RMT ticks for DShot timings - _configureRMTTiming(); + _preCalculateRMTTiming(); } // Constructor using pin number @@ -365,7 +365,7 @@ uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) } // Calculate CRC -uint16_t DShotRMT::_calculateCRC(const uint16_t data) +uint16_t DShotRMT::_calculateCRC(const uint16_t &data) { // DShot CRC uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK; @@ -380,14 +380,14 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data) } // Configure RMT ticks for DShot timings -void DShotRMT::_configureRMTTiming() +void DShotRMT::_preCalculateRMTTiming() { // Convert DShot timings (us) to RMT ticks - _rmt_ticks.ticks_per_bit = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); + _rmt_ticks.bit_length_ticks = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); _rmt_ticks.t1h_ticks = static_cast(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US); _rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0 - _rmt_ticks.t1l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t1h_ticks; - _rmt_ticks.t0l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t0h_ticks; + _rmt_ticks.t1l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t1h_ticks; + _rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks; // Pause between frames is frame time in us, some padding and about 30 us is added by hardware _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; diff --git a/src/DShotRMT.h b/src/DShotRMT.h index ad356b0..6bb665b 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -23,7 +23,7 @@ static constexpr auto DSHOT_BITS_PER_FRAME = 16; static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; // DShot Modes -typedef enum +typedef enum dshot_mode { DSHOT_OFF, DSHOT150, @@ -33,7 +33,7 @@ typedef enum } dshot_mode_t; // DShot Packet Structure -typedef struct +typedef struct dshot_packet { uint16_t throttle_value : 11; bool telemetric_request : 1; @@ -41,16 +41,16 @@ typedef struct } dshot_packet_t; // DShot Timing Configuration -typedef struct +typedef struct dshot_timing { double bit_length_us; double t1h_lenght_us; } dshot_timing_us_t; // RMT Timing Configuration -typedef struct +typedef struct rmt_ticks { - uint16_t ticks_per_bit; + uint16_t bit_length_ticks; uint16_t t1h_ticks; uint16_t t1l_ticks; uint16_t t0h_ticks; @@ -58,7 +58,7 @@ typedef struct } rmt_ticks_t; // Unified DShot Result Structure -typedef struct +typedef struct dshot_result { bool success; const char *msg; @@ -80,27 +80,22 @@ public: // Constructors & Destructor explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false); DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional); + ~DShotRMT(); // Public Core Functions // Initialize the RMT module and DShot config dshot_result_t begin(); - // Send throttle value (48-2047) + // Send throttle value (48 - 2047) dshot_result_t sendThrottle(uint16_t throttle); - // Send DShot command (0-47) + // Send DShot command (0 - 47) dshot_result_t sendCommand(uint16_t command); // Get telemetry data (bidirectional mode only) dshot_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); - // Public Getter Functions - gpio_num_t getGPIO() const { return _gpio; } - uint16_t getDShotPacket() const { return _parsed_packet; } - bool is_bidirectional() const { return _is_bidirectional; } - dshot_mode_t getMode() const { return _mode; } - // Public Info & Debug Functions void printDShotInfo(Stream &output = Serial) const; void printCpuInfo(Stream &output = Serial) const; @@ -140,7 +135,7 @@ private: static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2; - static constexpr auto const DSHOT_PADDING_US = 3; + static constexpr auto const DSHOT_PADDING_US = 20; // Add to pause between frames for compatibility static constexpr auto const RMT_BUFFER_SYMBOLS = 64; static constexpr auto const RMT_QUEUE_DEPTH = 1; static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame @@ -160,7 +155,6 @@ private: static constexpr char const *TX_INIT_FAILED = "TX RMT channel init failed!"; static constexpr char const *RX_INIT_SUCCESS = "RX RMT channel initialized successfully"; static constexpr char const *RX_INIT_FAILED = "RX RMT channel init failed!"; - static constexpr char const *RX_BUFFER_FAILED = "RX RMT buffer init failed!"; static constexpr char const *ENCODER_INIT_SUCCESS = "RMT encoder initialized successfully"; static constexpr char const *ENCODER_INIT_FAILED = "RMT encoder init failed!"; static constexpr char const *TRANSMISSION_SUCCESS = "Transmission successfully"; @@ -215,8 +209,8 @@ private: // Private Packet Management Functions dshot_packet_t _buildDShotPacket(const uint16_t &value); uint16_t _parseDShotPacket(const dshot_packet_t &packet); - uint16_t _calculateCRC(const uint16_t data); - void _configureRMTTiming(); + uint16_t _calculateCRC(const uint16_t &data); + void _preCalculateRMTTiming(); void _preCalculateBitPositions(); // Private Frame Processing Functions diff --git a/src/web_content.h b/src/web_content.h index 5a1bf28..2f7459d 100644 --- a/src/web_content.h +++ b/src/web_content.h @@ -6,7 +6,7 @@ * @license MIT */ -#pragma onve +#pragma once // Web Site Content static constexpr char index_html[] = R"rawliteral( From de6e1bebf9e9e5508f25b666ec1855714111a2f5 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Mon, 15 Sep 2025 15:56:04 +0200 Subject: [PATCH 6/6] Fix RX buffer --- library.properties | 2 +- src/DShotRMT.cpp | 40 +++++++++++++++++++--------------------- src/DShotRMT.h | 13 +++++++------ 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/library.properties b/library.properties index 243274d..753180c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DShotRMT -version=0.7.6 +version=0.8.0 author=Wastl Kraus maintainer=Wastl Kraus license=MIT diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 0113eae..e46a521 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -53,14 +53,14 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _dshot_encoder(nullptr), _tx_channel_config{}, _rx_channel_config{}, - _transmit_config{}, - _receive_config{}, + _rmt_tx_config{}, + _rmt_rx_config{}, _rx_event_callbacks{}, _last_erpm_atomic(0), _telemetry_ready_flag_atomic(false) { // Configure RMT ticks for DShot timings - _preCalculateRMTTiming(); + _preCalculateRMTTicks(); } // Constructor using pin number @@ -226,7 +226,7 @@ void DShotRMT::printDShotInfo(Stream &output) const // Print bit by bit for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) { - if ((_parsed_packet >> i) & 1) + if ((_parsed_packet >> i) & 0b0000000000000001) { output.print("1"); } @@ -264,8 +264,8 @@ dshot_result_t DShotRMT::_initTXChannel() _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; // Config RMT TX - _transmit_config.loop_count = 0; // No automatic loops - real-time calculation - _transmit_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag + _rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation + _rmt_tx_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag // Create RMT TX channel if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) @@ -295,8 +295,8 @@ dshot_result_t DShotRMT::_initRXChannel() _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; // Config RMT RX parameters - _receive_config.signal_range_min_ns = DSHOT_PULSE_MIN; - _receive_config.signal_range_max_ns = DSHOT_PULSE_MAX; + _rmt_rx_config.signal_range_min_ns = DSHOT_PULSE_MIN; + _rmt_rx_config.signal_range_max_ns = DSHOT_PULSE_MAX; // Create RMT RX channel if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) @@ -329,7 +329,7 @@ dshot_result_t DShotRMT::_initDShotEncoder() } // Private Packet Management Functions -// Build a complete DShot packet +// Build a complete DShot packet from a valid value dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) { // Init packet structure @@ -343,10 +343,10 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) } // Build packet - packet.throttle_value = value & 0b0000011111111111; + packet.throttle_value = value & DSHOT_THROTTLE_MAX; packet.telemetric_request = _is_bidirectional ? 1 : 0; - // CRC is calculated over 11bit + // CRC is calculated over 12bit uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; packet.checksum = _calculateCRC(data); @@ -359,9 +359,7 @@ uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) { // Parse DShot frame into "raw" 16 bit value uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request; - uint16_t parsed_packet = (data_and_telemetry << 4) | packet.checksum; - - return parsed_packet; + return (data_and_telemetry << 4) | packet.checksum; } // Calculate CRC @@ -380,7 +378,7 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t &data) } // Configure RMT ticks for DShot timings -void DShotRMT::_preCalculateRMTTiming() +void DShotRMT::_preCalculateRMTTicks() { // Convert DShot timings (us) to RMT ticks _rmt_ticks.bit_length_ticks = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); @@ -390,7 +388,7 @@ void DShotRMT::_preCalculateRMTTiming() _rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks; // Pause between frames is frame time in us, some padding and about 30 us is added by hardware - _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; + _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; // Double frame time for bidirectional mode (includes response time) if (_is_bidirectional) @@ -425,9 +423,9 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); // Performance reasons - rmt_symbol_word_t rx_symbols[DSHOT_BITS_PER_FRAME]; + rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME]; - if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_receive_config) != DSHOT_OK) + if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_rmt_rx_config) != DSHOT_OK) { return {false, RECEIVER_FAILED}; } @@ -453,7 +451,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) } // Perform RMT transmission - if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config) != DSHOT_OK) + if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK) { return {false, TRANSMISSION_FAILED}; } @@ -474,7 +472,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) } // Encode DShot packet into RMT symbol format (placed in IRAM for performance) -bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) +dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) { _parsed_packet = _parseDShotPacket(packet); @@ -491,7 +489,7 @@ bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_sym symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks; } - return DSHOT_OK; + return {true, ENCODING_SUCCESS}; } // Decode DShot telemetry frame from received RMT symbols diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 6bb665b..5c82f64 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -144,7 +144,7 @@ private: static constexpr auto const NO_DSHOT_TELEMETRY = 0; static constexpr auto const DSHOT_PULSE_MIN = 3000; // 3us minimum pulse static constexpr auto const DSHOT_PULSE_MAX = 60000; // 60us maximum pulse - static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111; + static constexpr auto const DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX; // Error Messages static constexpr char const *NONE = ""; @@ -157,6 +157,7 @@ private: static constexpr char const *RX_INIT_FAILED = "RX RMT channel init failed!"; static constexpr char const *ENCODER_INIT_SUCCESS = "RMT encoder initialized successfully"; static constexpr char const *ENCODER_INIT_FAILED = "RMT encoder init failed!"; + static constexpr char const *ENCODING_SUCCESS = "Packet encoded successfully"; static constexpr char const *TRANSMISSION_SUCCESS = "Transmission successfully"; static constexpr char const *TRANSMISSION_FAILED = "Transmission failed!"; static constexpr char const *RECEIVER_FAILED = "RMT receiver failed!"; @@ -173,7 +174,7 @@ private: dshot_mode_t _mode; bool _is_bidirectional; const dshot_timing_us_t &_dshot_timing; - uint32_t _frame_timer_us; + uint64_t _frame_timer_us; // Timing & Packet Variables rmt_ticks_t _rmt_ticks; @@ -193,8 +194,8 @@ private: // RMT Configuration Structures rmt_tx_channel_config_t _tx_channel_config; rmt_rx_channel_config_t _rx_channel_config; - rmt_transmit_config_t _transmit_config; - rmt_receive_config_t _receive_config; + rmt_transmit_config_t _rmt_tx_config; + rmt_receive_config_t _rmt_rx_config; // Bidirectional / Telemetry Variables rmt_rx_event_callbacks_t _rx_event_callbacks; @@ -210,12 +211,12 @@ private: dshot_packet_t _buildDShotPacket(const uint16_t &value); uint16_t _parseDShotPacket(const dshot_packet_t &packet); uint16_t _calculateCRC(const uint16_t &data); - void _preCalculateRMTTiming(); + void _preCalculateRMTTicks(); void _preCalculateBitPositions(); // Private Frame Processing Functions dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - bool _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); + dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); // Private Timing Control Functions