DShotRMT/src/dshot_definitions.h

265 lines
10 KiB
C
Raw Normal View History

2025-09-25 13:03:55 +01:00
#pragma once
#include <Arduino.h>
#include <driver/gpio.h>
2025-09-25 13:03:55 +01:00
#include <driver/rmt_tx.h>
#include <driver/rmt_rx.h>
#include <atomic>
2025-09-25 13:03:55 +01:00
// Defines the available DShot communication speeds with type safety.
2025-09-25 13:03:55 +01:00
enum class dshot_mode_t
{
DSHOT_OFF,
DSHOT150,
DSHOT300,
DSHOT600,
DSHOT1200
};
2025-09-27 13:59:23 +01:00
// Represents the 16-bit DShot data packet sent to the ESC.
2025-09-25 13:03:55 +01:00
typedef struct dshot_packet
{
2025-09-27 13:59:23 +01:00
uint16_t throttle_value : 11; // 11-bit throttle value or command.
bool telemetric_request : 1; // 1-bit telemetry request flag.
uint16_t checksum : 4; // 4-bit CRC checksum.
2025-09-25 13:03:55 +01:00
} dshot_packet_t;
2025-09-27 13:59:23 +01:00
// Defines the bit length and high time for a '1' bit in microseconds for each DShot mode.
2025-09-25 13:03:55 +01:00
typedef struct dshot_timing
{
2025-09-27 13:59:23 +01:00
double bit_length_us; // Total duration of one bit in microseconds.
double t1h_lenght_us; // High time duration for a '1' bit in microseconds.
2025-09-25 13:03:55 +01:00
} dshot_timing_us_t;
2025-09-27 13:59:23 +01:00
// Stores pre-calculated timing values in RMT ticks for efficient signal generation.
2025-09-25 13:03:55 +01:00
typedef struct rmt_ticks
{
2025-09-27 13:59:23 +01:00
uint16_t bit_length_ticks; // Total duration of one bit in RMT ticks.
uint16_t t1h_ticks; // High time duration for a '1' bit in RMT ticks.
uint16_t t1l_ticks; // Low time duration for a '1' bit in RMT ticks.
uint16_t t0h_ticks; // High time duration for a '0' bit in RMT ticks.
uint16_t t0l_ticks; // Low time duration for a '0' bit in RMT ticks.
2025-09-25 13:03:55 +01:00
} rmt_ticks_t;
2025-09-27 13:59:23 +01:00
// Enum class for specific error and success codes returned by DShotRMT functions.
2025-09-25 13:03:55 +01:00
enum class dshot_msg_code_t
{
2025-09-27 22:17:43 +01:00
DSHOT_NONE = 0,
DSHOT_UNKNOWN,
DSHOT_TX_INIT_FAILED,
DSHOT_RX_INIT_FAILED,
DSHOT_ENCODER_INIT_FAILED,
DSHOT_CALLBACK_REGISTERING_FAILED,
DSHOT_RECEIVER_FAILED,
DSHOT_TRANSMISSION_FAILED,
DSHOT_THROTTLE_NOT_IN_RANGE,
DSHOT_PERCENT_NOT_IN_RANGE,
DSHOT_COMMAND_NOT_VALID,
DSHOT_BIDIR_NOT_ENABLED,
DSHOT_TELEMETRY_FAILED,
DSHOT_INVALID_MAGNET_COUNT,
DSHOT_INVALID_COMMAND,
DSHOT_TIMING_CORRECTION,
DSHOT_INIT_FAILED,
DSHOT_INIT_SUCCESS,
DSHOT_TX_INIT_SUCCESS,
DSHOT_RX_INIT_SUCCESS,
DSHOT_ENCODER_INIT_SUCCESS,
DSHOT_ENCODING_SUCCESS,
DSHOT_TRANSMISSION_SUCCESS,
DSHOT_TELEMETRY_SUCCESS,
DSHOT_COMMAND_SUCCESS
2025-09-25 13:03:55 +01:00
};
2025-09-27 13:59:23 +01:00
// Contains the success status, an error code, and optional telemetry data.
2025-09-25 13:03:55 +01:00
typedef struct dshot_result
{
bool success;
2025-09-29 15:51:12 +01:00
dshot_msg_code_t result_code; // Specific error or success code.
2025-09-27 13:59:23 +01:00
uint16_t erpm; // Electrical RPM (eRPM) if telemetry is successful.
uint16_t motor_rpm; // Motor RPM if telemetry is successful and magnet count is known.
2025-09-25 13:03:55 +01:00
} dshot_result_t;
// Standard DShot commands (regular enum for easier handling as per project conventions).
2025-09-25 15:32:40 +01:00
enum dshotCommands_e
2025-09-25 13:03:55 +01:00
{
DSHOT_CMD_MOTOR_STOP = 0,
DSHOT_CMD_BEACON1,
DSHOT_CMD_BEACON2,
DSHOT_CMD_BEACON3,
DSHOT_CMD_BEACON4,
DSHOT_CMD_BEACON5,
DSHOT_CMD_ESC_INFO,
2025-09-25 13:03:55 +01:00
DSHOT_CMD_SPIN_DIRECTION_1,
DSHOT_CMD_SPIN_DIRECTION_2,
DSHOT_CMD_3D_MODE_OFF,
DSHOT_CMD_3D_MODE_ON,
DSHOT_CMD_SETTINGS_REQUEST,
2025-09-25 13:03:55 +01:00
DSHOT_CMD_SAVE_SETTINGS,
DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE,
DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE,
DSHOT_CMD_SPIN_DIRECTION_NORMAL = 20,
DSHOT_CMD_SPIN_DIRECTION_REVERSED = 21,
DSHOT_CMD_LED0_ON,
DSHOT_CMD_LED1_ON,
DSHOT_CMD_LED2_ON,
DSHOT_CMD_LED3_ON,
DSHOT_CMD_LED0_OFF,
DSHOT_CMD_LED1_OFF,
DSHOT_CMD_LED2_OFF,
DSHOT_CMD_LED3_OFF,
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF = 30,
DSHOT_CMD_SILENT_MODE_ON_OFF = 31,
2025-09-25 13:03:55 +01:00
DSHOT_CMD_MAX = 47
};
// --- Type-Safe Constants ---
2025-09-25 13:03:55 +01:00
// DShot Protocol Constants
static constexpr uint16_t DSHOT_THROTTLE_FAILSAFE = 0;
static constexpr uint16_t DSHOT_THROTTLE_MIN = 48;
static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047;
static constexpr uint16_t DSHOT_BITS_PER_FRAME = 16;
static constexpr uint16_t DEFAULT_MOTOR_MAGNET_COUNT = 14;
2025-09-25 13:03:55 +01:00
// Custom status codes
static constexpr int DSHOT_OK = 0;
static constexpr int DSHOT_ERROR = 1;
// Configuration Constants
static constexpr uint16_t DSHOT_NULL_PACKET = 0b0000000000000000;
static constexpr uint16_t DSHOT_FULL_PACKET = 0b1111111111111111;
static constexpr uint16_t DSHOT_CRC_MASK = 0b0000000000001111;
static constexpr rmt_clock_source_t DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
static constexpr uint32_t DSHOT_RMT_RESOLUTION = 8000000; // 8 MHz resolution
static constexpr uint16_t RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / 1000000; // RMT Ticks per microsecond
static constexpr uint16_t DSHOT_RX_TIMEOUT_MS = 2;
static constexpr uint16_t DSHOT_PADDING_US = 20; // Pause between frames
static constexpr uint16_t RMT_BUFFER_SYMBOLS = 64;
static constexpr uint16_t RMT_QUEUE_DEPTH = 1;
static constexpr uint16_t GCR_BITS_PER_FRAME = 21; // GCR bits in a DShot answer frame
static constexpr uint16_t POLE_PAIRS_MIN = 1;
static constexpr uint16_t MAGNETS_PER_POLE_PAIR = 2;
static constexpr uint16_t NO_DSHOT_TELEMETRY = 0;
static constexpr uint16_t DSHOT_PULSE_MIN_NS = 800; // 0.8us minimum pulse
static constexpr uint16_t DSHOT_PULSE_MAX_NS = 8000; // 8.0us maximum pulse
static constexpr uint16_t DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX;
static constexpr uint16_t DSHOT_TELEMETRY_BIT_POSITION = 11;
static constexpr uint16_t DSHOT_CRC_BIT_SHIFT = 4;
// Command Constants
static constexpr uint16_t DEFAULT_CMD_DELAY_US = 10;
static constexpr uint16_t DEFAULT_CMD_REPEAT_COUNT = 1;
static constexpr uint16_t SETTINGS_COMMAND_REPEATS = 10;
static constexpr uint16_t SETTINGS_COMMAND_DELAY_US = 5;
2025-09-25 13:03:55 +01:00
// Timing parameters for each DShot mode
const dshot_timing_us_t DSHOT_TIMING_US[] = {
{0.00, 0.00}, // DSHOT_OFF
{6.67, 5.00}, // DSHOT150
{3.33, 2.50}, // DSHOT300
{1.67, 1.25}, // DSHOT600
{0.83, 0.67} // DSHOT1200
};
2025-09-25 13:03:55 +01:00
// Error Messages
static constexpr char NONE[] = "";
static constexpr char UNKNOWN_ERROR[] = "Unknown Error!";
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 ENCODER_INIT_SUCCESS[] = "RMT encoder initialized successfully";
static constexpr char ENCODER_INIT_FAILED[] = "RMT encoder init failed!";
static constexpr char ENCODING_SUCCESS[] = "Packet encoded successfully";
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 PERCENT_NOT_IN_RANGE[] = "Percent not in range! (0.0 - 100.0)";
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!";
static constexpr char INVALID_MAGNET_COUNT[] = "Invalid motor magnet count!";
static constexpr char TIMING_CORRECTION[] = "Timing correction!";
static constexpr char CALLBACK_REGISTERING_FAILED[] = "RMT RX Callback registering failed!";
static constexpr char INVALID_COMMAND[] = "Invalid command!";
static constexpr char COMMAND_SUCCESS[] = "DShot command sent successfully";
2025-09-27 22:17:43 +01:00
2025-09-29 15:51:12 +01:00
// Helper to get result code string
inline const char* _get_result_code_str(dshot_msg_code_t code)
{
switch (code)
{
case dshot_msg_code_t::DSHOT_NONE:
return NONE;
case dshot_msg_code_t::DSHOT_UNKNOWN:
return UNKNOWN_ERROR;
case dshot_msg_code_t::DSHOT_TX_INIT_FAILED:
return TX_INIT_FAILED;
case dshot_msg_code_t::DSHOT_RX_INIT_FAILED:
return RX_INIT_FAILED;
case dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED:
return ENCODER_INIT_FAILED;
case dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED:
return CALLBACK_REGISTERING_FAILED;
case dshot_msg_code_t::DSHOT_RECEIVER_FAILED:
return RECEIVER_FAILED;
case dshot_msg_code_t::DSHOT_TRANSMISSION_FAILED:
return TRANSMISSION_FAILED;
case dshot_msg_code_t::DSHOT_THROTTLE_NOT_IN_RANGE:
return THROTTLE_NOT_IN_RANGE;
case dshot_msg_code_t::DSHOT_PERCENT_NOT_IN_RANGE:
return PERCENT_NOT_IN_RANGE;
case dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID:
return COMMAND_NOT_VALID;
case dshot_msg_code_t::DSHOT_BIDIR_NOT_ENABLED:
return BIDIR_NOT_ENABLED;
case dshot_msg_code_t::DSHOT_TELEMETRY_FAILED:
return TELEMETRY_FAILED;
case dshot_msg_code_t::DSHOT_INVALID_MAGNET_COUNT:
return INVALID_MAGNET_COUNT;
case dshot_msg_code_t::DSHOT_INVALID_COMMAND:
return INVALID_COMMAND;
case dshot_msg_code_t::DSHOT_TIMING_CORRECTION:
return TIMING_CORRECTION;
case dshot_msg_code_t::DSHOT_INIT_FAILED:
return INIT_FAILED;
case dshot_msg_code_t::DSHOT_INIT_SUCCESS:
return INIT_SUCCESS;
case dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS:
return TX_INIT_SUCCESS;
case dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS:
return RX_INIT_SUCCESS;
case dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS:
return ENCODER_INIT_SUCCESS;
case dshot_msg_code_t::DSHOT_ENCODING_SUCCESS:
return ENCODING_SUCCESS;
case dshot_msg_code_t::DSHOT_TRANSMISSION_SUCCESS:
return TRANSMISSION_SUCCESS;
case dshot_msg_code_t::DSHOT_TELEMETRY_SUCCESS:
return TELEMETRY_SUCCESS;
case dshot_msg_code_t::DSHOT_COMMAND_SUCCESS:
return COMMAND_SUCCESS;
default:
return UNKNOWN_ERROR;
}
}
2025-09-27 22:17:43 +01:00
// Helpers
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
{
2025-09-29 15:51:12 +01:00
output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", _get_result_code_str(result.result_code));
// Print telemetry data if available
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
{
output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm);
}
output.println();
2025-09-27 22:17:43 +01:00
}