Merge pull request #23 from derdoktor667/dev

...preparing release 0.7.1
This commit is contained in:
Wastl Kraus 2025-09-05 12:23:10 +02:00 committed by GitHub
commit 31b3dbdf56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 506 additions and 303 deletions

View File

@ -13,43 +13,42 @@ DShotCommandManager::DShotCommandManager(DShotRMT &dshot_instance)
: _dshot(dshot_instance), : _dshot(dshot_instance),
_total_commands_sent(0), _total_commands_sent(0),
_failed_commands(0), _failed_commands(0),
_last_execution_time_us(0),
_last_command_timestamp(0) _last_command_timestamp(0)
{ {
} }
// Init command manager // Init command manager
bool DShotCommandManager::begin() dshot_result_t DShotCommandManager::begin()
{ {
resetStatistics(); dshot_result_t result;
return true; result.success = true;
result.error_message = "Success";
return result;
} }
// --- BASIC COMMAND METHODS --- // --- BASIC COMMAND METHODS ---
dshot_command_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16_t repeat_count) dshot_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16_t repeat_count)
{ {
return sendCommandWithDelay(command, repeat_count, DEFAULT_COMMAND_DELAY_MS); 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_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"}; dshot_result_t result = {false, "Unknown error"};
if (!isValidCommand(command)) if (!isValidCommand(command))
{ {
result.error_message = "Invalid command"; result.error_message = "Invalid command";
_updateStatistics(false, 0);
return result; return result;
} }
uint64_t start_time = esp_timer_get_time();
bool all_successful = true; bool all_successful = true;
// Send command multiple times with delay // Send command multiple times with delay
for (uint16_t i = 0; i < repeat_count; i++) for (uint16_t i = 0; i < repeat_count; i++)
{ {
dshot_command_result_t single_result = _executeCommand(command); dshot_result_t single_result = _executeCommand(command);
if (!single_result.success) if (!single_result.success)
{ {
@ -61,12 +60,11 @@ dshot_command_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_
// Add delay between repetitions (except for last repetition) // Add delay between repetitions (except for last repetition)
if (i < repeat_count - 1) if (i < repeat_count - 1)
{ {
_delay(delay_ms); _delay_ms(delay_ms);
} }
} }
uint64_t end_time = esp_timer_get_time(); //
result.execution_time_us = (uint32_t)(end_time - start_time);
result.success = all_successful; result.success = all_successful;
if (result.success) if (result.success)
@ -74,57 +72,54 @@ dshot_command_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_
result.error_message = "Success"; result.error_message = "Success";
} }
_updateStatistics(result.success, result.execution_time_us);
return result; return result;
} }
// --- MOTOR CONTROL COMMANDS --- // --- MOTOR CONTROL COMMANDS ---
dshot_command_result_t DShotCommandManager::stopMotor() dshot_result_t DShotCommandManager::stopMotor()
{ {
return sendCommand(DSHOT_CMD_MOTOR_STOP); return sendCommand(DSHOT_CMD_MOTOR_STOP);
} }
// //
dshot_command_result_t DShotCommandManager::set3DMode(bool enable) dshot_result_t DShotCommandManager::set3DMode(bool enable)
{ {
dshot_commands_t command = enable ? DSHOT_CMD_3D_MODE_ON : DSHOT_CMD_3D_MODE_OFF; 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); return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
} }
// //
dshot_command_result_t DShotCommandManager::setSpinDirection(bool reversed) dshot_result_t DShotCommandManager::setSpinDirection(bool reversed)
{ {
dshot_commands_t command = reversed ? DSHOT_CMD_SPIN_DIRECTION_REVERSED : DSHOT_CMD_SPIN_DIRECTION_NORMAL; 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); return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
} }
// //
dshot_command_result_t DShotCommandManager::saveSettings() dshot_result_t DShotCommandManager::saveSettings()
{ {
return sendCommandWithDelay(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS); return sendCommandWithDelay(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
} }
// --- TELEMETRY COMMANDS --- // --- TELEMETRY COMMANDS ---
dshot_command_result_t DShotCommandManager::setExtendedTelemetry(bool enable) dshot_result_t DShotCommandManager::setExtendedTelemetry(bool enable)
{ {
dshot_commands_t command = enable ? DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE : DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE; dshot_commands_t command = enable ? DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE : DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE;
return sendCommand(command); return sendCommand(command);
} }
// //
dshot_command_result_t DShotCommandManager::requestESCInfo() dshot_result_t DShotCommandManager::requestESCInfo()
{ {
return sendCommand(DSHOT_CMD_ESC_INFO); return sendCommand(DSHOT_CMD_ESC_INFO);
} }
// --- LED CONTROL COMMANDS --- // --- LED CONTROL COMMANDS ---
dshot_command_result_t DShotCommandManager::setLED(uint8_t led_number, bool state) dshot_result_t DShotCommandManager::setLED(uint8_t led_number, bool state)
{ {
if (led_number > 3) if (led_number > 3)
{ {
dshot_command_result_t result = {false, 0, "Invalid LED number (0-3)"}; dshot_result_t result = {false, "Invalid LED number (0-3)"};
_updateStatistics(false, 0);
return result; return result;
} }
@ -172,12 +167,11 @@ dshot_command_result_t DShotCommandManager::setLED(uint8_t led_number, bool stat
} }
// --- BEACON COMMANDS --- // --- BEACON COMMANDS ---
dshot_command_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number) dshot_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number)
{ {
if (beacon_number < 1 || beacon_number > 5) if (beacon_number < 1 || beacon_number > 5)
{ {
dshot_command_result_t result = {false, 0, "Invalid beacon number (1-5)"}; dshot_result_t result = {false, "Invalid beacon number (1-5)"};
_updateStatistics(false, 0);
return result; return result;
} }
@ -186,28 +180,28 @@ dshot_command_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number
} }
// --- KISS ESC SPECIFIC COMMANDS --- // --- KISS ESC SPECIFIC COMMANDS ---
dshot_command_result_t DShotCommandManager::setAudioStreamMode(bool enable) dshot_result_t DShotCommandManager::setAudioStreamMode(bool enable)
{ {
// KISS audio stream mode is a toggle command // KISS audio stream mode is a toggle command
return sendCommand(DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF); return sendCommand(DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF);
} }
// //
dshot_command_result_t DShotCommandManager::setSilentMode(bool enable) dshot_result_t DShotCommandManager::setSilentMode(bool enable)
{ {
// KISS silent mode is a toggle command // KISS silent mode is a toggle command
return sendCommand(DSHOT_CMD_SILENT_MODE_ON_OFF); return sendCommand(DSHOT_CMD_SILENT_MODE_ON_OFF);
} }
// --- SEQUENCE COMMANDS --- // --- SEQUENCE COMMANDS ---
dshot_command_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length) dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length)
{ {
dshot_command_result_t result = {true, 0, "Success"}; dshot_result_t result = {true, "Success"};
uint64_t total_start_time = esp_timer_get_time(); uint64_t total_start_time = esp_timer_get_time();
for (size_t i = 0; i < sequence_length; i++) for (size_t i = 0; i < sequence_length; i++)
{ {
dshot_command_result_t item_result = sendCommandWithDelay( dshot_result_t item_result = sendCommandWithDelay(
sequence[i].command, sequence[i].command,
sequence[i].repeat_count, sequence[i].repeat_count,
DEFAULT_COMMAND_DELAY_MS); DEFAULT_COMMAND_DELAY_MS);
@ -222,18 +216,17 @@ dshot_command_result_t DShotCommandManager::executeSequence(const dshot_command_
// Add delay after command if specified // Add delay after command if specified
if (sequence[i].delay_ms > 0) if (sequence[i].delay_ms > 0)
{ {
_delay(sequence[i].delay_ms); _delay_ms(sequence[i].delay_ms);
} }
} }
uint64_t total_end_time = esp_timer_get_time(); uint64_t total_end_time = esp_timer_get_time();
result.execution_time_us = (uint32_t)(total_end_time - total_start_time);
return result; return result;
} }
// //
dshot_command_result_t DShotCommandManager::executeInitSequence() dshot_result_t DShotCommandManager::executeInitSequence()
{ {
// Basic ESC initialization sequence // Basic ESC initialization sequence
dshot_command_sequence_item_t init_sequence[] = { dshot_command_sequence_item_t init_sequence[] = {
@ -246,7 +239,7 @@ dshot_command_result_t DShotCommandManager::executeInitSequence()
} }
// //
dshot_command_result_t DShotCommandManager::executeCalibrationSequence() dshot_result_t DShotCommandManager::executeCalibrationSequence()
{ {
// Basic ESC calibration sequence // Basic ESC calibration sequence
dshot_command_sequence_item_t calibration_sequence[] = { dshot_command_sequence_item_t calibration_sequence[] = {
@ -330,64 +323,25 @@ bool DShotCommandManager::isValidCommand(dshot_commands_t command)
return (command >= DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX); 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 --- // --- PRIVATE METHODS ---
dshot_command_result_t DShotCommandManager::_executeCommand(dshot_commands_t command) dshot_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(); uint64_t start_time = esp_timer_get_time();
// Execute the command using the DShotRMT instance // Execute the command using the DShotRMT instance
bool success = _dshot.sendCommand(static_cast<uint16_t>(command)); dshot_result_t result = _dshot.sendCommand(static_cast<uint16_t>(command));
uint64_t end_time = esp_timer_get_time(); 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; _last_command_timestamp = end_time;
return result; return result;
} }
// //
void DShotCommandManager::_delay(uint32_t delay_ms) void DShotCommandManager::_delay_ms(uint32_t delay_ms)
{ {
if (delay_ms > 0) if (delay_ms > 0)
{ {
delay(delay_ms); 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;
}

View File

@ -12,17 +12,6 @@
#include <DShotRMT.h> #include <DShotRMT.h>
#include <dshot_commands.h> #include <dshot_commands.h>
// 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 // Command sequence item
typedef struct typedef struct
{ {
@ -39,63 +28,61 @@ public:
explicit DShotCommandManager(DShotRMT &dshot_instance); explicit DShotCommandManager(DShotRMT &dshot_instance);
// Initialize command manager // Initialize command manager
bool begin(); dshot_result_t begin();
bool printMenu(Stream &output);
void handleMenuInput(const String &input, Stream &output = Serial); void handleMenuInput(const String &input, Stream &output = Serial);
// Send a single DShot command // Send a single DShot command
dshot_command_result_t sendCommand(dshot_commands_t command, uint16_t repeat_count = 1); dshot_result_t sendCommand(dshot_commands_t command, uint16_t repeat_count = 1);
// Send command with specified delay between repetitions // Send command with specified delay between repetitions
dshot_command_result_t sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms); dshot_result_t sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms);
// --- MOTOR CONTROL COMMANDS --- // --- MOTOR CONTROL COMMANDS ---
// Stop motor (send MOTOR_STOP command) // Stop motor (send MOTOR_STOP command)
dshot_command_result_t stopMotor(); dshot_result_t stopMotor();
// Enable/disable 3D mode // Enable/disable 3D mode
dshot_command_result_t set3DMode(bool enable); dshot_result_t set3DMode(bool enable);
// Set motor spin direction // Set motor spin direction
dshot_command_result_t setSpinDirection(bool reversed); dshot_result_t setSpinDirection(bool reversed);
// Save current settings to ESC // Save current settings to ESC
dshot_command_result_t saveSettings(); dshot_result_t saveSettings();
// --- TELEMETRY COMMANDS --- // --- TELEMETRY COMMANDS ---
// Enable/disable extended telemetry // Enable/disable extended telemetry
dshot_command_result_t setExtendedTelemetry(bool enable); dshot_result_t setExtendedTelemetry(bool enable);
// Request ESC information // Request ESC information
dshot_command_result_t requestESCInfo(); dshot_result_t requestESCInfo();
// --- LED CONTROL COMMANDS (BLHeli32 only) --- // --- LED CONTROL COMMANDS (BLHeli32 only) ---
// Control ESC LEDs (BLHeli32 only) // Control ESC LEDs (BLHeli32 only)
dshot_command_result_t setLED(uint8_t led_number, bool state); dshot_result_t setLED(uint8_t led_number, bool state);
// --- BEACON COMMANDS --- // --- BEACON COMMANDS ---
// Activate beacon (motor beeping) // Activate beacon (motor beeping)
dshot_command_result_t activateBeacon(uint8_t beacon_number); dshot_result_t activateBeacon(uint8_t beacon_number);
// --- KISS ESC SPECIFIC COMMANDS --- // --- KISS ESC SPECIFIC COMMANDS ---
// Enable/disable audio stream mode (KISS ESCs) // Enable/disable audio stream mode (KISS ESCs)
dshot_command_result_t setAudioStreamMode(bool enable); dshot_result_t setAudioStreamMode(bool enable);
// Enable/disable silent mode (KISS ESCs) // Enable/disable silent mode (KISS ESCs)
dshot_command_result_t setSilentMode(bool enable); dshot_result_t setSilentMode(bool enable);
// --- SEQUENCE COMMANDS --- // --- SEQUENCE COMMANDS ---
// Execute a sequence of DShot commands // Execute a sequence of DShot commands
dshot_command_result_t executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length); dshot_result_t executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length);
// Execute ESC initialization sequence // Execute ESC initialization sequence
dshot_command_result_t executeInitSequence(); dshot_result_t executeInitSequence();
// Execute ESC calibration sequence // Execute ESC calibration sequence
dshot_command_result_t executeCalibrationSequence(); dshot_result_t executeCalibrationSequence();
// --- UTILITY METHODS --- // --- UTILITY METHODS ---
// Get command name as string // Get command name as string
@ -104,12 +91,6 @@ public:
// Check if command is valid // Check if command is valid
static bool isValidCommand(dshot_commands_t command); static bool isValidCommand(dshot_commands_t command);
// Print command execution statistics
void printStatistics(Stream &output = Serial) const;
// Reset execution statistics
void resetStatistics();
// --- GETTERS --- // --- GETTERS ---
// Get total number of commands sent // Get total number of commands sent
uint32_t getTotalCommandCount() const { return _total_commands_sent; } uint32_t getTotalCommandCount() const { return _total_commands_sent; }
@ -117,26 +98,23 @@ public:
// Get number of failed commands // Get number of failed commands
uint32_t getFailedCommandCount() const { return _failed_commands; } uint32_t getFailedCommandCount() const { return _failed_commands; }
// Get last command execution time // Get reference to underlying DShotRMT instance
uint32_t getLastExecutionTime() const { return _last_execution_time_us; } DShotRMT &getDShotRMT() { return _dshot; }
const DShotRMT &getDShotRMT() const { return _dshot; }
private: private:
// --- PRIVATE MEMBERS --- // --- PRIVATE MEMBERS ---
DShotRMT &_dshot; // Reference to DShotRMT instance DShotRMT &_dshot; // Reference to DShotRMT instance
uint32_t _total_commands_sent; // Total commands sent counter uint32_t _total_commands_sent; // Total commands sent counter
uint32_t _failed_commands; // Failed commands 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 uint64_t _last_command_timestamp; // Timestamp of last command
// --- PRIVATE METHODS --- // --- PRIVATE METHODS ---
// Execute single command with timing // Execute single command with timing
dshot_command_result_t _executeCommand(dshot_commands_t command); dshot_result_t _executeCommand(dshot_commands_t command);
// Wait for specified delay // Wait for specified delay
void _delay(uint32_t delay_ms); void _delay_ms(uint32_t delay_ms);
// Update execution statistics
void _updateStatistics(bool success, uint32_t execution_time_us);
// --- CONSTANTS --- // --- CONSTANTS ---
static constexpr uint32_t DEFAULT_COMMAND_DELAY_MS = 10; static constexpr uint32_t DEFAULT_COMMAND_DELAY_MS = 10;

View File

@ -29,6 +29,8 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
_last_erpm(0), _last_erpm(0),
_parsed_packet(0), _parsed_packet(0),
_packet{0}, _packet{0},
_total_transmissions(0),
_failed_transmissions(0),
_rmt_tx_channel(nullptr), _rmt_tx_channel(nullptr),
_rmt_rx_channel(nullptr), _rmt_rx_channel(nullptr),
_dshot_encoder(nullptr), _dshot_encoder(nullptr),
@ -90,33 +92,43 @@ DShotRMT::~DShotRMT()
} }
// Initialize DShotRMT // Initialize DShotRMT
uint16_t DShotRMT::begin() dshot_result_t DShotRMT::begin()
{ {
dshot_result_t result = {false, UNKNOWN_ERROR};
uint64_t start_time = esp_timer_get_time();
// Init RX channel first // Init RX channel first
if (_is_bidirectional) if (_is_bidirectional)
{ {
if (!_initRXChannel()) if (!_initRXChannel())
{ {
result.error_message = RX_INIT_FAILED;
_dshot_log(RX_INIT_FAILED); _dshot_log(RX_INIT_FAILED);
return DSHOT_ERROR; return result;
} }
} }
// Init TX channel // Init TX channel
if (!_initTXChannel()) if (!_initTXChannel())
{ {
result.error_message = TX_INIT_FAILED;
_dshot_log(TX_INIT_FAILED); _dshot_log(TX_INIT_FAILED);
return DSHOT_ERROR; return result;
} }
// Init DShot encoder // Init DShot encoder
if (_initDShotEncoder() != DSHOT_OK) if (_initDShotEncoder() != DSHOT_OK)
{ {
result.error_message = ENCODER_INIT_FAILED;
_dshot_log(ENCODER_INIT_FAILED); _dshot_log(ENCODER_INIT_FAILED);
return DSHOT_ERROR; return result;
} }
return DSHOT_OK; uint64_t end_time = esp_timer_get_time();
result.success = true;
result.error_message = INIT_SUCCESS;
return result;
} }
// Init RMT TX channel // Init RMT TX channel
@ -153,7 +165,7 @@ bool DShotRMT::_initRXChannel()
return DSHOT_ERROR; return DSHOT_ERROR;
} }
// Config RMT RX // Config RMT RX
_rx_channel_config.gpio_num = _gpio; _rx_channel_config.gpio_num = _gpio;
_rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
_rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
@ -212,9 +224,10 @@ bool DShotRMT::_initDShotEncoder()
} }
// Send throttle value // Send throttle value
bool DShotRMT::sendThrottle(uint16_t throttle) dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
{ {
static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP; static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP;
dshot_result_t result = {false, UNKNOWN_ERROR};
// Special case: if throttle is 0, use sendCommand() instead // Special case: if throttle is 0, use sendCommand() instead
if (throttle == 0) if (throttle == 0)
@ -226,6 +239,7 @@ bool DShotRMT::sendThrottle(uint16_t throttle)
if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle) if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle)
{ {
_dshot_log(THROTTLE_NOT_IN_RANGE); _dshot_log(THROTTLE_NOT_IN_RANGE);
result.error_message = THROTTLE_NOT_IN_RANGE;
} }
// Always store the original throttle value // Always store the original throttle value
@ -234,17 +248,21 @@ bool DShotRMT::sendThrottle(uint16_t throttle)
// Constrain throttle for transmission and send // Constrain throttle for transmission and send
uint16_t new_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); uint16_t new_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
_packet = _buildDShotPacket(new_throttle); _packet = _buildDShotPacket(new_throttle);
return _sendDShotFrame(_packet); return _sendDShotFrame(_packet);
} }
// Send DShot command to ESC // Send DShot command to ESC
bool DShotRMT::sendCommand(uint16_t command) dshot_result_t DShotRMT::sendCommand(uint16_t command)
{ {
dshot_result_t result = {false, UNKNOWN_ERROR};
// Validate command is within DShot specification range // Validate command is within DShot specification range
if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX) if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX)
{ {
_dshot_log(COMMAND_NOT_VALID); _dshot_log(COMMAND_NOT_VALID);
return DSHOT_ERROR; result.error_message = COMMAND_NOT_VALID;
return result;
} }
// Build packet and transmit // Build packet and transmit
@ -252,7 +270,41 @@ bool DShotRMT::sendCommand(uint16_t command)
return _sendDShotFrame(_packet); return _sendDShotFrame(_packet);
} }
// Get RPM from ESC (bidirectional mode only) // Get telemetry data with timing and error handling
dshot_telemetry_result_t DShotRMT::getTelemetry(uint8_t magnet_count)
{
dshot_telemetry_result_t result = {false, 0, 0, UNKNOWN_ERROR};
// Check if bidirectional mode is enabled
if (!_is_bidirectional)
{
result.error_message = BIDIR_NOT_ENABLED;
_dshot_log(BIDIR_NOT_ENABLED);
return result;
}
// Get eRPM
uint16_t erpm = getERPM();
if (erpm == DSHOT_NULL_PACKET)
{
result.error_message = TELEMETRY_TIMEOUT;
return result;
}
// Calculate motor RPM
uint8_t pole_pairs = max(1, magnet_count / 2);
uint32_t motor_rpm = erpm / pole_pairs;
result.success = true;
result.erpm = erpm;
result.motor_rpm = motor_rpm;
result.error_message = TELEMETRY_SUCCESS;
return result;
}
// Get RPM from ESC (bidirectional mode only) - backward compatibility
uint16_t DShotRMT::getERPM() uint16_t DShotRMT::getERPM()
{ {
// Check if bidirectional mode is enabled // Check if bidirectional mode is enabled
@ -278,13 +330,6 @@ uint16_t DShotRMT::getERPM()
return _last_erpm; return _last_erpm;
} }
// Convert eRPM to actual motor RPM
uint32_t DShotRMT::getMotorRPM(uint8_t magnet_count)
{
uint8_t pole_pairs = max(1, magnet_count / 2);
return getERPM() / pole_pairs;
}
// Build a complete DShot packet // Build a complete DShot packet
dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value)
{ {
@ -295,7 +340,7 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value)
if (value > DSHOT_THROTTLE_MAX) if (value > DSHOT_THROTTLE_MAX)
{ {
_dshot_log(PACKET_BUILD_ERROR); _dshot_log(PACKET_BUILD_ERROR);
// Something is really wrong // Something is really wrong
return packet; return packet;
} }
@ -303,10 +348,10 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value)
// Build packet // Build packet
packet.throttle_value = value & 0b0000011111111111; packet.throttle_value = value & 0b0000011111111111;
packet.telemetric_request = _is_bidirectional ? 1 : 0; packet.telemetric_request = _is_bidirectional ? 1 : 0;
// CRC is calculated over 11bit // CRC is calculated over 11bit
uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request;
packet.checksum = _calculateCRC(data); packet.checksum = _calculateCRC(data);
return packet; return packet;
@ -338,21 +383,28 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data)
} }
// Transmit DShot packet via RMT // Transmit DShot packet via RMT
bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
{ {
dshot_result_t result = {false, UNKNOWN_ERROR};
uint64_t start_time = esp_timer_get_time();
// Check timing requirements // Check timing requirements
if (!_timer_signal()) if (!_timer_signal())
{ {
return DSHOT_ERROR; return result;
} }
// Enable RMT RX before RMT TX // Enable RMT RX before RMT TX
if (_is_bidirectional) if (_is_bidirectional)
{ {
// Performance reasons // Performance reasons
rmt_symbol_word_t rx_symbols[DSHOT_BITS_PER_FRAME]; rmt_symbol_word_t rx_symbols[DSHOT_BITS_PER_FRAME];
rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config); if (rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config) != DSHOT_OK)
{
result.error_message = RX_RMT_RECEIVER_ERROR;
return result;
}
} }
// Local for performance // Local for performance
@ -370,16 +422,18 @@ bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
// Disable RMT RX for sending // Disable RMT RX for sending
if (rmt_disable(_rmt_rx_channel) != DSHOT_OK) if (rmt_disable(_rmt_rx_channel) != DSHOT_OK)
{ {
result.error_message = RX_RMT_RECEIVER_ERROR;
_dshot_log(RX_RMT_RECEIVER_ERROR); _dshot_log(RX_RMT_RECEIVER_ERROR);
return DSHOT_ERROR; return result;
} }
} }
// Perform RMT transmission // 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, &_transmit_config) != DSHOT_OK)
{ {
result.error_message = TRANSMITTER_ERROR;
_dshot_log(TRANSMITTER_ERROR); _dshot_log(TRANSMITTER_ERROR);
return DSHOT_ERROR; return result;
} }
// Re-enable RMT RX // Re-enable RMT RX
@ -387,15 +441,20 @@ bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
{ {
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
{ {
result.error_message = RX_RMT_RECEIVER_ERROR;
_dshot_log(RX_RMT_RECEIVER_ERROR); _dshot_log(RX_RMT_RECEIVER_ERROR);
return DSHOT_ERROR; return result;
} }
} }
// Update timestamp and return success // Update timestamp and calculate execution time
_timer_reset(); _timer_reset();
uint64_t end_time = esp_timer_get_time();
return DSHOT_OK; result.success = true;
result.error_message = TRANSMISSION_SUCCESS;
return result;
} }
// Encode DShot packet into RMT symbol format (placed in IRAM for performance) // Encode DShot packet into RMT symbol format (placed in IRAM for performance)
@ -492,9 +551,9 @@ void DShotRMT::printDShotInfo(Stream &output) const
// Current DShot mode // Current DShot mode
output.printf("Current Mode: DSHOT%d\n", output.printf("Current Mode: DSHOT%d\n",
_mode == DSHOT150 ? 150 : _mode == DSHOT150 ? 150 :
_mode == DSHOT300 ? 300 : _mode == DSHOT300 ? 300 :
_mode == DSHOT600 ? 600 : _mode == DSHOT600 ? 600 :
_mode == DSHOT1200 ? 1200 : 0); _mode == DSHOT1200 ? 1200 : 0);
output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO"); output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO");

