...update result codes
This commit is contained in:
parent
252209dd1b
commit
29c851d4e2
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
125
DShotRMT.cpp
125
DShotRMT.cpp
|
|
@ -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");
|
||||||
|
|
|
||||||
74
DShotRMT.h
74
DShotRMT.h
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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("================================================");
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 function 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 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 (%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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue