diff --git a/README.md b/README.md index 232a5b7..c3421a6 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ void setup() { motor.begin(); // Print CPU Info - DShotRMT::printCpuInfo(Serial); + printCpuInfo(Serial); Serial.println("Motor initialized. Ramping up to 25% throttle..."); @@ -79,7 +79,7 @@ void loop() { motor.sendThrottlePercent(0); // Print DShot Info - DShotRMT::printDShotInfo(motor, Serial); + printDShotInfo(motor, Serial); } ``` @@ -104,7 +104,7 @@ The `web_control` and `web_client` examples require the following additional lib The main class is `DShotRMT`. Here are the most important methods: -- `DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT)`: Constructor to create a new DShotRMT instance. +- `DShotRMT(gpio_num_t gpio, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT)`: Constructor to create a new DShotRMT instance. - `begin()`: Initializes the DShot RMT channels and encoder. - `sendThrottlePercent(float percent)`: Sends a throttle value as a percentage (0.0-100.0) to the ESC. - `sendThrottle(uint16_t throttle)`: Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command. @@ -115,8 +115,8 @@ The main class is `DShotRMT`. Here are the most important methods: - `setMotorSpinDirection(bool reversed)`: Sets the motor spin direction. `true` for reversed, `false` for normal. - `saveESCSettings()`: Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory. - `printDShotResult(dshot_result_t &result, Stream &output = Serial)`: Prints the result of a DShot operation to the specified output stream. -- `DShotRMT::printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)`: Prints detailed DShot signal information for a given DShotRMT instance. -- `DShotRMT::printCpuInfo(Stream &output = Serial)`: Prints detailed CPU information. +- `printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial)`: Prints detailed DShot signal information for a given DShotRMT instance. +- `printCpuInfo(Stream &output = Serial)`: Prints detailed CPU information. - `setMotorMagnetCount(uint16_t magnet_count)`: Sets the motor magnet count for RPM calculation. - `getMode()`: Gets the current DShot mode. - `isBidirectional()`: Checks if bidirectional DShot is enabled. diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 3d10bef..85c5f26 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -18,7 +18,7 @@ static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27; // static constexpr auto MOTOR01_PIN = 17; // Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200) -static constexpr dshot_mode_t DSHOT_MODE = dshot_mode_t::DSHOT300; +static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) // Note: Bidirectional DShot is currently not officially supported @@ -26,7 +26,7 @@ static constexpr dshot_mode_t DSHOT_MODE = dshot_mode_t::DSHOT300; static constexpr auto IS_BIDIRECTIONAL = false; // Motor magnet count for RPM calculation -static constexpr auto MOTOR01_MAGNET_COUNT = 14; +// static constexpr auto MOTOR01_MAGNET_COUNT = 14; // Creates the motor instance DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); @@ -41,7 +41,7 @@ void setup() motor01.begin(); // Print CPU Info - DShotRMT::printCpuInfo(USB_SERIAL); + printCpuInfo(USB_SERIAL); // printMenu(); @@ -80,7 +80,7 @@ void loop() // Print motor stats every 3 seconds in continuous mode if (continuous_throttle && (esp_timer_get_time() - last_stats_print >= 3000000)) { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); USB_SERIAL.println(" "); @@ -132,7 +132,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous } else if (input == "info") { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); } else if (input == "rpm" && IS_BIDIRECTIONAL) { diff --git a/examples/throttle_percent/throttle_percent.ino b/examples/throttle_percent/throttle_percent.ino index 5fb9000..5903a7b 100644 --- a/examples/throttle_percent/throttle_percent.ino +++ b/examples/throttle_percent/throttle_percent.ino @@ -17,7 +17,7 @@ static constexpr auto USB_SERIAL_BAUD = 115200; static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27; // Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200) -static constexpr dshot_mode_t DSHOT_MODE = dshot_mode_t::DSHOT300; +static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) // Note: Bidirectional DShot is currently not officially supported @@ -44,7 +44,7 @@ void setup() motor01.begin(); // Print CPU Info - motor01.printCpuInfo(); + printCpuInfo(USB_SERIAL); // printMenu(); @@ -98,7 +98,7 @@ void handleSerialInput(const String &input) } else if (input == "info") { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); } else if (input == "rpm" && IS_BIDIRECTIONAL) { diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index 9d1ca57..147a69e 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -39,10 +39,10 @@ static constexpr auto &USB_SERIAL = Serial; static constexpr auto USB_SERIAL_BAUD = 115200; // Motor configuration - Pin number or GPIO_PIN -static constexpr auto MOTOR01_PIN = 17; +static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27; // Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200) -static constexpr dshot_mode_t DSHOT_MODE = dshot_mode_t::DSHOT300; +static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is currently not officially supported due to instability and external hardware requirements. @@ -84,7 +84,7 @@ void setup() USB_SERIAL.println("DShotRMT Web Client Demo Starting..."); motor01.begin(); - motor01.printCpuInfo(); + printCpuInfo(USB_SERIAL); // Connect to WiFi network USB_SERIAL.println("\nConnecting to WiFi network..."); @@ -172,7 +172,7 @@ void loop() // Print motor stats every 3 seconds in continuous mode if ((esp_timer_get_time() - last_serial_update >= 3000000)) { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); // Get Motor RPM if bidirectional and armed if (IS_BIDIRECTIONAL && isArmed) @@ -447,7 +447,7 @@ void handleSerialInput(const String &input) if (input == "info") { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); USB_SERIAL.println(" "); USB_SERIAL.printf("Arming Status: %s\n", isArmed ? "ARMED" : "DISARMED"); return; diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index 6cc773f..d78edb2 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -38,10 +38,10 @@ static constexpr auto &USB_SERIAL = Serial; static constexpr auto USB_SERIAL_BAUD = 115200; // Motor configuration - Pin number or GPIO_PIN -static constexpr auto MOTOR01_PIN = 17; +static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27; // Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200) -static constexpr dshot_mode_t DSHOT_MODE = dshot_mode_t::DSHOT300; +static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is currently not officially supported due to instability and external hardware requirements. @@ -74,7 +74,7 @@ void setup() USB_SERIAL.begin(USB_SERIAL_BAUD); motor01.begin(); - motor01.printCpuInfo(); + printCpuInfo(USB_SERIAL); // Set IP Address WiFi.softAPConfig(local_IP, gateway, subnet); @@ -138,7 +138,7 @@ void loop() // Print motor stats every 3 seconds in continuous mode if ((esp_timer_get_time() - last_serial_update >= 3000000)) { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); USB_SERIAL.println(" "); @@ -254,7 +254,7 @@ void handleSerialInput(const String &input) } if (input == "info") { - DShotRMT::printDShotInfo(motor01, USB_SERIAL); + printDShotInfo(motor01, USB_SERIAL); USB_SERIAL.println(" "); USB_SERIAL.printf("Arming Status: %s\n", isArmed ? "ARMED" : "DISARMED"); return; diff --git a/library.properties b/library.properties index 7e3bc36..6f1aa34 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DShotRMT -version=0.8.7 +version=0.8.8 author=Wastl Kraus maintainer=Wastl Kraus license=MIT diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 75a4faf..91896aa 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -8,33 +8,13 @@ #include -// Constructors & Destructor // Constructor with GPIO number DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count) : _gpio(gpio), _mode(mode), _is_bidirectional(is_bidirectional), _motor_magnet_count(magnet_count), - _dshot_timing(DSHOT_TIMING_US[static_cast(mode)]), - _frame_timer_us(0), - _rmt_ticks{0}, - _last_throttle(dshotCommands_e::DSHOT_CMD_MOTOR_STOP), - _last_transmission_time_us(0), - _last_command_timestamp(0), - _encoded_frame_value(0), - _packet{0}, - _pulse_level(1), // DShot standard: signal is idle-low, so pulses start by going HIGH - _idle_level(0), // DShot standard: signal returns to LOW after the high pulse - _rmt_tx_channel(nullptr), - _rmt_rx_channel(nullptr), - _dshot_encoder(nullptr), - _tx_channel_config{}, - _rx_channel_config{}, - _rmt_tx_config{}, - _rmt_rx_config{}, - _rx_event_callbacks{}, - _last_erpm_atomic(0), - _telemetry_ready_flag_atomic(false) + _dshot_timing(DSHOT_TIMING_US[static_cast(mode)]) { // Pre-calculate timing and bit positions for performance _preCalculateRMTTicks(); @@ -78,7 +58,6 @@ DShotRMT::~DShotRMT() } } -// Public Core Functions // Initialize DShotRMT dshot_result_t DShotRMT::begin() { @@ -149,20 +128,16 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent) } // Send DShot command to ESC -dshot_result_t DShotRMT::sendCommand(uint16_t command) +dshot_result_t DShotRMT::sendCommand(dshotCommands_e command) { - if (command > dshotCommands_e::DSHOT_CMD_MAX) - { - return {false, dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID}; - } - - _packet = _buildDShotPacket(command); + _packet = _buildDShotPacket(static_cast(command)); return _sendDShotFrame(_packet); } + // Send full DShot commands for setup etc // This is a blocking function that uses delayMicroseconds for repetitions. -dshot_result_t DShotRMT::sendCommand(dshotCommands_e dshot_command, uint16_t repeat_count, uint16_t delay_us) +dshot_result_t DShotRMT::_sendCommandInternal(dshotCommands_e dshot_command, uint16_t repeat_count, uint16_t delay_us) { dshot_result_t result = {false, dshot_msg_code_t::DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; @@ -205,7 +180,7 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e dshot_command, uint16_t rep } // Get telemetry data -dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) +dshot_result_t DShotRMT::getTelemetry() { dshot_result_t result = {false, dshot_msg_code_t::DSHOT_TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; @@ -216,7 +191,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) } // Use stored magnet count if parameter is 0 (default) - uint16_t final_magnet_count = (magnet_count == 0) ? _motor_magnet_count : magnet_count; + uint16_t final_magnet_count = _motor_magnet_count; // Check if the callback has set the flag for new data if (_telemetry_ready_flag_atomic) @@ -246,22 +221,19 @@ dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed) // Use command as a yes / no switch dshotCommands_e command = reversed ? dshotCommands_e::DSHOT_CMD_SPIN_DIRECTION_REVERSED : dshotCommands_e::DSHOT_CMD_SPIN_DIRECTION_NORMAL; - return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); + return _sendCommandInternal(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); } -dshot_result_t DShotRMT::getESCInfo() -{ - return sendCommand(dshotCommands_e::DSHOT_CMD_ESC_INFO); -} + // Use with caution dshot_result_t DShotRMT::saveESCSettings() { - return sendCommand(dshotCommands_e::DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); + return _sendCommandInternal(dshotCommands_e::DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US); } // Simple check -bool DShotRMT::_isValidCommand(dshotCommands_e command) +bool DShotRMT::_isValidCommand(dshotCommands_e command) const { return (command >= dshotCommands_e::DSHOT_CMD_MOTOR_STOP && command <= dshotCommands_e::DSHOT_CMD_MAX); } @@ -271,8 +243,8 @@ dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command) { uint64_t start_time = esp_timer_get_time(); - // Execute the command using the DShotRMT instance - dshot_result_t result = sendCommand(command); + _packet = _buildDShotPacket(static_cast(command)); + dshot_result_t result = _sendDShotFrame(_packet); uint64_t end_time = esp_timer_get_time(); _last_command_timestamp = end_time; @@ -350,6 +322,7 @@ dshot_result_t DShotRMT::_initRXChannel() return {true, dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS}; } +// dshot_result_t DShotRMT::_initDShotEncoder() { rmt_bytes_encoder_config_t encoder_config = { @@ -367,8 +340,7 @@ dshot_result_t DShotRMT::_initDShotEncoder() }, .flags = { .msb_first = 1 // DShot is MSB first - } - }; + }}; if (rmt_new_bytes_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) { @@ -379,7 +351,7 @@ dshot_result_t DShotRMT::_initDShotEncoder() } // Private Packet Management Functions -dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) +dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) const { dshot_packet_t packet = {}; @@ -393,14 +365,14 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) return packet; } -uint16_t DShotRMT::_buildDShotFrameValue(const dshot_packet_t &packet) +uint16_t DShotRMT::_buildDShotFrameValue(const dshot_packet_t &packet) const { // Combine throttle, telemetry bit, and CRC into a single 16-bit frame uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request; return (data_and_telemetry << 4) | packet.checksum; } -uint16_t DShotRMT::_calculateCRC(const uint16_t &data) +uint16_t DShotRMT::_calculateCRC(const uint16_t &data) const { // Standard DShot CRC calculation using XOR uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK; @@ -463,7 +435,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) // This function needs to be fast, as it generates the RMT symbols just before sending // Placed in IRAM for high performance, as it's called from an ISR context -uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) +uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) const { uint32_t gcr_value = 0; @@ -503,7 +475,7 @@ uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) } // Timing Control Functions -bool IRAM_ATTR DShotRMT::_isFrameIntervalElapsed() +bool IRAM_ATTR DShotRMT::_isFrameIntervalElapsed() const { // Check if the minimum interval between frames has passed uint64_t current_time = esp_timer_get_time(); @@ -537,47 +509,3 @@ bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const return false; } - -// Public Static Utility Functions -void DShotRMT::printDShotInfo(const DShotRMT &dshot_rmt, Stream &output) -{ - output.println("\n === DShot Signal Info === "); - - uint16_t dshot_mode_val = 0; - switch (dshot_rmt.getMode()) - { - case dshot_mode_t::DSHOT150: - dshot_mode_val = 150; - break; - case dshot_mode_t::DSHOT300: - dshot_mode_val = 300; - break; - case dshot_mode_t::DSHOT600: - dshot_mode_val = 600; - break; - case dshot_mode_t::DSHOT1200: - dshot_mode_val = 1200; - break; - } - output.printf("Current Mode: DSHOT%d\n", dshot_mode_val); - - output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO"); - output.printf("Current Packet: "); - - for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) - { - output.print((dshot_rmt.getEncodedFrameValue() >> i) & 1); - } - - output.printf("\nCurrent Value: %u\n", dshot_rmt.getThrottleValue()); -} - -void DShotRMT::printCpuInfo(Stream &output) -{ - output.println("\n === 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()); -} diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 9505881..26bc6e1 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -9,64 +9,49 @@ #pragma once #include -#include -#include #include #include #include -// Main class for DShot signal generation and reception. -// This class provides an interface to generate DShot signals for Electronic Speed Controllers (ESCs) -// and to receive telemetry data using the ESP32's RMT peripheral. +#include "dshot_definitions.h" +#include + +// DShotRMT Class Definition class DShotRMT { public: // Constructor for DShotRMT with GPIO number. - explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = dshot_mode_t::DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); + explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); // Constructor for DShotRMT with Arduino pin number. DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); - // Destructor for DShotRMT. - // Cleans up RMT channels and encoder resources. + // Destructor ~DShotRMT(); // Public Core Functions // Initializes the DShot RMT channels and encoder. dshot_result_t begin(); - // Sends a DShot throttle value to the ESC. - dshot_result_t sendThrottle(uint16_t throttle); - - // Sends a DShot throttle value as a percentage to the ESC. + // Sends a throttle value as a percentage (0.0-100.0) to the ESC. dshot_result_t sendThrottlePercent(float percent); - // Sends a single DShot command to the ESC. - dshot_result_t sendCommand(uint16_t command); + // Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command. + dshot_result_t sendThrottle(uint16_t throttle); - // Sends a DShot command multiple times with a delay between repetitions. This is a blocking function. - dshot_result_t sendCommand(dshotCommands_e dshot_command, uint16_t repeat_count = DEFAULT_CMD_REPEAT_COUNT, uint16_t delay_us = DEFAULT_CMD_DELAY_US); + // Sends a DShot command (0-47) to the ESC. + dshot_result_t sendCommand(dshotCommands_e command); // Retrieves telemetry data from the ESC. - dshot_result_t getTelemetry(uint16_t magnet_count = 0); + dshot_result_t getTelemetry(); - // Sends a command to the ESC to request ESC information. - dshot_result_t getESCInfo(); - - // Sets the motor spin direction. + // Sets the motor spin direction. 'true' for reversed, 'false' for normal. dshot_result_t setMotorSpinDirection(bool reversed); - // Sends a command to the ESC to save its current settings. - // Use with caution as this writes to ESC's non-volatile memory. + // Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory. dshot_result_t saveESCSettings(); // Public Utility & Info Functions - // Prints detailed DShot signal information for a given DShotRMT instance. - static void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial); - - // Prints detailed CPU information. - static void printCpuInfo(Stream &output = Serial); - // Sets the motor magnet count for RPM calculation. void setMotorMagnetCount(uint16_t magnet_count); @@ -76,41 +61,29 @@ public: // Checks if bidirectional DShot is enabled. bool isBidirectional() const { return _is_bidirectional; } - // Gets the last encoded DShot frame value. + // Gets the encoded frame value. uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } // Gets the last transmitted throttle value. uint16_t getThrottleValue() const { return _packet.throttle_value; } + // Testing return "verbose" messages + const char *getDShotMsg(dshot_result_t &result) const { return (_get_result_code_str(result.result_code)); } + // Deprecated Methods // Deprecated. Use sendThrottle() instead. [[deprecated("Use sendThrottle() instead")]] - bool setThrottle(uint16_t throttle) - { - auto result = sendThrottle(throttle); - return result.success; - } + dshot_result_t sendValue(uint16_t value) { return sendThrottle(value); } // Deprecated. Use sendCommand() instead. [[deprecated("Use sendCommand() instead")]] - bool sendDShotCommand(uint16_t command) - { - auto result = sendCommand(command); - return result.success; - } - - // Deprecated. Use getTelemetry() instead. - [[deprecated("Use getTelemetry() instead")]] - uint32_t getMotorRPM(uint8_t magnet_count) - { - auto result = getTelemetry(magnet_count); - return result.motor_rpm; - } + dshot_result_t sendCommand(uint16_t command) { return sendCommand(static_cast(command)); } private: // --- UTILITY METHODS --- - bool _isValidCommand(dshotCommands_e command); + bool _isValidCommand(dshotCommands_e command) const; dshot_result_t _executeCommand(dshotCommands_e command); + dshot_result_t _sendCommandInternal(dshotCommands_e dshot_command, uint16_t repeat_count, uint16_t delay_us); // Core Configuration Variables gpio_num_t _gpio; @@ -118,33 +91,33 @@ private: bool _is_bidirectional; uint16_t _motor_magnet_count; const dshot_timing_us_t &_dshot_timing; - uint64_t _frame_timer_us; + uint64_t _frame_timer_us = 0; // Timing & Packet Variables - rmt_ticks_t _rmt_ticks; - uint16_t _last_throttle; - uint64_t _last_transmission_time_us; - uint64_t _last_command_timestamp; - uint16_t _encoded_frame_value; - dshot_packet_t _packet; - uint16_t _pulse_level; // DShot protocol: Signal is idle-low, so pulses start by going HIGH. - uint16_t _idle_level; // DShot protocol: Signal returns to LOW after the high pulse. + rmt_ticks_t _rmt_ticks{}; + uint16_t _last_throttle = dshotCommands_e::DSHOT_CMD_MOTOR_STOP; + uint64_t _last_transmission_time_us = 0; + uint64_t _last_command_timestamp = 0; + uint16_t _encoded_frame_value = 0; + dshot_packet_t _packet{}; + uint16_t _pulse_level = 1; // DShot protocol: Signal is idle-low, so pulses start by going HIGH. + uint16_t _idle_level = 0; // DShot protocol: Signal returns to LOW after the high pulse. // RMT Hardware Handles - rmt_channel_handle_t _rmt_tx_channel; - rmt_channel_handle_t _rmt_rx_channel; - rmt_encoder_handle_t _dshot_encoder; + rmt_channel_handle_t _rmt_tx_channel = nullptr; + rmt_channel_handle_t _rmt_rx_channel = nullptr; + rmt_encoder_handle_t _dshot_encoder = nullptr; // RMT Configuration Structures - rmt_tx_channel_config_t _tx_channel_config; - rmt_rx_channel_config_t _rx_channel_config; - rmt_transmit_config_t _rmt_tx_config; - rmt_receive_config_t _rmt_rx_config; + rmt_tx_channel_config_t _tx_channel_config{}; + rmt_rx_channel_config_t _rx_channel_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; - std::atomic _last_erpm_atomic; - std::atomic _telemetry_ready_flag_atomic; + rmt_rx_event_callbacks_t _rx_event_callbacks{}; + std::atomic _last_erpm_atomic{0}; + std::atomic _telemetry_ready_flag_atomic{false}; // Private Initialization Functions dshot_result_t _initTXChannel(); @@ -152,19 +125,78 @@ private: dshot_result_t _initDShotEncoder(); // Private Packet Management Functions - dshot_packet_t _buildDShotPacket(const uint16_t &value); - uint16_t _buildDShotFrameValue(const dshot_packet_t &packet); - uint16_t _calculateCRC(const uint16_t &data); + dshot_packet_t _buildDShotPacket(const uint16_t &value) const; + uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; + uint16_t _calculateCRC(const uint16_t &data) const; void _preCalculateRMTTicks(); // Private Frame Processing Functions dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); + uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; // Private Timing Control Functions - bool _isFrameIntervalElapsed(); + bool _isFrameIntervalElapsed() const; void _recordFrameTransmissionTime(); // Static Callback Functions static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); }; + +// Helper to quick print DShot result codes +inline void printDShotResult(dshot_result_t &result, Stream &output = Serial) +{ + output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", _get_result_code_str(result.result_code)); + + // 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(); +} + +// Helper to print DShot signal info +inline void printDShotInfo(const DShotRMT &dshot_rmt, Stream &output = Serial) +{ + output.println("\n === DShot Signal Info === "); + + uint16_t dshot_mode_val = 0; + switch (dshot_rmt.getMode()) + { + case DSHOT150: + dshot_mode_val = 150; + break; + case DSHOT300: + dshot_mode_val = 300; + break; + case DSHOT600: + dshot_mode_val = 600; + break; + case DSHOT1200: + dshot_mode_val = 1200; + break; + } + + output.printf("Current Mode: DSHOT%d\n", dshot_mode_val); + output.printf("Bidirectional: %s\n", dshot_rmt.isBidirectional() ? "YES" : "NO"); + output.printf("Current Packet: "); + + for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) + { + output.print((dshot_rmt.getEncodedFrameValue() >> i) & 1); + } + + output.printf("\nCurrent Value: %u\n", dshot_rmt.getThrottleValue()); +} + +// Helper to print CPU info +inline void printCpuInfo(Stream &output = Serial) +{ + output.println("\n === 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()); +} \ No newline at end of file diff --git a/src/dshot_definitions.h b/src/dshot_definitions.h index 1bdb583..d8db6cb 100644 --- a/src/dshot_definitions.h +++ b/src/dshot_definitions.h @@ -6,14 +6,15 @@ #include #include -// Defines the available DShot communication speeds with type safety. -enum class dshot_mode_t +// Defines the available DShot communication speeds. +enum dshot_mode_t { DSHOT_OFF, DSHOT150, DSHOT300, DSHOT600, - DSHOT1200 + DSHOT1200, + DSHOT_MODE_MAX }; // Represents the 16-bit DShot data packet sent to the ESC. @@ -157,7 +158,8 @@ const dshot_timing_us_t DSHOT_TIMING_US[] = { {6.67, 5.00}, // DSHOT150 {3.33, 2.50}, // DSHOT300 {1.67, 1.25}, // DSHOT600 - {0.83, 0.67} // DSHOT1200 + {0.83, 0.67}, // DSHOT1200 + {0.00, 0.00} // DSHOT_MODE_MAX (dummy entry) }; // Error Messages @@ -245,18 +247,4 @@ inline const char *_get_result_code_str(dshot_msg_code_t code) default: return UNKNOWN_ERROR; } -} - -// Helper to quick print DShot result codes -inline void printDShotResult(dshot_result_t &result, Stream &output = Serial) -{ - output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", _get_result_code_str(result.result_code)); - - // 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(); -} +} \ No newline at end of file diff --git a/src/ota_update.h b/src/ota_update.h index 695426e..1d78be7 100644 --- a/src/ota_update.h +++ b/src/ota_update.h @@ -1,6 +1,6 @@ /** * @file ota_update.h - * @brief DShot signal generation using ESP32 RMT with bidirectional support + * @brief Contains the HTML, CSS, and JavaScript for the OTA (Over-The-Air) update web page. * @author Wastl Kraus * @date 2025-09-13 * @license MIT diff --git a/src/web_content.h b/src/web_content.h index 2b8130d..d5b3a0a 100644 --- a/src/web_content.h +++ b/src/web_content.h @@ -1,6 +1,6 @@ /** * @file web_content.h - * @brief DShot signal generation using ESP32 RMT with bidirectional support + * @brief Contains the HTML, CSS, and JavaScript for the main DShot control web page. * @author Wastl Kraus * @date 2025-09-13 * @license MIT