View File

@ -65,14 +65,31 @@ typedef struct
uint16_t ticks_zero_low; uint16_t ticks_zero_low;
} dshot_timing_t; } dshot_timing_t;
// Command execution result structure
typedef struct
{
bool success;
const char *error_message;
} dshot_result_t;
// DShot telemetry result structure
typedef struct
{
bool success;
uint16_t erpm;
uint32_t motor_rpm;
const char *error_message;
} dshot_telemetry_result_t;
// Naming convention
typedef dshotCommands_e dshot_commands_t;
// //
class DShotRMT class DShotRMT
{ {
public: public:
// Constructor with GPIO enum // Constructor with GPIO enum
explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false);
dshot_mode_t mode = DSHOT300,
bool is_bidirectional = false);
// Constructor with pin number // Constructor with pin number
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional); DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional);
@ -81,24 +98,29 @@ public:
~DShotRMT(); ~DShotRMT();
// Initialize the RMT module and DShot config // Initialize the RMT module and DShot config
uint16_t begin(); dshot_result_t begin();
// Send throttle value (48-2047) // Send throttle value (48-2047)
bool sendThrottle(uint16_t throttle); dshot_result_t sendThrottle(uint16_t throttle);
// Send DShot command (0-47) // Send DShot command (0-47)
bool sendCommand(uint16_t command); dshot_result_t sendCommand(uint16_t command);
// Get telemetry data (bidirectional mode only) // Get telemetry data (bidirectional mode only)
dshot_telemetry_result_t getTelemetry(uint8_t magnet_count = 14);
// Get eRPM only (bidirectional mode only)
uint16_t getERPM(); uint16_t getERPM();
// Convert eRPM to motor RPM // --- GETTERS ---
uint32_t getMotorRPM(uint8_t magnet_count);
//
gpio_num_t getGPIO() const { return _gpio; } gpio_num_t getGPIO() const { return _gpio; }
uint16_t getDShotPacket() const { return _parsed_packet; } uint16_t getDShotPacket() const { return _parsed_packet; }
bool is_bidirectional() const { return _is_bidirectional; } bool is_bidirectional() const { return _is_bidirectional; }
dshot_mode_t getMode() const { return _mode; }
// Get execution statistics
uint32_t getTotalTransmissions() const { return _total_transmissions; }
uint32_t getFailedTransmissions() const { return _failed_transmissions; }
// --- INFO --- // --- INFO ---
void printDShotInfo(Stream &output = Serial) const; void printDShotInfo(Stream &output = Serial) const;
@ -106,10 +128,25 @@ public:
// --- DEPRECATED METHODS --- // --- DEPRECATED METHODS ---
[[deprecated("Use sendThrottle() instead")]] [[deprecated("Use sendThrottle() instead")]]
bool setThrottle(uint16_t throttle) { return sendThrottle(throttle); } bool setThrottle(uint16_t throttle)
{
auto result = sendThrottle(throttle);
return result.success;
}
[[deprecated("Use sendCommand() instead")]] [[deprecated("Use sendCommand() instead")]]
bool sendDShotCommand(uint16_t command) { return sendCommand(command); } bool sendDShotCommand(uint16_t command)
{
auto result = sendCommand(command);
return result.success;
}
[[deprecated("Use getTelemetry() instead")]]
uint32_t getMotorRPM(uint8_t magnet_count)
{
auto result = getTelemetry(magnet_count);
return result.success;
}
private: private:
// --- CONFIG --- // --- CONFIG ---
@ -125,6 +162,10 @@ private:
uint16_t _parsed_packet; uint16_t _parsed_packet;
dshot_packet_t _packet; dshot_packet_t _packet;
// --- STATISTICS ---
uint32_t _total_transmissions;
uint32_t _failed_transmissions;
// --- RMT HARDWARE HANDLES --- // --- RMT HARDWARE HANDLES ---
rmt_channel_handle_t _rmt_tx_channel; rmt_channel_handle_t _rmt_tx_channel;
rmt_channel_handle_t _rmt_rx_channel; rmt_channel_handle_t _rmt_rx_channel;
@ -147,7 +188,7 @@ private:
uint16_t _calculateCRC(const uint16_t data); uint16_t _calculateCRC(const uint16_t data);
// --- FRAME PROCESSING --- // --- FRAME PROCESSING ---
bool _sendDShotFrame(const dshot_packet_t &packet); dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
@ -164,13 +205,14 @@ private:
static constexpr auto DSHOT_TELEMETRY_INVALID = (0xffff); static constexpr auto DSHOT_TELEMETRY_INVALID = (0xffff);
// --- ERROR HANDLING & LOGGING --- // --- ERROR HANDLING & LOGGING ---
void _dshot_log(const char *msg, Stream &output = Serial) { output.println(msg); } void _dshot_log(const char *msg, Stream &output = Serial) const { output.println(msg); }
// --- CONSTANTS & ERROR MESSAGES --- // --- CONSTANTS & ERROR MESSAGES ---
static constexpr bool DSHOT_OK = 0; static constexpr bool DSHOT_OK = 0;
static constexpr bool DSHOT_ERROR = 1; static constexpr bool DSHOT_ERROR = 1;
static constexpr char *NEW_LINE = " "; static constexpr char *NEW_LINE = " ";
static constexpr char *UNKNOWN_ERROR = "Unknown Error!";
static constexpr char *TX_INIT_FAILED = "Failed to initialize TX channel!"; static constexpr char *TX_INIT_FAILED = "Failed to initialize TX channel!";
static constexpr char *RX_INIT_FAILED = "Failed to initialize RX channel!"; static constexpr char *RX_INIT_FAILED = "Failed to initialize RX channel!";
static constexpr char *ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!"; static constexpr char *ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!";
@ -181,4 +223,8 @@ private:
static constexpr char *RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!"; 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 *PACKET_BUILD_ERROR = "Value too big for DShot Packet!";
static constexpr char *TRANSMITTER_ERROR = "RMT TX Transmitter Error!"; static constexpr char *TRANSMITTER_ERROR = "RMT TX Transmitter Error!";
static constexpr char *INIT_SUCCESS = "DShotRMT initialized successfully";
static constexpr char *TELEMETRY_SUCCESS = "Telemetry read successfully";
static constexpr char *TELEMETRY_TIMEOUT = "Telemetry read timeout";
static constexpr char *TRANSMISSION_SUCCESS = "Transmission successful";
}; };

View File

@ -1,11 +1,9 @@
/* /*
* command_manager_example.ino * command_manager.ino
* Example sketch demonstrating DShotCommandManager usage * Example sketch for DShotCommandManager
* Author: Wastl Kraus * Author: Wastl Kraus
* Date: 2025-09-04 * Date: 2025-09-04
* License: MIT * License: MIT
*
* Modified by Especiallist to support continuous throttle sending.
*/ */
#include <Arduino.h> #include <Arduino.h>
@ -28,7 +26,20 @@ DShotRMT motor01(MOTOR01_PIN, DSHOT300, IS_BIDIRECTIONAL);
DShotCommandManager commandManager(motor01); DShotCommandManager commandManager(motor01);
// Global variable to store the desired continuous throttle value // Global variable to store the desired continuous throttle value
static volatile uint16_t throttle_now = NULL; static volatile uint16_t throttle_now = 0;
// Helper function to print telemetry results
void printTelemetryResult(const dshot_telemetry_result_t &result)
{
if (result.success)
{
USB_SERIAL.printf("Telemetry: eRPM=%u, Motor RPM=%u\n", result.erpm, result.motor_rpm);
}
else
{
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.error_message);
}
}
// //
void setup() void setup()
@ -36,25 +47,11 @@ void setup()
// Start USB Serial Port // Start USB Serial Port
USB_SERIAL.begin(USB_SERIAL_BAUD); USB_SERIAL.begin(USB_SERIAL_BAUD);
USB_SERIAL.println("=== DShotRMT Command Manager Example ===");
// Initialize DShot // Initialize DShot
if (motor01.begin() != 0) motor01.begin();
{
USB_SERIAL.println("ERROR: Failed to initialize DShotRMT!");
while (1)
delay(1000);
}
// Init Command Manager // Init Command Manager
if (!commandManager.begin()) commandManager.begin();
{
USB_SERIAL.println("ERROR: Failed to initialize DShotCommandManager!");
while (1)
delay(1000);
}
USB_SERIAL.println("Initialization successful!");
// Print Menu // Print Menu
printMenu(); printMenu();
@ -75,9 +72,15 @@ void loop()
} }
// Continuously send the stored throttle value // Continuously send the stored throttle value
if (throttle_now != NULL) if (throttle_now != 0)
{ {
motor01.sendThrottle(throttle_now); dshot_result_t result = motor01.sendThrottle(throttle_now);
// Only print errors to avoid spam
if (!result.success)
{
printResult(result);
}
// Print motor stats every 2 seconds // Print motor stats every 2 seconds
if (esp_timer_get_time() - last_stats_print >= 2000000) if (esp_timer_get_time() - last_stats_print >= 2000000)
@ -87,8 +90,8 @@ void loop()
// Get Motor RPM // Get Motor RPM
if (IS_BIDIRECTIONAL) if (IS_BIDIRECTIONAL)
{ {
uint32_t rpm = motor01.getMotorRPM(MOTOR01_MAGNET_COUNT); dshot_telemetry_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
USB_SERIAL.printf("Motor RPM: %u\n", rpm); printTelemetryResult(telem_result);
} }
// Time Stamp // Time Stamp
@ -100,92 +103,64 @@ void loop()
// //
void handleUserInput(const String &input) void handleUserInput(const String &input)
{ {
dshot_command_result_t result; dshot_result_t cmd_result;
if (input == "1") if (input == "1")
{ {
// Stop motor command should also reset the continuous throttle value // Stop motor command should also reset the continuous throttle value
throttle_now = 0; throttle_now = 0;
USB_SERIAL.print("Stopping motor... "); USB_SERIAL.print("Stopping motor... ");
result = commandManager.stopMotor(); cmd_result = commandManager.stopMotor();
printResult(result); printResult(cmd_result);
} }
else if (input == "2") else if (input == "2")
{ {
USB_SERIAL.print("Activating beacon 1... "); USB_SERIAL.print("Activating beacon 1... ");
result = commandManager.activateBeacon(1); cmd_result = commandManager.activateBeacon(1);
printResult(result); printResult(cmd_result);
} }
else if (input == "3") else if (input == "3")
{ {
USB_SERIAL.print("Setting normal spin direction... "); USB_SERIAL.print("Setting normal spin direction... ");
result = commandManager.setSpinDirection(false); cmd_result = commandManager.setSpinDirection(false);
printResult(result); printResult(cmd_result);
} }
else if (input == "4") else if (input == "4")
{ {
USB_SERIAL.print("Setting reversed spin direction... "); USB_SERIAL.print("Setting reversed spin direction... ");
result = commandManager.setSpinDirection(true); cmd_result = commandManager.setSpinDirection(true);
printResult(result); printResult(cmd_result);
} }
else if (input == "5") else if (input == "5")
{ {
USB_SERIAL.print("Enabling 3D mode... "); USB_SERIAL.print("Getting ESC Info... ");
result = commandManager.set3DMode(true); cmd_result = commandManager.requestESCInfo();
printResult(result); printResult(cmd_result);
} }
else if (input == "6") else if (input == "6")
{ {
USB_SERIAL.print("Disabling 3D mode... "); USB_SERIAL.print("Turning LED 0 ON... ");
result = commandManager.set3DMode(false); cmd_result = commandManager.setLED(0, true);
printResult(result); printResult(cmd_result);
} }
else if (input == "7") 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... "); USB_SERIAL.print("Turning LED 0 OFF... ");
result = commandManager.setLED(0, false); cmd_result = commandManager.setLED(0, false);
printResult(result); printResult(cmd_result);
} }
else if (input == "i") else if (input == "h" || input == "help")
{
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(); printMenu();
} }
else if (input == "help") else if (input == "info")
{ {
printMenu(); motor01.printDShotInfo();
}
else if (input == "rpm" && IS_BIDIRECTIONAL)
{
dshot_telemetry_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
printTelemetryResult(result);
} }
else if (input.startsWith("cmd ")) else if (input.startsWith("cmd "))
{ {
@ -195,12 +170,12 @@ void handleUserInput(const String &input)
{ {
USB_SERIAL.printf("Sending command %d (%s)... ", cmd_num, USB_SERIAL.printf("Sending command %d (%s)... ", cmd_num,
DShotCommandManager::getCommandName(static_cast<dshot_commands_t>(cmd_num))); DShotCommandManager::getCommandName(static_cast<dshot_commands_t>(cmd_num)));
result = commandManager.sendCommand(static_cast<dshot_commands_t>(cmd_num)); cmd_result = commandManager.sendCommand(static_cast<dshot_commands_t>(cmd_num));
printResult(result); printResult(cmd_result);
} }
else else
{ {
USB_SERIAL.printf("Invalid command number: %d (valid range: 0-%d)\n", cmd_num, DSHOT_CMD_MAX); USB_SERIAL.printf("Invalid command number: %d (valid range: 0 - %d)\n", cmd_num, DSHOT_CMD_MAX);
} }
} }
else if (input.startsWith("throttle ")) else if (input.startsWith("throttle "))
@ -211,65 +186,139 @@ void handleUserInput(const String &input)
{ {
throttle_now = throttle_value; throttle_now = throttle_value;
USB_SERIAL.printf("Setting continuous throttle to %d\n", throttle_now); USB_SERIAL.printf("Setting continuous throttle to %d\n", throttle_now);
// Send first throttle command and show result
dshot_result_t result = motor01.sendThrottle(throttle_now);
printResult(result);
if (result.success)
{
USB_SERIAL.println("Continuous throttle mode enabled. Send '0' or 'throttle 0' to stop.");
}
} }
else if (throttle_value == 0) else if (throttle_value == 0)
{ {
throttle_now = 0; throttle_now = 0;
USB_SERIAL.println("Continuous throttle stopped."); USB_SERIAL.println("Continuous throttle stopped.");
// Send stop command
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
printResult(result);
} }
else else
{ {
USB_SERIAL.printf("Invalid throttle value: %d (valid range: 48-2047, use 0 to stop)\n", throttle_value); USB_SERIAL.printf("Invalid throttle value: %d (valid range: %d-%d, use 0 to stop)\n",
throttle_value, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
}
}
else if (input == "0")
{
// Quick stop
throttle_now = 0;
USB_SERIAL.print("Emergency stop... ");
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
printResult(result);
}
else if (input.startsWith("repeat "))
{
// Repeat command: "repeat cmd 5 count 10" - sends command 5 ten times
String params = input.substring(7);
if (params.startsWith("cmd "))
{
int space_pos = params.indexOf(' ', 4);
if (space_pos > 0 && params.substring(space_pos + 1).startsWith("count "))
{
int cmd_num = params.substring(4, space_pos).toInt();
int repeat_count = params.substring(space_pos + 7).toInt();
if (DShotCommandManager::isValidCommand(static_cast<dshot_commands_t>(cmd_num)) &&
repeat_count > 0 && repeat_count <= 100)
{
USB_SERIAL.printf("Sending command %d (%s) %d times... ", cmd_num,
DShotCommandManager::getCommandName(static_cast<dshot_commands_t>(cmd_num)),
repeat_count);
cmd_result = commandManager.sendCommand(static_cast<dshot_commands_t>(cmd_num), repeat_count);
printResult(cmd_result);
}
else
{
USB_SERIAL.println("Invalid command or repeat count (1-100)");
}
}
else
{
USB_SERIAL.println("Usage: repeat cmd <number> count <repeat_count>");
}
}
else
{
USB_SERIAL.println("Usage: repeat cmd <number> count <repeat_count>");
} }
} }
else else
{ {
USB_SERIAL.println("Unknown command. Type 'h' for help."); USB_SERIAL.printf("Unknown command: '%s'. Type 'h' or 'help' for help.\n", input.c_str());
} }
} }
// //
void printResult(const dshot_command_result_t &result) void printResult(const dshot_result_t &result)
{ {
if (!result.success) if (result.success)
{ {
USB_SERIAL.printf("SUCCESS (%u us)\n", result.execution_time_us); USB_SERIAL.printf("SUCCESS\n");
} }
else else
{ {
USB_SERIAL.printf("FAILED - %s (%u us)\n", result.error_message, result.execution_time_us); USB_SERIAL.printf("FAILED - %s \n", result.error_message);
} }
} }
//
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);
}
// //
void printMenu() void printMenu()
{ {
USB_SERIAL.println("================================================"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println("\n=== DShot Command Manager Menu ==="); USB_SERIAL.println(" DShot Command Manager Menu ");
USB_SERIAL.println("Basic Commands:"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println(" 1 - Stop Motor"); USB_SERIAL.println(" 1 - Stop Motor");
USB_SERIAL.println(" 2 - Activate Beacon 1"); USB_SERIAL.println(" 2 - Activate Beacon 1");
USB_SERIAL.println(" 3 - Set Normal Spin Direction"); USB_SERIAL.println(" 3 - Set Normal Spin");
USB_SERIAL.println(" 4 - Set Reversed Spin Direction"); USB_SERIAL.println(" 4 - Set Reversed Spin");
USB_SERIAL.println(" 5 - Enable 3D Mode"); USB_SERIAL.println(" 5 - Get ESC Info");
USB_SERIAL.println(" 6 - Disable 3D Mode"); USB_SERIAL.println(" 6 - Turn LED 0 ON");
USB_SERIAL.println(" 7 - Save Settings"); USB_SERIAL.println(" 7 - Turn LED 0 OFF");
USB_SERIAL.println(" 8 - Turn LED 0 ON"); USB_SERIAL.println(" 0 - Emergency Stop");
USB_SERIAL.println(" 9 - Turn LED 0 OFF"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println(""); USB_SERIAL.println(" cmd <number> - Send Command (0 - 47)");
USB_SERIAL.println("Sequences:"); USB_SERIAL.println(" throttle <value> - Set throttle (48 - 2047)");
USB_SERIAL.println(" i - Execute Initialization Sequence"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println(" c - Execute Calibration Sequence"); USB_SERIAL.println(" info - Show DShot signal info");
USB_SERIAL.println(""); USB_SERIAL.println(" status - Show system status");
USB_SERIAL.println("Advanced:"); if (IS_BIDIRECTIONAL)
USB_SERIAL.println(" cmd <number> - Send DShot command (0 - 47)"); {
USB_SERIAL.println(" throttle <value> - Set throttle (48 - 2047)"); USB_SERIAL.println(" rpm - Get telemetry data");
USB_SERIAL.println(" throttle 0 - Stop sending throttle"); }
USB_SERIAL.println(""); USB_SERIAL.println(" h / help - Show this Menu");
USB_SERIAL.println(" h - Show this Menu"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println(""); USB_SERIAL.println("EXAMPLE INPUT:");
USB_SERIAL.println("Examples:"); USB_SERIAL.println(" cmd 5 - Get ESC Info");
USB_SERIAL.println(" cmd 1 - Stop Motor"); USB_SERIAL.println(" throttle 1000 - Set throttle to 1000");
USB_SERIAL.println(" throttle 1000 - Set throttle to 1000"); USB_SERIAL.println("**********************************************");
USB_SERIAL.println("================================================");
} }

View File

@ -9,9 +9,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <DShotRMT.h> #include <DShotRMT.h>
// Debug output
static constexpr auto DEBUG = false;
// USB serial port settings // USB serial port settings
static constexpr auto &USB_SERIAL = Serial0; static constexpr auto &USB_SERIAL = Serial0;
static constexpr auto USB_SERIAL_BAUD = 115200; static constexpr auto USB_SERIAL_BAUD = 115200;
@ -38,49 +35,169 @@ void setup()
// Starts the USB Serial Port // Starts the USB Serial Port
USB_SERIAL.begin(USB_SERIAL_BAUD); USB_SERIAL.begin(USB_SERIAL_BAUD);
// Initializes DShot Signal // Initialize DShot Signal
motor01.begin(); motor01.begin();
// Print CPU Info // Print CPU Info
motor01.printCpuInfo(); motor01.printCpuInfo();
USB_SERIAL.println(" "); //
USB_SERIAL.println("***********************************"); printMenu();
USB_SERIAL.println(" === DShotRMT Demo started. === ");
USB_SERIAL.println("Enter a throttle value (48 2047):");
} }
// //
void loop() void loop()
{ {
/// ...safety first // Safety first
static uint16_t throttle = DSHOT_CMD_MOTOR_STOP; static uint16_t throttle = DSHOT_CMD_MOTOR_STOP;
static bool continuous_throttle = true;
// Time Measurement // Time Measurement
static uint32_t last_stats_print = 0; static uint32_t last_stats_print = 0;
// Read throttle value // Handle serial input
if (USB_SERIAL.available() > 0) if (USB_SERIAL.available() > 0)
{ {
throttle = USB_SERIAL.readStringUntil('\n').toInt(); String input = USB_SERIAL.readStringUntil('\n');
input.trim();
if (input.length() > 0)
{
handleSerialInput(input, throttle, continuous_throttle);
}
} }
// Send the current throttle value // Send throttle value in continuous mode
motor01.sendThrottle(throttle); if (continuous_throttle)
{
motor01.sendThrottle(throttle);
}
// Print motor stats every 2 seconds // Print motor stats every 5 seconds in continuous mode
if (millis() - last_stats_print >= 2000) if (continuous_throttle && (millis() - last_stats_print >= 5000))
{ {
motor01.printDShotInfo(); motor01.printDShotInfo();
// Get Motor RPM // Get Motor RPM if bidirectional
if (IS_BIDIRECTIONAL) if (IS_BIDIRECTIONAL)
{ {
uint32_t rpm = motor01.getMotorRPM(MOTOR01_MAGNET_COUNT); dshot_telemetry_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
USB_SERIAL.printf("Motor RPM: %u\n", rpm); printTelemetryResult(telem_result);
} }
// Time Stamp // Time Stamp
last_stats_print = millis(); last_stats_print = millis();
} }
} }
//
void printMenu()
{
USB_SERIAL.println(" ");
USB_SERIAL.println("******************************************");
USB_SERIAL.println(" DShotRMT Demo ");
USB_SERIAL.println("******************************************");
USB_SERIAL.println(" <value> - Set throttle (48 2047)");
USB_SERIAL.println(" 0 - Stop motor");
USB_SERIAL.println("******************************************");
USB_SERIAL.println(" cmd <number> - Send DShot command (0-47)");
USB_SERIAL.println(" info - Show motor info");
if (IS_BIDIRECTIONAL)
{
USB_SERIAL.println(" rpm - Get telemetry data");
}
USB_SERIAL.println("******************************************");
USB_SERIAL.println(" h / help - Show this Menu");
USB_SERIAL.println("******************************************");
}
// Helper to print command results
void printCommandResult(const dshot_result_t &result, const String &operation)
{
if (result.success)
{
USB_SERIAL.printf("%s: SUCCESS\n", operation.c_str());
}
else
{
USB_SERIAL.printf("%s: FAILED - %s\n", operation.c_str(), result.error_message);
}
}
// Helper to print telemetry results
void printTelemetryResult(const dshot_telemetry_result_t &result)
{
if (result.success)
{
USB_SERIAL.printf("Telemetry: eRPM=%u, Motor RPM=%u (%s)\n", result.erpm, result.motor_rpm, result.error_message);
}
else
{
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.error_message);
}
}
//
void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous_throttle)
{
if (input == "0")
{
// Stop motor
throttle = 0;
continuous_throttle = true; // kill motor for sure
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
printCommandResult(result, "Stop Motor");
}
else if (input == "info")
{
continuous_throttle = false;
motor01.printDShotInfo();
}
else if (input == "rpm" && IS_BIDIRECTIONAL)
{
dshot_telemetry_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
printTelemetryResult(result);
}
else if (input.startsWith("cmd "))
{
continuous_throttle = false;
// Send DShot command
int cmd_num = input.substring(4).toInt();
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
{
dshot_result_t result = motor01.sendCommand(cmd_num);
printCommandResult(result, "DShot Command " + String(cmd_num));
}
else
{
USB_SERIAL.printf("Invalid command: %d (valid range: 0 - %d)\n", cmd_num, DSHOT_CMD_MAX);
}
}
else if (input == "h" || input == "help")
{
printMenu();
}
else
{
// Parse input throttle value
int throttle_value = input.toInt();
if (throttle_value >= DSHOT_THROTTLE_MIN && throttle_value <= DSHOT_THROTTLE_MAX)
{
throttle = throttle_value;
continuous_throttle = true;
dshot_result_t result = motor01.sendThrottle(throttle);
printCommandResult(result, "Set Throttle " + String(throttle));
USB_SERIAL.println("Continuous throttle mode enabled. Send '0' to stop.");
}
else
{
USB_SERIAL.printf("Invalid input: '%s'\n", input.c_str());
USB_SERIAL.printf("Valid throttle range: %d - %d\n", DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
}
}
}

View File

@ -1,5 +1,5 @@
name=DShotRMT name=DShotRMT
version=0.7.0 version=0.7.1
author=derdoktor667 author=derdoktor667
maintainer=derdoktor667 maintainer=derdoktor667
sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S. sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.