...update error handling
This commit is contained in:
parent
cfca2de4e9
commit
feb6f4ecac
|
|
@ -22,7 +22,7 @@ dshot_result_t DShotCommandManager::begin()
|
|||
{
|
||||
dshot_result_t result;
|
||||
result.success = true;
|
||||
result.error_message = "Success";
|
||||
result.msg = "Success";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t comman
|
|||
|
||||
if (!isValidCommand(command))
|
||||
{
|
||||
result.error_message = "Invalid command";
|
||||
result.msg = "Invalid command";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t comman
|
|||
if (!single_result.success)
|
||||
{
|
||||
all_successful = false;
|
||||
result.error_message = single_result.error_message;
|
||||
result.msg = single_result.msg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -64,12 +64,12 @@ dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t comman
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
result.success = all_successful;
|
||||
|
||||
if (result.success)
|
||||
{
|
||||
result.error_message = "Success";
|
||||
result.msg = "Success";
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -194,7 +194,7 @@ dshot_result_t DShotCommandManager::setSilentMode(bool enable)
|
|||
}
|
||||
|
||||
// --- SEQUENCE COMMANDS ---
|
||||
dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length)
|
||||
dshot_result_t DShotCommandManager::executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length)
|
||||
{
|
||||
dshot_result_t result = {true, "Success"};
|
||||
uint64_t total_start_time = esp_timer_get_time();
|
||||
|
|
@ -209,7 +209,7 @@ dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence
|
|||
if (!item_result.success)
|
||||
{
|
||||
result.success = false;
|
||||
result.error_message = item_result.error_message;
|
||||
result.msg = item_result.msg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ dshot_result_t DShotCommandManager::executeSequence(const dshot_command_sequence
|
|||
dshot_result_t DShotCommandManager::executeInitSequence()
|
||||
{
|
||||
// Basic ESC initialization sequence
|
||||
dshot_command_sequence_item_t init_sequence[] = {
|
||||
dshot_commandmanager_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
|
||||
|
|
@ -242,7 +242,7 @@ dshot_result_t DShotCommandManager::executeInitSequence()
|
|||
dshot_result_t DShotCommandManager::executeCalibrationSequence()
|
||||
{
|
||||
// Basic ESC calibration sequence
|
||||
dshot_command_sequence_item_t calibration_sequence[] = {
|
||||
dshot_commandmanager_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
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@
|
|||
#include <DShotRMT.h>
|
||||
#include <dshot_commands.h>
|
||||
|
||||
// Command sequence item
|
||||
// Command item
|
||||
typedef struct
|
||||
{
|
||||
dshot_commands_t command;
|
||||
uint16_t repeat_count;
|
||||
uint32_t delay_ms;
|
||||
} dshot_command_sequence_item_t;
|
||||
} dshot_commandmanager_item_t;
|
||||
|
||||
// Advanced DShot command manager class
|
||||
class DShotCommandManager
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
|
||||
// --- SEQUENCE COMMANDS ---
|
||||
// Execute a sequence of DShot commands
|
||||
dshot_result_t executeSequence(const dshot_command_sequence_item_t *sequence, size_t sequence_length);
|
||||
dshot_result_t executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length);
|
||||
|
||||
// Execute ESC initialization sequence
|
||||
dshot_result_t executeInitSequence();
|
||||
|
|
|
|||
177
DShotRMT.cpp
177
DShotRMT.cpp
|
|
@ -18,6 +18,36 @@ static constexpr dshot_timing_t DSHOT_TIMINGS[] = {
|
|||
{16, 8, 6, 2, 3, 5} // DSHOT1200
|
||||
};
|
||||
|
||||
// --- HELPERS ---
|
||||
void printDShotResult(dshot_result_t &result, Stream &output)
|
||||
{
|
||||
if (result.success)
|
||||
{
|
||||
output.printf("Staus: SUCCESS - %s\n", result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.printf("Status: FAILED - %s\n", result.msg);
|
||||
}
|
||||
|
||||
output.println(" ");
|
||||
}
|
||||
|
||||
//
|
||||
void printDShotTelemetry(dshot_telemetry_result_t &result, Stream &output)
|
||||
{
|
||||
if (result.success)
|
||||
{
|
||||
output.printf("Telemetry: eRPM=%u, Motor RPM=%u \n", result.erpm, result.motor_rpm);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.printf("Telemetry: FAILED - %s\n", result.msg);
|
||||
}
|
||||
|
||||
output.println(" ");
|
||||
}
|
||||
|
||||
// Constructor with GPIO number
|
||||
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
||||
: _gpio(gpio),
|
||||
|
|
@ -26,11 +56,8 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
|||
_frame_timer_us(0),
|
||||
_timing_config(DSHOT_TIMINGS[mode]),
|
||||
_last_transmission_time(0),
|
||||
_last_erpm(0),
|
||||
_parsed_packet(0),
|
||||
_packet{0},
|
||||
_total_transmissions(0),
|
||||
_failed_transmissions(0),
|
||||
_rmt_tx_channel(nullptr),
|
||||
_rmt_rx_channel(nullptr),
|
||||
_dshot_encoder(nullptr),
|
||||
|
|
@ -98,46 +125,45 @@ DShotRMT::~DShotRMT()
|
|||
// Init DShotRMT
|
||||
dshot_result_t DShotRMT::begin()
|
||||
{
|
||||
dshot_result_t result = {false, UNKNOWN_ERROR};
|
||||
uint64_t start_time = esp_timer_get_time();
|
||||
// Result container
|
||||
dshot_result_t result = {false, INIT_FAILED};
|
||||
|
||||
// Init RX channel first
|
||||
if (_is_bidirectional)
|
||||
{
|
||||
if (!_initRXChannel())
|
||||
if (!_initRXChannel().success)
|
||||
{
|
||||
result.error_message = RX_INIT_FAILED;
|
||||
_dshot_log(RX_INIT_FAILED);
|
||||
result.msg = RX_INIT_FAILED;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Init TX channel
|
||||
if (!_initTXChannel())
|
||||
if (!_initTXChannel().success)
|
||||
{
|
||||
result.error_message = TX_INIT_FAILED;
|
||||
_dshot_log(TX_INIT_FAILED);
|
||||
result.msg = TX_INIT_FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Init DShot encoder
|
||||
if (_initDShotEncoder() != DSHOT_OK)
|
||||
if (!_initDShotEncoder().success)
|
||||
{
|
||||
result.error_message = ENCODER_INIT_FAILED;
|
||||
_dshot_log(ENCODER_INIT_FAILED);
|
||||
result.msg = ENCODER_INIT_FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t end_time = esp_timer_get_time();
|
||||
result.success = true;
|
||||
result.error_message = INIT_SUCCESS;
|
||||
result.msg = INIT_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Init RMT TX channel
|
||||
bool DShotRMT::_initTXChannel()
|
||||
dshot_result_t DShotRMT::_initTXChannel()
|
||||
{
|
||||
// Result container
|
||||
dshot_result_t result = {false, TX_INIT_FAILED};
|
||||
|
||||
// Configure TX channel
|
||||
_tx_channel_config.gpio_num = _gpio;
|
||||
_tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
|
||||
|
|
@ -152,21 +178,33 @@ bool DShotRMT::_initTXChannel()
|
|||
// Create RMT TX channel
|
||||
if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK)
|
||||
{
|
||||
_dshot_log(TX_INIT_FAILED);
|
||||
return DSHOT_ERROR;
|
||||
return result;
|
||||
}
|
||||
|
||||
return (rmt_enable(_rmt_tx_channel) == DSHOT_OK);
|
||||
//
|
||||
if (rmt_enable(_rmt_tx_channel) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
result.msg = TX_INIT_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Init RMT RX channel
|
||||
bool DShotRMT::_initRXChannel()
|
||||
dshot_result_t DShotRMT::_initRXChannel()
|
||||
{
|
||||
// Result container
|
||||
dshot_result_t result = {false, RX_INIT_FAILED};
|
||||
|
||||
// Create a queue for RX callback data
|
||||
_rx_queue = xQueueCreate(RMT_QUEUE_DEPTH, sizeof(rmt_rx_done_event_data_t));
|
||||
if (_rx_queue == nullptr)
|
||||
{
|
||||
return DSHOT_ERROR;
|
||||
result.msg = RX_BUFFER_FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Config RMT RX
|
||||
|
|
@ -182,8 +220,7 @@ bool DShotRMT::_initRXChannel()
|
|||
// Create RMT RX channel
|
||||
if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK)
|
||||
{
|
||||
_dshot_log(RX_INIT_FAILED);
|
||||
return DSHOT_ERROR;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Register RX callback
|
||||
|
|
@ -191,48 +228,64 @@ bool DShotRMT::_initRXChannel()
|
|||
|
||||
if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, _rx_queue) != DSHOT_OK)
|
||||
{
|
||||
_dshot_log(RX_INIT_FAILED);
|
||||
return DSHOT_ERROR;
|
||||
result.msg = RX_BUFFER_FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
return (rmt_enable(_rmt_rx_channel) == DSHOT_OK);
|
||||
//
|
||||
if (rmt_enable(_rmt_rx_channel) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
result.msg = RX_INIT_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Callback for RMT RX
|
||||
bool IRAM_ATTR DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
// Init RX buffer
|
||||
QueueHandle_t rx_queue = (QueueHandle_t)user_data;
|
||||
QueueHandle_t rx_buffer = (QueueHandle_t)user_data;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
// Copy callback data into RX buffer
|
||||
xQueueGenericSendFromISR(rx_queue, edata, &xHigherPriorityTaskWoken, queueSEND_TO_BACK);
|
||||
xQueueGenericSendFromISR(rx_buffer, edata, &xHigherPriorityTaskWoken, queueSEND_TO_BACK);
|
||||
|
||||
return (xHigherPriorityTaskWoken == pdTRUE);
|
||||
}
|
||||
|
||||
// Initialize DShot encoder
|
||||
bool DShotRMT::_initDShotEncoder()
|
||||
dshot_result_t DShotRMT::_initDShotEncoder()
|
||||
{
|
||||
// Result container
|
||||
dshot_result_t result = {false, ENCODER_INIT_FAILED};
|
||||
|
||||
// Create copy encoder configuration
|
||||
rmt_copy_encoder_config_t encoder_config = {};
|
||||
|
||||
// Create encoder instance
|
||||
if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
|
||||
{
|
||||
_dshot_log(ENCODER_INIT_FAILED);
|
||||
return DSHOT_ERROR;
|
||||
return result;
|
||||
}
|
||||
|
||||
return DSHOT_OK;
|
||||
result.success = true;
|
||||
result.msg = ENCODER_INIT_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Send throttle value
|
||||
dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
||||
{
|
||||
static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP;
|
||||
// Result container
|
||||
dshot_result_t result = {false, UNKNOWN_ERROR};
|
||||
|
||||
static uint16_t last_throttle = DSHOT_CMD_MOTOR_STOP;
|
||||
|
||||
// Special case: if throttle is 0, use sendCommand() instead
|
||||
if (throttle == 0)
|
||||
{
|
||||
|
|
@ -242,8 +295,7 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
|||
// Log only if throttle is out of range and different from last time
|
||||
if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle)
|
||||
{
|
||||
_dshot_log(THROTTLE_NOT_IN_RANGE);
|
||||
result.error_message = THROTTLE_NOT_IN_RANGE;
|
||||
result.msg = THROTTLE_NOT_IN_RANGE;
|
||||
}
|
||||
|
||||
// Always store the original throttle value
|
||||
|
|
@ -259,63 +311,67 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
|||
// Send DShot command to ESC
|
||||
dshot_result_t DShotRMT::sendCommand(uint16_t command)
|
||||
{
|
||||
// Result container
|
||||
dshot_result_t result = {false, UNKNOWN_ERROR};
|
||||
|
||||
// Validate command is within DShot specification range
|
||||
if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX)
|
||||
{
|
||||
_dshot_log(COMMAND_NOT_VALID);
|
||||
result.error_message = COMMAND_NOT_VALID;
|
||||
result.msg = COMMAND_NOT_VALID;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build packet and transmit
|
||||
_packet = _buildDShotPacket(command);
|
||||
|
||||
return _sendDShotFrame(_packet);
|
||||
}
|
||||
|
||||
// Get telemetry data with timing and error handling
|
||||
dshot_telemetry_result_t DShotRMT::getTelemetry(uint8_t magnet_count)
|
||||
dshot_telemetry_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
||||
{
|
||||
dshot_telemetry_result_t result = {false, 0, 0, UNKNOWN_ERROR};
|
||||
// Result container
|
||||
dshot_telemetry_result_t result = {false, 0, 0, TELEMETRY_FAILED};
|
||||
|
||||
// Check if bidirectional mode is enabled
|
||||
if (!_is_bidirectional)
|
||||
{
|
||||
result.error_message = BIDIR_NOT_ENABLED;
|
||||
_dshot_log(BIDIR_NOT_ENABLED);
|
||||
result.msg = BIDIR_NOT_ENABLED;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get eRPM
|
||||
uint16_t erpm = getERPM();
|
||||
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;
|
||||
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;
|
||||
result.msg = TELEMETRY_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get RPM from ESC (bidirectional mode only) - backward compatibility
|
||||
uint16_t DShotRMT::getERPM()
|
||||
uint16_t DShotRMT::_getERPM()
|
||||
{
|
||||
static uint16_t last_erpm = 0;
|
||||
|
||||
// Result container
|
||||
dshot_telemetry_result_t result = {false, 0, 0, TELEMETRY_FAILED};
|
||||
|
||||
// Check if bidirectional mode is enabled
|
||||
if (!_is_bidirectional)
|
||||
{
|
||||
_dshot_log(BIDIR_NOT_ENABLED);
|
||||
return _last_erpm;
|
||||
return last_erpm;
|
||||
}
|
||||
|
||||
// RMT RX event data
|
||||
|
|
@ -327,11 +383,11 @@ uint16_t DShotRMT::getERPM()
|
|||
// Decode the received symbols if a valid frame was received
|
||||
if (rx_data.num_symbols > DSHOT_NULL_PACKET)
|
||||
{
|
||||
_last_erpm = _decodeDShotFrame(rx_data.received_symbols);
|
||||
last_erpm = _decodeDShotFrame(rx_data.received_symbols);
|
||||
}
|
||||
}
|
||||
|
||||
return _last_erpm;
|
||||
return last_erpm;
|
||||
}
|
||||
|
||||
// Build a complete DShot packet
|
||||
|
|
@ -343,8 +399,6 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value)
|
|||
// Re-check for valid value
|
||||
if (value > DSHOT_THROTTLE_MAX)
|
||||
{
|
||||
_dshot_log(PACKET_BUILD_ERROR);
|
||||
|
||||
// Something is really wrong
|
||||
return packet;
|
||||
}
|
||||
|
|
@ -390,7 +444,6 @@ uint16_t DShotRMT::_calculateCRC(const uint16_t data)
|
|||
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
|
||||
if (!_timer_signal())
|
||||
|
|
@ -406,7 +459,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
|||
|
||||
if (rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config) != DSHOT_OK)
|
||||
{
|
||||
result.error_message = RX_RMT_RECEIVER_ERROR;
|
||||
result.msg = RECEIVER_FAILED;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -426,8 +479,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
|||
// Disable RMT RX for sending
|
||||
if (rmt_disable(_rmt_rx_channel) != DSHOT_OK)
|
||||
{
|
||||
result.error_message = RX_RMT_RECEIVER_ERROR;
|
||||
_dshot_log(RX_RMT_RECEIVER_ERROR);
|
||||
result.msg = RECEIVER_FAILED;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -435,8 +487,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
|||
// Perform RMT transmission
|
||||
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);
|
||||
result.msg = TRANSMISSION_FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -445,18 +496,16 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
|||
{
|
||||
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
|
||||
{
|
||||
result.error_message = RX_RMT_RECEIVER_ERROR;
|
||||
_dshot_log(RX_RMT_RECEIVER_ERROR);
|
||||
result.msg = RECEIVER_FAILED;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Update timestamp and calculate execution time
|
||||
_timer_reset();
|
||||
uint64_t end_time = esp_timer_get_time();
|
||||
|
||||
result.success = true;
|
||||
result.error_message = TRANSMISSION_SUCCESS;
|
||||
result.msg = TRANSMISSION_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
94
DShotRMT.h
94
DShotRMT.h
|
|
@ -15,19 +15,20 @@
|
|||
#include <driver/rmt_rx.h>
|
||||
|
||||
// DShot Protocol Constants
|
||||
constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
|
||||
constexpr auto DSHOT_THROTTLE_MIN = 48;
|
||||
constexpr auto DSHOT_THROTTLE_MAX = 2047;
|
||||
constexpr auto DSHOT_BITS_PER_FRAME = 16;
|
||||
constexpr auto DSHOT_SWITCH_TIME = 30; // Time in us between TX and RX
|
||||
constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
|
||||
constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
|
||||
static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
|
||||
static constexpr auto DSHOT_THROTTLE_MIN = 48;
|
||||
static constexpr auto DSHOT_THROTTLE_MAX = 2047;
|
||||
static constexpr auto DSHOT_BITS_PER_FRAME = 16;
|
||||
static constexpr auto DSHOT_SWITCH_TIME = 30; // Time in us - add some padding to RX - TX switching
|
||||
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
|
||||
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2; // Never reached, just a timeeout
|
||||
static constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
|
||||
static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14;
|
||||
|
||||
// RMT Configuration Constants
|
||||
constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
||||
constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution
|
||||
constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
|
||||
constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
|
||||
constexpr auto RMT_BUFFER_SYMBOLS = 64;
|
||||
constexpr auto RMT_QUEUE_DEPTH = 1;
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ constexpr auto RMT_QUEUE_DEPTH = 1;
|
|||
constexpr uint32_t DSHOT_PULSE_MIN = 3000;
|
||||
constexpr uint32_t DSHOT_PULSE_MAX = 60000;
|
||||
|
||||
// DShot Mode Enumeration
|
||||
// DShot Modes
|
||||
typedef enum
|
||||
{
|
||||
DSHOT_OFF,
|
||||
|
|
@ -46,7 +47,7 @@ typedef enum
|
|||
DSHOT1200
|
||||
} dshot_mode_t;
|
||||
|
||||
// DShot Packet Structure
|
||||
// DShot Packet
|
||||
typedef struct
|
||||
{
|
||||
uint16_t throttle_value : 11;
|
||||
|
|
@ -54,7 +55,7 @@ typedef struct
|
|||
uint16_t checksum : 4;
|
||||
} dshot_packet_t;
|
||||
|
||||
// DShot Timing Configuration Structure
|
||||
// DShot Timing Configuration
|
||||
typedef struct
|
||||
{
|
||||
uint32_t frame_length_us;
|
||||
|
|
@ -65,25 +66,29 @@ typedef struct
|
|||
uint16_t ticks_zero_low;
|
||||
} dshot_timing_t;
|
||||
|
||||
// Command execution result structure
|
||||
// Error handling
|
||||
typedef struct
|
||||
{
|
||||
bool success;
|
||||
const char *error_message;
|
||||
const char *msg;
|
||||
} dshot_result_t;
|
||||
|
||||
// DShot telemetry result structure
|
||||
// DShot telemetry result
|
||||
typedef struct
|
||||
{
|
||||
bool success;
|
||||
uint16_t erpm;
|
||||
uint32_t motor_rpm;
|
||||
const char *error_message;
|
||||
uint16_t motor_rpm;
|
||||
const char *msg;
|
||||
} dshot_telemetry_result_t;
|
||||
|
||||
// Naming convention
|
||||
typedef dshotCommands_e dshot_commands_t;
|
||||
|
||||
// --- HELPERS ---
|
||||
void printDShotResult(dshot_result_t &result, Stream &output = Serial);
|
||||
void printDShotTelemetry(dshot_telemetry_result_t &result, Stream &output = Serial);
|
||||
|
||||
//
|
||||
class DShotRMT
|
||||
{
|
||||
|
|
@ -106,21 +111,12 @@ public:
|
|||
// Send DShot command (0-47)
|
||||
dshot_result_t sendCommand(uint16_t command);
|
||||
|
||||
// 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();
|
||||
|
||||
// --- GETTERS ---
|
||||
gpio_num_t getGPIO() const { return _gpio; }
|
||||
uint16_t getDShotPacket() const { return _parsed_packet; }
|
||||
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; }
|
||||
dshot_telemetry_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
||||
|
||||
// --- INFO ---
|
||||
void printDShotInfo(Stream &output = Serial) const;
|
||||
|
|
@ -158,7 +154,6 @@ private:
|
|||
|
||||
// --- TIMING & STATE VARIABLES ---
|
||||
uint64_t _last_transmission_time;
|
||||
uint16_t _last_erpm;
|
||||
uint16_t _parsed_packet;
|
||||
dshot_packet_t _packet;
|
||||
|
||||
|
|
@ -178,9 +173,9 @@ private:
|
|||
rmt_receive_config_t _receive_config;
|
||||
|
||||
// --- INITS ---
|
||||
bool _initTXChannel();
|
||||
bool _initRXChannel();
|
||||
bool _initDShotEncoder();
|
||||
dshot_result_t _initTXChannel();
|
||||
dshot_result_t _initRXChannel();
|
||||
dshot_result_t _initDShotEncoder();
|
||||
|
||||
// --- PACKET MANAGEMENT ---
|
||||
dshot_packet_t _buildDShotPacket(const uint16_t value);
|
||||
|
|
@ -192,6 +187,9 @@ private:
|
|||
bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
|
||||
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
|
||||
|
||||
// --- HELPERS ---
|
||||
uint16_t _getERPM();
|
||||
|
||||
// --- TIMING CONTROL ---
|
||||
bool IRAM_ATTR _timer_signal();
|
||||
bool _timer_reset();
|
||||
|
|
@ -204,27 +202,27 @@ private:
|
|||
// --- DSHOT DEFAULTS ---
|
||||
static constexpr auto DSHOT_TELEMETRY_INVALID = (0xffff);
|
||||
|
||||
// --- ERROR HANDLING & LOGGING ---
|
||||
void _dshot_log(const char *msg, Stream &output = Serial) const { output.println(msg); }
|
||||
|
||||
// --- CONSTANTS & ERROR MESSAGES ---
|
||||
static constexpr bool DSHOT_OK = 0;
|
||||
static constexpr bool DSHOT_ERROR = 1;
|
||||
|
||||
static constexpr char *NEW_LINE = " ";
|
||||
static constexpr char *NONE = "";
|
||||
static constexpr char *UNKNOWN_ERROR = "Unknown Error!";
|
||||
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 *ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!";
|
||||
static constexpr char *CRC_CHECK_FAILED = "RX CRC Check failed!";
|
||||
static constexpr char *THROTTLE_NOT_IN_RANGE = "Throttle value not in range (48 - 2047)!";
|
||||
static constexpr char *COMMAND_NOT_VALID = "Not a valid DShot Command (0 - 47)!";
|
||||
static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot support not enabled!";
|
||||
static constexpr char *RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!";
|
||||
static constexpr char *PACKET_BUILD_ERROR = "Value too big for DShot Packet!";
|
||||
static constexpr char *TRANSMITTER_ERROR = "RMT TX Transmitter Error!";
|
||||
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";
|
||||
static constexpr char *INIT_SUCCESS = "SignalGeneratorRMT initialized successfully";
|
||||
static constexpr char *INIT_FAILED = "SignalGeneratorRMT init failed!";
|
||||
static constexpr char *TX_INIT_SUCCESS = "TX RMT channel initialized successfully";
|
||||
static constexpr char *TX_INIT_FAILED = "TX RMT channel init failed!";
|
||||
static constexpr char *RX_INIT_SUCCESS = "RX RMT channel initialized successfully";
|
||||
static constexpr char *RX_INIT_FAILED = "RX RMT channel init failed!";
|
||||
static constexpr char *RX_BUFFER_FAILED = "RX RMT buffer init failed!";
|
||||
static constexpr char *ENCODER_INIT_SUCCESS = "RMT encoder initialized successfully";
|
||||
static constexpr char *ENCODER_INIT_FAILED = "RMT encoder init failed!";
|
||||
static constexpr char *TRANSMISSION_SUCCESS = "Transmission successfully";
|
||||
static constexpr char *TRANSMISSION_FAILED = "Transmission failed!";
|
||||
static constexpr char *RECEIVER_FAILED = "RMT receiver failed!";
|
||||
static constexpr char *THROTTLE_NOT_IN_RANGE = "Throttle not in range! (48 - 2047)";
|
||||
static constexpr char *COMMAND_NOT_VALID = "Command not valid! (0 - 47)";
|
||||
static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot not enabled!";
|
||||
static constexpr char *TELEMETRY_SUCCESS = "Valid Telemetric Frame received!";
|
||||
static constexpr char *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ void printTelemetryResult(const dshot_telemetry_result_t &result)
|
|||
}
|
||||
else
|
||||
{
|
||||
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.error_message);
|
||||
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ void printResult(const dshot_result_t &result)
|
|||
}
|
||||
else
|
||||
{
|
||||
USB_SERIAL.printf("FAILED - %s \n", result.error_message);
|
||||
USB_SERIAL.printf("FAILED - %s \n", result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ void loop()
|
|||
static bool continuous_throttle = true;
|
||||
|
||||
// Time Measurement
|
||||
static uint32_t last_stats_print = 0;
|
||||
static uint64_t last_stats_print = 0;
|
||||
|
||||
// Handle serial input
|
||||
if (USB_SERIAL.available() > 0)
|
||||
|
|
@ -73,23 +73,24 @@ void loop()
|
|||
motor01.sendThrottle(throttle);
|
||||
}
|
||||
|
||||
// Print motor stats every 5 seconds in continuous mode
|
||||
if (continuous_throttle && (millis() - last_stats_print >= 5000))
|
||||
// Print motor stats every 3 seconds in continuous mode
|
||||
if (continuous_throttle && (esp_timer_get_time() - last_stats_print >= 3000000))
|
||||
{
|
||||
motor01.printDShotInfo();
|
||||
|
||||
USB_SERIAL.println(" ");
|
||||
USB_SERIAL.println("Type 'help' to show Menu");
|
||||
|
||||
// Get Motor RPM if bidirectional
|
||||
if (IS_BIDIRECTIONAL)
|
||||
{
|
||||
dshot_telemetry_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
|
||||
printTelemetryResult(telem_result);
|
||||
printDShotTelemetry(telem_result);
|
||||
}
|
||||
|
||||
USB_SERIAL.println("Type 'help' to show Menu");
|
||||
|
||||
// Time Stamp
|
||||
last_stats_print = millis();
|
||||
last_stats_print = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,32 +115,6 @@ void printMenu()
|
|||
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)
|
||||
{
|
||||
|
|
@ -149,17 +124,16 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
|
|||
throttle = 0;
|
||||
continuous_throttle = true; // kill motor for sure
|
||||
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||
printCommandResult(result, "Stop Motor");
|
||||
printDShotResult(result);
|
||||
}
|
||||
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);
|
||||
printDShotTelemetry(result);
|
||||
}
|
||||
else if (input.startsWith("cmd "))
|
||||
{
|
||||
|
|
@ -171,7 +145,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
|
|||
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));
|
||||
printDShotResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -193,7 +167,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
|
|||
continuous_throttle = true;
|
||||
|
||||
dshot_result_t result = motor01.sendThrottle(throttle);
|
||||
printCommandResult(result, "Set Throttle " + String(throttle));
|
||||
printDShotResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue