2025-09-04 13:41:05 +01:00
|
|
|
/*
|
|
|
|
|
* DShotCommandManager.cpp
|
|
|
|
|
* Advanced DShot command management for DShotRMT library
|
|
|
|
|
* Author: Wastl Kraus
|
|
|
|
|
* Date: 2025-09-04
|
|
|
|
|
* License: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <DShotCommandManager.h>
|
|
|
|
|
|
|
|
|
|
// Constructor
|
|
|
|
|
DShotCommandManager::DShotCommandManager(DShotRMT &dshot_instance)
|
|
|
|
|
: _dshot(dshot_instance),
|
|
|
|
|
_total_commands_sent(0),
|
|
|
|
|
_failed_commands(0),
|
|
|
|
|
_last_command_timestamp(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Init command manager
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::begin()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result;
|
|
|
|
|
result.success = true;
|
|
|
|
|
result.error_message = "Success";
|
|
|
|
|
return result;
|
2025-09-04 13:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- BASIC COMMAND METHODS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16_t repeat_count)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
return sendCommandWithDelay(command, repeat_count, DEFAULT_COMMAND_DELAY_MS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result = {false, "Unknown error"};
|
|
|
|
|
|
2025-09-04 13:41:05 +01:00
|
|
|
if (!isValidCommand(command))
|
|
|
|
|
{
|
|
|
|
|
result.error_message = "Invalid command";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool all_successful = true;
|
|
|
|
|
|
|
|
|
|
// Send command multiple times with delay
|
|
|
|
|
for (uint16_t i = 0; i < repeat_count; i++)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t single_result = _executeCommand(command);
|
2025-09-04 13:41:05 +01:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
_delay_ms(delay_ms);
|
2025-09-04 13:41:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 11:16:19 +01:00
|
|
|
//
|
2025-09-04 13:41:05 +01:00
|
|
|
result.success = all_successful;
|
|
|
|
|
|
|
|
|
|
if (result.success)
|
|
|
|
|
{
|
|
|
|
|
result.error_message = "Success";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- MOTOR CONTROL COMMANDS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::stopMotor()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::set3DMode(bool enable)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::setSpinDirection(bool reversed)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::saveSettings()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
return sendCommandWithDelay(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- TELEMETRY COMMANDS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::setExtendedTelemetry(bool enable)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
dshot_commands_t command = enable ? DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE : DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE;
|
|
|
|
|
return sendCommand(command);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::requestESCInfo()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
return sendCommand(DSHOT_CMD_ESC_INFO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- LED CONTROL COMMANDS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::setLED(uint8_t led_number, bool state)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
if (led_number > 3)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result = {false, "Invalid LED number (0-3)"};
|
2025-09-04 13:41:05 +01:00
|
|
|
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 ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
if (beacon_number < 1 || beacon_number > 5)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result = {false, "Invalid beacon number (1-5)"};
|
2025-09-04 13:41:05 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dshot_commands_t command = static_cast<dshot_commands_t>(DSHOT_CMD_BEACON1 + beacon_number - 1);
|
|
|
|
|
return sendCommand(command);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- KISS ESC SPECIFIC COMMANDS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::setAudioStreamMode(bool enable)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
// KISS audio stream mode is a toggle command
|
|
|
|
|
return sendCommand(DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::setSilentMode(bool enable)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
// KISS silent mode is a toggle command
|
|
|
|
|
return sendCommand(DSHOT_CMD_SILENT_MODE_ON_OFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- SEQUENCE COMMANDS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result = {true, "Success"};
|
2025-09-04 13:41:05 +01:00
|
|
|
uint64_t total_start_time = esp_timer_get_time();
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sequence_length; i++)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t item_result = sendCommandWithDelay(
|
2025-09-04 13:41:05 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2025-09-05 11:16:19 +01:00
|
|
|
_delay_ms(sequence[i].delay_ms);
|
2025-09-04 13:41:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t total_end_time = esp_timer_get_time();
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::executeInitSequence()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
// 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]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::executeCalibrationSequence()
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- PRIVATE METHODS ---
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t DShotCommandManager::_executeCommand(dshot_commands_t command)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
uint64_t start_time = esp_timer_get_time();
|
|
|
|
|
|
|
|
|
|
// Execute the command using the DShotRMT instance
|
2025-09-05 11:16:19 +01:00
|
|
|
dshot_result_t result = _dshot.sendCommand(static_cast<uint16_t>(command));
|
2025-09-04 13:41:05 +01:00
|
|
|
|
|
|
|
|
uint64_t end_time = esp_timer_get_time();
|
|
|
|
|
_last_command_timestamp = end_time;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
2025-09-05 11:16:19 +01:00
|
|
|
void DShotCommandManager::_delay_ms(uint32_t delay_ms)
|
2025-09-04 13:41:05 +01:00
|
|
|
{
|
|
|
|
|
if (delay_ms > 0)
|
|
|
|
|
{
|
|
|
|
|
delay(delay_ms);
|
|
|
|
|
}
|
|
|
|
|
}
|