diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 519ab92..76f1123 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,6 @@ concurrency: cancel-in-progress: true env: - ARDUINO_CLI_VERSION: '1.3.0' ESP32_CORE_VERSION: '3.3.0' jobs: @@ -29,8 +28,6 @@ jobs: - name: Setup Arduino CLI uses: arduino/setup-arduino-cli@v2 - with: - version: ${{ env.ARDUINO_CLI_VERSION }} - name: Cache Arduino data uses: actions/cache@v4 @@ -66,6 +63,7 @@ jobs: matrix: example: - "examples/dshot300/dshot300.ino" + - "examples/command_manager/command_manager.ino" build-flags: - name: "Release" flags: "Automated Build" @@ -76,8 +74,6 @@ jobs: - name: Setup Arduino CLI uses: arduino/setup-arduino-cli@v2 - with: - version: ${{ env.ARDUINO_CLI_VERSION }} - name: Cache Arduino data uses: actions/cache@v4 @@ -116,8 +112,6 @@ jobs: - name: Setup Arduino CLI uses: arduino/setup-arduino-cli@v2 - with: - version: ${{ env.ARDUINO_CLI_VERSION }} - name: Cache Arduino data uses: actions/cache@v4 @@ -143,7 +137,8 @@ jobs: --language=c++ \ --platform=unix32 \ --inline-suppr \ - ./DShotRMT.cpp ./DShotRMT.h + ./DShotRMT.cpp ./DShotRMT.h \ + ./DShotCommandManager.cpp ./DShotCommandManager.h # ============================================================================ # Build Status Report diff --git a/DShotCommandManager.cpp b/DShotCommandManager.cpp new file mode 100644 index 0000000..7ec6f0c --- /dev/null +++ b/DShotCommandManager.cpp @@ -0,0 +1,393 @@ +/* + * DShotCommandManager.cpp + * Advanced DShot command management for DShotRMT library + * Author: Wastl Kraus + * Date: 2025-09-04 + * License: MIT + */ + +#include + +// Constructor +DShotCommandManager::DShotCommandManager(DShotRMT &dshot_instance) + : _dshot(dshot_instance), + _total_commands_sent(0), + _failed_commands(0), + _last_execution_time_us(0), + _last_command_timestamp(0) +{ +} + +// Init command manager +bool DShotCommandManager::begin() +{ + resetStatistics(); + return true; +} + +// --- BASIC COMMAND METHODS --- +dshot_command_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16_t repeat_count) +{ + return sendCommandWithDelay(command, repeat_count, DEFAULT_COMMAND_DELAY_MS); +} + +// +dshot_command_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms) +{ + dshot_command_result_t result = {false, 0, "Unknown error"}; + + if (!isValidCommand(command)) + { + result.error_message = "Invalid command"; + _updateStatistics(false, 0); + return result; + } + + uint64_t start_time = esp_timer_get_time(); + bool all_successful = true; + + // Send command multiple times with delay + for (uint16_t i = 0; i < repeat_count; i++) + { + dshot_command_result_t single_result = _executeCommand(command); + + if (!single_result.success) + { + all_successful = false; + result.error_message = single_result.error_message; + break; + } + + // Add delay between repetitions (except for last repetition) + if (i < repeat_count - 1) + { + _delay(delay_ms); + } + } + + uint64_t end_time = esp_timer_get_time(); + result.execution_time_us = (uint32_t)(end_time - start_time); + result.success = all_successful; + + if (result.success) + { + result.error_message = "Success"; + } + + _updateStatistics(result.success, result.execution_time_us); + + return result; +} + +// --- MOTOR CONTROL COMMANDS --- +dshot_command_result_t DShotCommandManager::stopMotor() +{ + return sendCommand(DSHOT_CMD_MOTOR_STOP); +} + +// +dshot_command_result_t DShotCommandManager::set3DMode(bool enable) +{ + dshot_commands_t command = enable ? DSHOT_CMD_3D_MODE_ON : DSHOT_CMD_3D_MODE_OFF; + return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS); +} + +// +dshot_command_result_t DShotCommandManager::setSpinDirection(bool reversed) +{ + dshot_commands_t command = reversed ? DSHOT_CMD_SPIN_DIRECTION_REVERSED : DSHOT_CMD_SPIN_DIRECTION_NORMAL; + return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS); +} + +// +dshot_command_result_t DShotCommandManager::saveSettings() +{ + return sendCommandWithDelay(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS); +} + +// --- TELEMETRY COMMANDS --- +dshot_command_result_t DShotCommandManager::setExtendedTelemetry(bool enable) +{ + dshot_commands_t command = enable ? DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE : DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE; + return sendCommand(command); +} + +// +dshot_command_result_t DShotCommandManager::requestESCInfo() +{ + return sendCommand(DSHOT_CMD_ESC_INFO); +} + +// --- LED CONTROL COMMANDS --- +dshot_command_result_t DShotCommandManager::setLED(uint8_t led_number, bool state) +{ + if (led_number > 3) + { + dshot_command_result_t result = {false, 0, "Invalid LED number (0-3)"}; + _updateStatistics(false, 0); + return result; + } + + dshot_commands_t command; + if (state) + { + // LED ON commands + switch (led_number) + { + case 0: + command = DSHOT_CMD_LED0_ON; + break; + case 1: + command = DSHOT_CMD_LED1_ON; + break; + case 2: + command = DSHOT_CMD_LED2_ON; + break; + case 3: + command = DSHOT_CMD_LED3_ON; + break; + } + } + else + { + // LED OFF commands + switch (led_number) + { + case 0: + command = DSHOT_CMD_LED0_OFF; + break; + case 1: + command = DSHOT_CMD_LED1_OFF; + break; + case 2: + command = DSHOT_CMD_LED2_OFF; + break; + case 3: + command = DSHOT_CMD_LED3_OFF; + break; + } + } + + return sendCommand(command); +} + +// --- BEACON COMMANDS --- +dshot_command_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number) +{ + if (beacon_number < 1 || beacon_number > 5) + { + dshot_command_result_t result = {false, 0, "Invalid beacon number (1-5)"}; + _updateStatistics(false, 0); + return result; + } + + dshot_commands_t command = static_cast(DSHOT_CMD_BEACON1 + beacon_number - 1); + return sendCommand(command); +} + +// --- KISS ESC SPECIFIC COMMANDS --- +dshot_command_result_t DShotCommandManager::setAudioStreamMode(bool enable) +{ + // KISS audio stream mode is a toggle command + return sendCommand(DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF); +} + +// +dshot_command_result_t DShotCommandManager::setSilentMode(bool enable) +{ + // KISS silent mode is a toggle command + return sendCommand(DSHOT_CMD_SILENT_MODE_ON_OFF); +} + +// --- SEQUENCE COMMANDS --- +dshot_command_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length) +{ + dshot_command_result_t result = {true, 0, "Success"}; + uint64_t total_start_time = esp_timer_get_time(); + + for (size_t i = 0; i < sequence_length; i++) + { + dshot_command_result_t item_result = sendCommandWithDelay( + sequence[i].command, + sequence[i].repeat_count, + DEFAULT_COMMAND_DELAY_MS); + + if (!item_result.success) + { + result.success = false; + result.error_message = item_result.error_message; + break; + } + + // Add delay after command if specified + if (sequence[i].delay_ms > 0) + { + _delay(sequence[i].delay_ms); + } + } + + uint64_t total_end_time = esp_timer_get_time(); + result.execution_time_us = (uint32_t)(total_end_time - total_start_time); + + return result; +} + +// +dshot_command_result_t DShotCommandManager::executeInitSequence() +{ + // Basic ESC initialization sequence + dshot_command_sequence_item_t init_sequence[] = { + {DSHOT_CMD_MOTOR_STOP, 5, 100}, // Stop motor, repeat 5 times, wait 100ms + {DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE, 1, 50}, // Enable telemetry, wait 50ms + {DSHOT_CMD_ESC_INFO, 1, 100} // Request ESC info, wait 100ms + }; + + return executeSequence(init_sequence, sizeof(init_sequence) / sizeof(init_sequence[0])); +} + +// +dshot_command_result_t DShotCommandManager::executeCalibrationSequence() +{ + // Basic ESC calibration sequence + dshot_command_sequence_item_t calibration_sequence[] = { + {DSHOT_CMD_MOTOR_STOP, 10, 500}, // Ensure motor is stopped + {DSHOT_CMD_SPIN_DIRECTION_NORMAL, 10, 100}, // Set normal spin direction + {DSHOT_CMD_3D_MODE_OFF, 10, 100}, // Disable 3D mode + {DSHOT_CMD_SAVE_SETTINGS, 10, 1000}, // Save settings + {DSHOT_CMD_MOTOR_STOP, 5, 100} // Final stop + }; + + return executeSequence(calibration_sequence, sizeof(calibration_sequence) / sizeof(calibration_sequence[0])); +} + +// --- UTILITY METHODS --- +const char *DShotCommandManager::getCommandName(dshot_commands_t command) +{ + switch (command) + { + case DSHOT_CMD_MOTOR_STOP: + return "MOTOR_STOP"; + case DSHOT_CMD_BEACON1: + return "BEACON1"; + case DSHOT_CMD_BEACON2: + return "BEACON2"; + case DSHOT_CMD_BEACON3: + return "BEACON3"; + case DSHOT_CMD_BEACON4: + return "BEACON4"; + case DSHOT_CMD_BEACON5: + return "BEACON5"; + case DSHOT_CMD_ESC_INFO: + return "ESC_INFO"; + case DSHOT_CMD_SPIN_DIRECTION_1: + return "SPIN_DIRECTION_1"; + case DSHOT_CMD_SPIN_DIRECTION_2: + return "SPIN_DIRECTION_2"; + case DSHOT_CMD_3D_MODE_OFF: + return "3D_MODE_OFF"; + case DSHOT_CMD_3D_MODE_ON: + return "3D_MODE_ON"; + case DSHOT_CMD_SETTINGS_REQUEST: + return "SETTINGS_REQUEST"; + case DSHOT_CMD_SAVE_SETTINGS: + return "SAVE_SETTINGS"; + case DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE: + return "EXTENDED_TELEMETRY_ENABLE"; + case DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE: + return "EXTENDED_TELEMETRY_DISABLE"; + case DSHOT_CMD_SPIN_DIRECTION_NORMAL: + return "SPIN_DIRECTION_NORMAL"; + case DSHOT_CMD_SPIN_DIRECTION_REVERSED: + return "SPIN_DIRECTION_REVERSED"; + case DSHOT_CMD_LED0_ON: + return "LED0_ON"; + case DSHOT_CMD_LED1_ON: + return "LED1_ON"; + case DSHOT_CMD_LED2_ON: + return "LED2_ON"; + case DSHOT_CMD_LED3_ON: + return "LED3_ON"; + case DSHOT_CMD_LED0_OFF: + return "LED0_OFF"; + case DSHOT_CMD_LED1_OFF: + return "LED1_OFF"; + case DSHOT_CMD_LED2_OFF: + return "LED2_OFF"; + case DSHOT_CMD_LED3_OFF: + return "LED3_OFF"; + case DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF: + return "AUDIO_STREAM_MODE_ON_OFF"; + case DSHOT_CMD_SILENT_MODE_ON_OFF: + return "SILENT_MODE_ON_OFF"; + default: + return "UNKNOWN"; + } +} + +// +bool DShotCommandManager::isValidCommand(dshot_commands_t command) +{ + return (command >= DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX); +} + +// +void DShotCommandManager::printStatistics(Stream &output) const +{ + output.println("\n--- DShot Command Manager Statistics ---"); + output.printf("Total commands sent: %u\n", _total_commands_sent); + output.printf("Failed commands: %u\n", _failed_commands); + output.printf("Success rate: %.2f%%\n", + _total_commands_sent > 0 ? (float)(_total_commands_sent - _failed_commands) / _total_commands_sent * 100.0f : 0.0f); + output.printf("Last execution time: %u us\n", _last_execution_time_us); + output.printf("Last command timestamp: %llu us\n", _last_command_timestamp); +} + +// +void DShotCommandManager::resetStatistics() +{ + _total_commands_sent = 0; + _failed_commands = 0; + _last_execution_time_us = 0; + _last_command_timestamp = 0; +} + +// --- PRIVATE METHODS --- +dshot_command_result_t DShotCommandManager::_executeCommand(dshot_commands_t command) +{ + dshot_command_result_t result = {false, 0, "Execution failed"}; + + uint64_t start_time = esp_timer_get_time(); + + // Execute the command using the DShotRMT instance + bool success = _dshot.sendCommand(static_cast(command)); + + uint64_t end_time = esp_timer_get_time(); + + result.success = success; + result.execution_time_us = (uint32_t)(end_time - start_time); + result.error_message = success ? "Success" : "Command transmission failed"; + + _last_command_timestamp = end_time; + + return result; +} + +// +void DShotCommandManager::_delay(uint32_t delay_ms) +{ + if (delay_ms > 0) + { + delay(delay_ms); + } +} + +// +void DShotCommandManager::_updateStatistics(bool success, uint32_t execution_time_us) +{ + _total_commands_sent++; + if (!success) + { + _failed_commands++; + } + _last_execution_time_us = execution_time_us; +} diff --git a/DShotCommandManager.h b/DShotCommandManager.h new file mode 100644 index 0000000..ccb2f05 --- /dev/null +++ b/DShotCommandManager.h @@ -0,0 +1,146 @@ +/* + * DShotCommandManager.h + * Advanced DShot command management for DShotRMT library + * Author: Wastl Kraus + * Date: 2025-09-04 + * License: MIT + */ + +#pragma once + +#include +#include +#include + +// Naming convention +typedef dshotCommands_e dshot_commands_t; + +// Command execution result structure +typedef struct +{ + bool success; + uint32_t execution_time_us; + const char *error_message; +} dshot_command_result_t; + +// Command sequence item +typedef struct +{ + dshot_commands_t command; + uint16_t repeat_count; + uint32_t delay_ms; +} dshot_command_sequence_item_t; + +// Advanced DShot command manager class +class DShotCommandManager +{ +public: + // Constructor + explicit DShotCommandManager(DShotRMT &dshot_instance); + + // Initialize command manager + bool begin(); + + bool printMenu(Stream &output); + + void handleMenuInput(const String &input, Stream &output = Serial); + + // Send a single DShot command + dshot_command_result_t sendCommand(dshot_commands_t command, uint16_t repeat_count = 1); + + // Send command with specified delay between repetitions + dshot_command_result_t sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms); + + // --- MOTOR CONTROL COMMANDS --- + // Stop motor (send MOTOR_STOP command) + dshot_command_result_t stopMotor(); + + // Enable/disable 3D mode + dshot_command_result_t set3DMode(bool enable); + + // Set motor spin direction + dshot_command_result_t setSpinDirection(bool reversed); + + // Save current settings to ESC + dshot_command_result_t saveSettings(); + + // --- TELEMETRY COMMANDS --- + // Enable/disable extended telemetry + dshot_command_result_t setExtendedTelemetry(bool enable); + + // Request ESC information + dshot_command_result_t requestESCInfo(); + + // --- LED CONTROL COMMANDS (BLHeli32 only) --- + + // Control ESC LEDs (BLHeli32 only) + dshot_command_result_t setLED(uint8_t led_number, bool state); + + // --- BEACON COMMANDS --- + // Activate beacon (motor beeping) + dshot_command_result_t activateBeacon(uint8_t beacon_number); + + // --- KISS ESC SPECIFIC COMMANDS --- + // Enable/disable audio stream mode (KISS ESCs) + dshot_command_result_t setAudioStreamMode(bool enable); + + // Enable/disable silent mode (KISS ESCs) + dshot_command_result_t setSilentMode(bool enable); + + // --- SEQUENCE COMMANDS --- + // Execute a sequence of DShot commands + dshot_command_result_t executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length); + + // Execute ESC initialization sequence + dshot_command_result_t executeInitSequence(); + + // Execute ESC calibration sequence + dshot_command_result_t executeCalibrationSequence(); + + // --- UTILITY METHODS --- + // Get command name as string + static const char *getCommandName(dshot_commands_t command); + + // Check if command is valid + static bool isValidCommand(dshot_commands_t command); + + // Print command execution statistics + void printStatistics(Stream &output = Serial) const; + + // Reset execution statistics + void resetStatistics(); + + // --- GETTERS --- + // Get total number of commands sent + uint32_t getTotalCommandCount() const { return _total_commands_sent; } + + // Get number of failed commands + uint32_t getFailedCommandCount() const { return _failed_commands; } + + // Get last command execution time + uint32_t getLastExecutionTime() const { return _last_execution_time_us; } + +private: + // --- PRIVATE MEMBERS --- + DShotRMT &_dshot; // Reference to DShotRMT instance + uint32_t _total_commands_sent; // Total commands sent counter + uint32_t _failed_commands; // Failed commands counter + uint32_t _last_execution_time_us; // Last command execution time + uint64_t _last_command_timestamp; // Timestamp of last command + + // --- PRIVATE METHODS --- + // Execute single command with timing + dshot_command_result_t _executeCommand(dshot_commands_t command); + + // Wait for specified delay + void _delay(uint32_t delay_ms); + + // Update execution statistics + void _updateStatistics(bool success, uint32_t execution_time_us); + + // --- CONSTANTS --- + static constexpr uint32_t DEFAULT_COMMAND_DELAY_MS = 10; + static constexpr uint16_t DEFAULT_REPEAT_COUNT = 1; + static constexpr uint16_t SETTINGS_COMMAND_REPEATS = 10; // Settings commands need 10 repeats + static constexpr uint32_t SETTINGS_COMMAND_DELAY_MS = 5; +}; diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 94580d5..597130f 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -63,6 +63,7 @@ DShotRMT::~DShotRMT() { rmt_disable(_rmt_tx_channel); rmt_del_channel(_rmt_tx_channel); + _rmt_tx_channel = nullptr; } // @@ -70,32 +71,28 @@ DShotRMT::~DShotRMT() { rmt_disable(_rmt_rx_channel); rmt_del_channel(_rmt_rx_channel); + _rmt_rx_channel = nullptr; } // if (_dshot_encoder) { rmt_del_encoder(_dshot_encoder); + _dshot_encoder = nullptr; } // if (_rx_queue) { vQueueDelete(_rx_queue); + _rx_queue = nullptr; } } // Initialize DShotRMT uint16_t DShotRMT::begin() { - // Init TX channel - if (!_initTXChannel()) - { - _dshot_log(TX_INIT_FAILED); - return DSHOT_ERROR; - } - - // Init RX channel + // Init RX channel first if (_is_bidirectional) { if (!_initRXChannel()) @@ -105,6 +102,13 @@ uint16_t DShotRMT::begin() } } + // Init TX channel + if (!_initTXChannel()) + { + _dshot_log(TX_INIT_FAILED); + return DSHOT_ERROR; + } + // Init DShot encoder if (_initDShotEncoder() != DSHOT_OK) { @@ -297,7 +301,7 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) } // Build packet - packet.throttle_value = value; + packet.throttle_value = value & 0b0000011111111111; packet.telemetric_request = _is_bidirectional ? 1 : 0; // CRC is calculated over 11bit @@ -334,7 +338,7 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data) } // Transmit DShot packet via RMT -uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) +bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) { // Check timing requirements if (!_timer_signal()) @@ -349,9 +353,6 @@ uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) rmt_symbol_word_t rx_symbols[DSHOT_BITS_PER_FRAME]; rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config); - - // Disable RMT RX for sending - rmt_disable(_rmt_rx_channel); } // Local for performance @@ -363,11 +364,21 @@ uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) // Calculate transmission data size size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); - // Perform RMT transmission - uint16_t result = rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config); - - if (result != DSHOT_OK) + // TODO: Find out, why this is needed + if (_is_bidirectional) { + // Disable RMT RX for sending + if (rmt_disable(_rmt_rx_channel) != DSHOT_OK) + { + _dshot_log(RX_RMT_RECEIVER_ERROR); + return DSHOT_ERROR; + } + } + + // Perform RMT transmission + if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config) != DSHOT_OK) + { + _dshot_log(TRANSMITTER_ERROR); return DSHOT_ERROR; } @@ -377,11 +388,13 @@ uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) { _dshot_log(RX_RMT_RECEIVER_ERROR); + return DSHOT_ERROR; } } // Update timestamp and return success _timer_reset(); + return DSHOT_OK; } @@ -406,47 +419,51 @@ bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_sym return DSHOT_OK; } -// Decode received RMT symbols +// Decodes a DShot telemetry frame from received RMT symbols. uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) { - // DShot answer is GCR encoded. - // GCR decoding: bit_N = gcr_bit_N ^ gcr_bit_(N-1) - uint32_t raw_gcr_data = 0; + uint32_t gcr_value = 0; - // Based on DShot bidirectional protocol, idle state is high, - // so the first duration is a low pulse. - // Bit 1: long low pulse, short high pulse - // Bit 0: short low pulse, long high pulse + // Decode GCR symbols into a 21-bit value. + // '1' has a longer low pulse (duration0 > duration1). + // '0' has a longer high pulse (duration1 > duration0). for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i) { - // Check which duration is longer to determine if it's a '1' bit bool bit_is_one = symbols[i].duration0 > symbols[i].duration1; - raw_gcr_data = (raw_gcr_data << 1) | bit_is_one; + gcr_value = (gcr_value << 1) | bit_is_one; } - // Extract the 10-bit data from the GCR frame - uint16_t gcr_data = (raw_gcr_data >> 5) & 0b0000001111111111; // Mask for 10 bits + // Perform GCR decoding: data = gcr ^ (gcr >> 1). + uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1); - // GCR decoding over the "throttle" bits - uint16_t received_data = gcr_data ^ (gcr_data >> 1); + // Extract 16 data bits and 4 CRC bits from 20-bit frame. + // The first bit of the GCR frame is a start bit and is discarded. + uint16_t data_and_crc = (decoded_frame & 0xFFFF); - // Extract CRC from gcr answer (4 bits) - uint16_t received_crc = raw_gcr_data & 0b0000000000001111; // Mask for 4 bits + // Cutting 4 bits? + uint16_t received_data = data_and_crc >> 4; - // Calculate expected CRC using the new, centralized function - // Telemetry request bit is always 1 for bidirectional - uint16_t data_for_crc = (received_data << 1) | 1; - uint16_t calculated_crc = _calculateCRC(data_for_crc); - - // Validate CRC - if (received_crc != calculated_crc) + // Masking CRC + uint16_t received_crc = data_and_crc & 0b0000000000001111; + + // Telemetry request bit is always 1. + if (!(received_data & (1 << 11))) { - _dshot_log(CRC_CHECK_FAILED); return DSHOT_NULL_PACKET; } - // The data is eRPM * 100 - return received_data; + // Calculate expected CRC + uint16_t data_for_crc = received_data; + uint16_t calculated_crc = _calculateCRC(data_for_crc); + + // Validate CRC + if (received_crc != calculated_crc) + { + return DSHOT_NULL_PACKET; + } + + // Return the eRPM value (first 11 bits of received data). + return received_data & 0b0000011111111111; } // Check if enough time has passed for next transmission @@ -468,7 +485,7 @@ bool DShotRMT::_timer_reset() } // Print timing diagnostic information to specified stream -void DShotRMT::printDshotInfo(Stream &output) const +void DShotRMT::printDShotInfo(Stream &output) const { output.println(" "); output.println(" === DShot Signal Info === "); diff --git a/DShotRMT.h b/DShotRMT.h index 475f5e5..21cc76d 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -27,7 +27,7 @@ constexpr auto DSHOT_RX_TIMEOUT_MS = 2; 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 GCR_BITS_PER_FRAME = 20; +constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC) constexpr auto RMT_BUFFER_SYMBOLS = 64; constexpr auto RMT_QUEUE_DEPTH = 1; @@ -47,15 +47,15 @@ typedef enum } dshot_mode_t; // DShot Packet Structure -typedef struct +typedef struct { uint16_t throttle_value : 11; - uint16_t telemetric_request : 1; + bool telemetric_request : 1; uint16_t checksum : 4; } dshot_packet_t; // DShot Timing Configuration Structure -typedef struct +typedef struct { uint32_t frame_length_us; uint16_t ticks_per_bit; @@ -101,16 +101,16 @@ public: bool is_bidirectional() const { return _is_bidirectional; } // --- INFO --- - void printDshotInfo(Stream &output = Serial0) const; - void printCpuInfo(Stream &output = Serial0) const; + void printDShotInfo(Stream &output = Serial) const; + void printCpuInfo(Stream &output = Serial) const; // --- DEPRECATED METHODS --- [[deprecated("Use sendThrottle() instead")]] bool setThrottle(uint16_t throttle) { return sendThrottle(throttle); } - + [[deprecated("Use sendCommand() instead")]] bool sendDShotCommand(uint16_t command) { return sendCommand(command); } - + private: // --- CONFIG --- gpio_num_t _gpio; @@ -147,7 +147,7 @@ private: uint16_t _calculateCRC(const uint16_t data); // --- FRAME PROCESSING --- - uint16_t _sendDShotFrame(const dshot_packet_t &packet); + bool _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); @@ -160,12 +160,15 @@ private: rmt_rx_event_callbacks_t _rx_event_callbacks; 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 DSHOT_TELEMETRY_INVALID = (0xffff); + // --- ERROR HANDLING & LOGGING --- void _dshot_log(const char *msg, Stream &output = Serial) { output.println(msg); } // --- CONSTANTS & ERROR MESSAGES --- - static constexpr uint16_t DSHOT_OK = 0; - static constexpr uint16_t DSHOT_ERROR = 1; + static constexpr bool DSHOT_OK = 0; + static constexpr bool DSHOT_ERROR = 1; static constexpr char *NEW_LINE = " "; static constexpr char *TX_INIT_FAILED = "Failed to initialize TX channel!"; @@ -177,4 +180,5 @@ private: static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot support not enabled!"; static constexpr char *RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!"; static constexpr char *PACKET_BUILD_ERROR = "Value too big for DShot Packet!"; + static constexpr char *TRANSMITTER_ERROR = "RMT TX Transmitter Error!"; }; diff --git a/README.md b/README.md index 9ea9cb8..c7c716a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A modern, robust C++ library for generating DShot signals on the ESP32 using the new ESP-IDF 5 RMT encoder API (`rmt_tx.h` / `rmt_rx.h`). Supports all standard DShot modes (150, 300, 600) and features continuous frame transmission with configurable pause. -**Now with BiDirectional DShot support!** +**Now with BiDirectional DShot support and advanced command management!** > The legacy version (using the old `rmt.h` API) is still available in the `oldAPI` branch. @@ -14,6 +14,8 @@ Supports all standard DShot modes (150, 300, 600) and features continuous frame - **All DShot Modes:** DSHOT150, DSHOT300 (default), DSHOT600, (DSHOT1200) - **BiDirectional DShot:** Experimental support for RPM feedback +- **Advanced Command Manager:** High-level API for ESC configuration and control +- **Command Sequences:** Predefined initialization and calibration sequences - **Continuous Frames:** Independent timed, Hardware signal generation - **Configurable Pause:** Ensures ESCs can reliably detect frame boundaries - **Simple API:** Easy integration into your Arduino or ESP-IDF project @@ -32,8 +34,113 @@ git clone https://github.com/derdoktor667/DShotRMT.git ## ⚡ Quick Start +### Basic Usage (DShotRMT) + ```cpp -Use "dshot300.ino" example sketch +#include + +// Create motor instance +DShotRMT motor(17, DSHOT300); + +void setup() { + Serial.begin(115200); + motor.begin(); +} + +void loop() { + motor.sendThrottle(1000); // Send throttle value + delay(20); +} +``` + +### Advanced Usage (DShotCommandManager) + +```cpp +#include +#include + +// Create motor and command manager instances +DShotRMT motor(17, DSHOT300); +DShotCommandManager cmdManager(motor); + +void setup() { + Serial.begin(115200); + motor.begin(); + cmdManager.begin(); + + // Execute initialization sequence + cmdManager.executeInitSequence(); +} + +void loop() { + // Your main code here +} +``` + +--- + +## 🎛️ DShotCommandManager API + +The `DShotCommandManager` provides a high-level interface for ESC control and configuration: + +### Motor Control +- `stopMotor()` - Stop motor immediately +- `set3DMode(bool enable)` - Enable/disable 3D mode +- `setSpinDirection(bool reversed)` - Set motor spin direction +- `saveSettings()` - Save current settings to ESC + +### LED Control (BLHeli32 only) +- `setLED(uint8_t led_number, bool state)` - Control ESC LEDs (0-3) + +### Beacon Functions +- `activateBeacon(uint8_t beacon_number)` - Activate motor beeping (1-5) + +### Telemetry +- `setExtendedTelemetry(bool enable)` - Enable/disable extended telemetry +- `requestESCInfo()` - Request ESC information + +### Command Sequences +- `executeInitSequence()` - Basic ESC initialization +- `executeCalibrationSequence()` - ESC calibration sequence +- `executeSequence(sequence, length)` - Custom command sequences + +### Utility Functions +- `getCommandName(command)` - Get command name as string +- `isValidCommand(command)` - Validate command +- `printStatistics()` - Print execution statistics +- `resetStatistics()` - Reset execution counters + +--- + +## 📚 Examples + +### 1. Basic DShot Control +Use the `dshot300.ino` example for simple throttle control. + +### 2. Advanced Command Management +Use the `command_manager.ino` example for interactive ESC control: + +``` +=== DShot Command Manager Menu === +Basic Commands: + 1 - Stop Motor + 2 - Activate Beacon 1 + 3 - Set Normal Spin Direction + 4 - Set Reversed Spin Direction + 5 - Enable 3D Mode + 6 - Disable 3D Mode + 7 - Save Settings + 8 - Turn LED 0 ON + 9 - Turn LED 0 OFF + +Sequences: + i - Execute Initialization Sequence + c - Execute Calibration Sequence + +Advanced: + cmd - Send DShot command (0 - 47) + throttle - Set throttle (48 - 2047) + throttle 0 - Stop sending throttle ``` --- @@ -95,13 +202,45 @@ Perfect for DShot: --- -## 📝 API Reference +## 📝 Core API Reference +### DShotRMT Class - `DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool isBidirectional)` -- `void begin()` -- `void sendThrottle(uint16_t throttle)` +- `uint16_t begin()` +- `bool sendThrottle(uint16_t throttle)` +- `bool sendCommand(uint16_t command)` +- `uint16_t getERPM()` - Get eRPM (bidirectional mode only) +- `uint32_t getMotorRPM(uint8_t magnet_count)` - Convert to motor RPM -See [examples/dshot300/dshot300.ino](examples/dshot300/dshot300.ino) for a more informations. +### DShotCommandManager Class +- `DShotCommandManager(DShotRMT &dshot_instance)` +- `bool begin()` +- `dshot_command_result_t sendCommand(dshot_commands_t command, uint16_t repeat_count = 1)` +- `dshot_command_result_t sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms)` + +All command methods return a `dshot_command_result_t` structure containing: +- `bool success` - Command execution status +- `uint32_t execution_time_us` - Execution time in microseconds +- `const char* error_message` - Error description + +--- + +## 🎯 DShot Commands + +The library supports all standard DShot commands: + +| Command | Value | Description | +|---------|-------|-------------| +| MOTOR_STOP | 0 | Stop motor | +| BEACON1-5 | 1-5 | Motor beeping | +| ESC_INFO | 6 | Request ESC information | +| SPIN_DIRECTION_1/2 | 7-8 | Set spin direction | +| 3D_MODE_OFF/ON | 9-10 | 3D mode control | +| SAVE_SETTINGS | 12 | Save settings to ESC | +| EXTENDED_TELEMETRY_ENABLE/DISABLE | 13-14 | Telemetry control | +| LED0-3_ON/OFF | 22-29 | LED control (BLHeli32) | +| AUDIO_STREAM_MODE | 30 | KISS audio mode | +| SILENT_MODE | 31 | KISS silent mode | --- diff --git a/examples/command_manager/command_manager.ino b/examples/command_manager/command_manager.ino new file mode 100644 index 0000000..6d0d508 --- /dev/null +++ b/examples/command_manager/command_manager.ino @@ -0,0 +1,275 @@ +/* + * command_manager_example.ino + * Example sketch demonstrating DShotCommandManager usage + * Author: Wastl Kraus + * Date: 2025-09-04 + * License: MIT + * + * Modified by Especiallist to support continuous throttle sending. + */ + +#include +#include +#include + +// USB serial port settings +static constexpr auto &USB_SERIAL = Serial0; +static constexpr auto USB_SERIAL_BAUD = 115200; + +// Motor configuration +static constexpr auto MOTOR01_PIN = 17; +static constexpr auto IS_BIDIRECTIONAL = false; + +// Motor magnet count for RPM calculation +static constexpr auto MOTOR01_MAGNET_COUNT = 14; + +// Create motor and command manager instances +DShotRMT motor01(MOTOR01_PIN, DSHOT300, IS_BIDIRECTIONAL); +DShotCommandManager commandManager(motor01); + +// Global variable to store the desired continuous throttle value +static volatile uint16_t throttle_now = NULL; + +// +void setup() +{ + // Start USB Serial Port + USB_SERIAL.begin(USB_SERIAL_BAUD); + + USB_SERIAL.println("=== DShotRMT Command Manager Example ==="); + + // Initialize DShot + if (motor01.begin() != 0) + { + USB_SERIAL.println("ERROR: Failed to initialize DShotRMT!"); + while (1) + delay(1000); + } + + // Init Command Manager + if (!commandManager.begin()) + { + USB_SERIAL.println("ERROR: Failed to initialize DShotCommandManager!"); + while (1) + delay(1000); + } + + USB_SERIAL.println("Initialization successful!"); + + // Print Menu + printMenu(); +} + +// +void loop() +{ + // Time Measurement + static uint64_t last_stats_print = 0; + + // Check for serial input + if (USB_SERIAL.available() > 0) + { + String input = USB_SERIAL.readStringUntil('\n'); + input.trim(); + handleUserInput(input); + } + + // Continuously send the stored throttle value + if (throttle_now != NULL) + { + motor01.sendThrottle(throttle_now); + + // Print motor stats every 2 seconds + if (esp_timer_get_time() - last_stats_print >= 2000000) + { + motor01.printDShotInfo(); + + // Get Motor RPM + if (IS_BIDIRECTIONAL) + { + uint32_t rpm = motor01.getMotorRPM(MOTOR01_MAGNET_COUNT); + USB_SERIAL.printf("Motor RPM: %u\n", rpm); + } + + // Time Stamp + last_stats_print = esp_timer_get_time(); + } + } +} + +// +void handleUserInput(const String &input) +{ + dshot_command_result_t result; + + if (input == "1") + { + // Stop motor command should also reset the continuous throttle value + throttle_now = 0; + USB_SERIAL.print("Stopping motor... "); + result = commandManager.stopMotor(); + printResult(result); + } + else if (input == "2") + { + USB_SERIAL.print("Activating beacon 1... "); + result = commandManager.activateBeacon(1); + printResult(result); + } + else if (input == "3") + { + USB_SERIAL.print("Setting normal spin direction... "); + result = commandManager.setSpinDirection(false); + printResult(result); + } + else if (input == "4") + { + USB_SERIAL.print("Setting reversed spin direction... "); + result = commandManager.setSpinDirection(true); + printResult(result); + } + else if (input == "5") + { + USB_SERIAL.print("Enabling 3D mode... "); + result = commandManager.set3DMode(true); + printResult(result); + } + else if (input == "6") + { + USB_SERIAL.print("Disabling 3D mode... "); + result = commandManager.set3DMode(false); + printResult(result); + } + else if (input == "7") + { + USB_SERIAL.print("Saving settings... "); + result = commandManager.saveSettings(); + printResult(result); + } + else if (input == "8") + { + USB_SERIAL.print("Turning LED 0 ON... "); + result = commandManager.setLED(0, true); + printResult(result); + } + else if (input == "9") + { + USB_SERIAL.print("Turning LED 0 OFF... "); + result = commandManager.setLED(0, false); + printResult(result); + } + else if (input == "i") + { + USB_SERIAL.print("Executing initialization sequence... "); + result = commandManager.executeInitSequence(); + printResult(result); + } + else if (input == "c") + { + USB_SERIAL.print("Executing calibration sequence... "); + result = commandManager.executeCalibrationSequence(); + printResult(result); + } + else if (input == "s") + { + commandManager.printStatistics(); + } + else if (input == "r") + { + commandManager.resetStatistics(); + USB_SERIAL.println("Statistics reset."); + } + else if (input == "h") + { + printMenu(); + } + else if (input == "help") + { + printMenu(); + } + else if (input.startsWith("cmd ")) + { + // Direct command execution: "cmd 5" sends command 5 + int cmd_num = input.substring(4).toInt(); + if (DShotCommandManager::isValidCommand(static_cast(cmd_num))) + { + USB_SERIAL.printf("Sending command %d (%s)... ", cmd_num, + DShotCommandManager::getCommandName(static_cast(cmd_num))); + result = commandManager.sendCommand(static_cast(cmd_num)); + printResult(result); + } + else + { + USB_SERIAL.printf("Invalid command number: %d (valid range: 0-%d)\n", cmd_num, DSHOT_CMD_MAX); + } + } + else if (input.startsWith("throttle ")) + { + // Throttle control: "throttle 1000" sets throttle to 1000 + int throttle_value = input.substring(9).toInt(); + if (throttle_value >= DSHOT_THROTTLE_MIN && throttle_value <= DSHOT_THROTTLE_MAX) + { + throttle_now = throttle_value; + USB_SERIAL.printf("Setting continuous throttle to %d\n", throttle_now); + } + else if (throttle_value == 0) + { + throttle_now = 0; + USB_SERIAL.println("Continuous throttle stopped."); + } + else + { + USB_SERIAL.printf("Invalid throttle value: %d (valid range: 48-2047, use 0 to stop)\n", throttle_value); + } + } + else + { + USB_SERIAL.println("Unknown command. Type 'h' for help."); + } +} + +// +void printResult(const dshot_command_result_t &result) +{ + if (!result.success) + { + USB_SERIAL.printf("SUCCESS (%u us)\n", result.execution_time_us); + } + else + { + USB_SERIAL.printf("FAILED - %s (%u us)\n", result.error_message, result.execution_time_us); + } +} + +// +void printMenu() +{ + USB_SERIAL.println("================================================"); + USB_SERIAL.println("\n=== DShot Command Manager Menu ==="); + USB_SERIAL.println("Basic Commands:"); + USB_SERIAL.println(" 1 - Stop Motor"); + USB_SERIAL.println(" 2 - Activate Beacon 1"); + USB_SERIAL.println(" 3 - Set Normal Spin Direction"); + USB_SERIAL.println(" 4 - Set Reversed Spin Direction"); + USB_SERIAL.println(" 5 - Enable 3D Mode"); + USB_SERIAL.println(" 6 - Disable 3D Mode"); + USB_SERIAL.println(" 7 - Save Settings"); + USB_SERIAL.println(" 8 - Turn LED 0 ON"); + USB_SERIAL.println(" 9 - Turn LED 0 OFF"); + USB_SERIAL.println(""); + USB_SERIAL.println("Sequences:"); + USB_SERIAL.println(" i - Execute Initialization Sequence"); + USB_SERIAL.println(" c - Execute Calibration Sequence"); + USB_SERIAL.println(""); + USB_SERIAL.println("Advanced:"); + USB_SERIAL.println(" cmd - Send DShot command (0 - 47)"); + USB_SERIAL.println(" throttle - Set throttle (48 - 2047)"); + USB_SERIAL.println(" throttle 0 - Stop sending throttle"); + USB_SERIAL.println(""); + USB_SERIAL.println(" h - Show this Menu"); + USB_SERIAL.println(""); + USB_SERIAL.println("Examples:"); + USB_SERIAL.println(" cmd 1 - Stop Motor"); + USB_SERIAL.println(" throttle 1000 - Set throttle to 1000"); + USB_SERIAL.println("================================================"); +} diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index d0c8af5..4343b93 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -71,7 +71,7 @@ void loop() // Print motor stats every 2 seconds if (millis() - last_stats_print >= 2000) { - motor01.printDshotInfo(); + motor01.printDShotInfo(); // Get Motor RPM if (IS_BIDIRECTIONAL) diff --git a/keywords.txt b/keywords.txt index 67b4d0f..16404c7 100644 --- a/keywords.txt +++ b/keywords.txt @@ -7,9 +7,13 @@ ####################################### DShotRMT KEYWORD1 +DShotCommandManager KEYWORD1 dshot_mode_t KEYWORD1 dshot_packet_t KEYWORD1 dshot_timing_t KEYWORD1 +dshot_commands_t KEYWORD1 +dshot_command_result_t KEYWORD1 +dshot_command_sequence_item_t KEYWORD1 dshotCommands_e KEYWORD1 dshotCommandType_e KEYWORD1 @@ -17,6 +21,7 @@ dshotCommandType_e KEYWORD1 # Methods and Functions (KEYWORD2) ####################################### +# DShotRMT Methods begin KEYWORD2 setThrottle KEYWORD2 sendThrottle KEYWORD2 @@ -27,23 +32,101 @@ getMotorRPM KEYWORD2 getGPIO KEYWORD2 getDShotPacket KEYWORD2 is_bidirectional KEYWORD2 +printDShotInfo KEYWORD2 +printCpuInfo KEYWORD2 + +# DShotCommandManager Methods +sendCommand KEYWORD2 +sendCommandWithDelay KEYWORD2 +stopMotor KEYWORD2 +set3DMode KEYWORD2 +setSpinDirection KEYWORD2 +saveSettings KEYWORD2 +setExtendedTelemetry KEYWORD2 +requestESCInfo KEYWORD2 +setLED KEYWORD2 +activateBeacon KEYWORD2 +setAudioStreamMode KEYWORD2 +setSilentMode KEYWORD2 +executeSequence KEYWORD2 +executeInitSequence KEYWORD2 +executeCalibrationSequence KEYWORD2 +getCommandName KEYWORD2 +isValidCommand KEYWORD2 +printStatistics KEYWORD2 +resetStatistics KEYWORD2 +getTotalCommandCount KEYWORD2 +getFailedCommandCount KEYWORD2 +getLastExecutionTime KEYWORD2 +printMenu KEYWORD2 +handleMenuInput KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +# DShot Modes DSHOT_OFF LITERAL1 DSHOT150 LITERAL1 DSHOT300 LITERAL1 DSHOT600 LITERAL1 DSHOT1200 LITERAL1 +# DShot Throttle Constants DSHOT_THROTTLE_FAILSAFE LITERAL1 DSHOT_THROTTLE_MIN LITERAL1 DSHOT_THROTTLE_MAX LITERAL1 +# DShot Commands DSHOT_CMD_MOTOR_STOP LITERAL1 +DSHOT_CMD_BEACON1 LITERAL1 +DSHOT_CMD_BEACON2 LITERAL1 +DSHOT_CMD_BEACON3 LITERAL1 +DSHOT_CMD_BEACON4 LITERAL1 +DSHOT_CMD_BEACON5 LITERAL1 +DSHOT_CMD_ESC_INFO LITERAL1 +DSHOT_CMD_SPIN_DIRECTION_1 LITERAL1 +DSHOT_CMD_SPIN_DIRECTION_2 LITERAL1 +DSHOT_CMD_3D_MODE_OFF LITERAL1 +DSHOT_CMD_3D_MODE_ON LITERAL1 +DSHOT_CMD_SETTINGS_REQUEST LITERAL1 +DSHOT_CMD_SAVE_SETTINGS LITERAL1 +DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE LITERAL1 +DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE LITERAL1 +DSHOT_CMD_SPIN_DIRECTION_NORMAL LITERAL1 +DSHOT_CMD_SPIN_DIRECTION_REVERSED LITERAL1 +DSHOT_CMD_LED0_ON LITERAL1 +DSHOT_CMD_LED1_ON LITERAL1 +DSHOT_CMD_LED2_ON LITERAL1 +DSHOT_CMD_LED3_ON LITERAL1 +DSHOT_CMD_LED0_OFF LITERAL1 +DSHOT_CMD_LED1_OFF LITERAL1 +DSHOT_CMD_LED2_OFF LITERAL1 +DSHOT_CMD_LED3_OFF LITERAL1 +DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF LITERAL1 +DSHOT_CMD_SILENT_MODE_ON_OFF LITERAL1 DSHOT_CMD_MAX LITERAL1 +# DShot Command Types DSHOT_CMD_TYPE_INLINE LITERAL1 -DSHOT_CMD_TYPE_BLOCKING LITERAL1 \ No newline at end of file +DSHOT_CMD_TYPE_BLOCKING LITERAL1 + +# Protocol Constants +DSHOT_BITS_PER_FRAME LITERAL1 +DSHOT_SWITCH_TIME LITERAL1 +DSHOT_NULL_PACKET LITERAL1 +DSHOT_RX_TIMEOUT_MS LITERAL1 +GCR_BITS_PER_FRAME LITERAL1 + +# RMT Constants +DSHOT_CLOCK_SRC_DEFAULT LITERAL1 +DSHOT_RMT_RESOLUTION LITERAL1 +RMT_BUFFER_SIZE LITERAL1 +RMT_BUFFER_SYMBOLS LITERAL1 +RMT_QUEUE_DEPTH LITERAL1 +DSHOT_PULSE_MIN LITERAL1 +DSHOT_PULSE_MAX LITERAL1 + +# Status Constants +DSHOT_OK LITERAL1 +DSHOT_ERROR LITERAL1 diff --git a/library.properties b/library.properties index 23c07ff..5e9ba5a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DShotRMT -version=0.6.6 +version=0.7.0 author=derdoktor667 maintainer=derdoktor667 sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.