more simplifications
This commit is contained in:
parent
a763786fbf
commit
30d87aae4f
|
|
@ -6,5 +6,8 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
#include "src/DShotRMT.h"
|
#include "src/DShotRMT.h"
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ static constexpr auto IS_BIDIRECTIONAL = false;
|
||||||
// static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
// static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, DEFAULT_MOTOR_MAGNET_COUNT);
|
||||||
|
|
||||||
//
|
//
|
||||||
void setup()
|
void setup()
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ static constexpr auto IS_BIDIRECTIONAL = false;
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, DEFAULT_MOTOR_MAGNET_COUNT);
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
void handleSerialInput(const String &input);
|
void handleSerialInput(const String &input);
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include <DShotRMT.h>
|
#include <DShotRMT.h>
|
||||||
#include <ota_update.h>
|
#include "web_utilities/ota_update.h"
|
||||||
#include <web_content.h>
|
#include "web_utilities/web_content.h"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
|
|
@ -51,7 +51,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT);
|
||||||
|
|
||||||
// Web Server Configuration
|
// Web Server Configuration
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
@ -254,7 +254,8 @@ void setupOTA()
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
} else {
|
} else {
|
||||||
USB_SERIAL.println("OTA Update failed!");
|
USB_SERIAL.println("OTA Update failed!");
|
||||||
} }, handleOTAUpload);
|
}
|
||||||
|
}, handleOTAUpload);
|
||||||
|
|
||||||
USB_SERIAL.println("OTA Update ready at: /update");
|
USB_SERIAL.println("OTA Update ready at: /update");
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +345,7 @@ void printWiFiStatus()
|
||||||
{
|
{
|
||||||
USB_SERIAL.println();
|
USB_SERIAL.println();
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
USB_SERIAL.println(" --- WIFI INFO --- ");
|
USB_SERIAL.println(" --- WIFI INFO ---");
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
USB_SERIAL.printf("SSID: %s\n", WiFi.SSID().c_str());
|
USB_SERIAL.printf("SSID: %s\n", WiFi.SSID().c_str());
|
||||||
USB_SERIAL.printf("IP Address: %s\n", WiFi.localIP().toString().c_str());
|
USB_SERIAL.printf("IP Address: %s\n", WiFi.localIP().toString().c_str());
|
||||||
|
|
@ -393,7 +394,7 @@ void printMenu()
|
||||||
{
|
{
|
||||||
USB_SERIAL.println(" ");
|
USB_SERIAL.println(" ");
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
USB_SERIAL.println(" --- DShotRMT Web Client Demo --- ");
|
USB_SERIAL.println(" --- DShotRMT Web Client Demo ---");
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
|
|
||||||
if (wifi_connected)
|
if (wifi_connected)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include <DShotRMT.h>
|
#include <DShotRMT.h>
|
||||||
#include <web_content.h>
|
#include "web_utilities/web_content.h"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
|
|
@ -50,7 +50,7 @@ static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT);
|
||||||
|
|
||||||
// Web Server Configuration
|
// Web Server Configuration
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <DShotRMT.h>
|
#include "DShotRMT.h"
|
||||||
|
|
||||||
// Constructor with GPIO number
|
// Constructor with GPIO number
|
||||||
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
||||||
|
|
@ -62,6 +62,7 @@ DShotRMT::~DShotRMT()
|
||||||
dshot_result_t DShotRMT::begin()
|
dshot_result_t DShotRMT::begin()
|
||||||
{
|
{
|
||||||
dshot_result_t result = init_rmt_tx_channel(_gpio, &_rmt_tx_channel, _is_bidirectional);
|
dshot_result_t result = init_rmt_tx_channel(_gpio, &_rmt_tx_channel, _is_bidirectional);
|
||||||
|
|
||||||
if (!result.success)
|
if (!result.success)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -81,6 +82,7 @@ dshot_result_t DShotRMT::begin()
|
||||||
}
|
}
|
||||||
|
|
||||||
result = init_dshot_encoder(&_dshot_encoder, _rmt_ticks, _pulse_level, _idle_level);
|
result = init_dshot_encoder(&_dshot_encoder, _rmt_ticks, _pulse_level, _idle_level);
|
||||||
|
|
||||||
if (!result.success)
|
if (!result.success)
|
||||||
{
|
{
|
||||||
// Cleanup previously allocated channels on failure
|
// Cleanup previously allocated channels on failure
|
||||||
|
|
@ -97,7 +99,7 @@ dshot_result_t DShotRMT::begin()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, dshot_msg_code_t::DSHOT_INIT_SUCCESS};
|
return {true, DSHOT_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send throttle value
|
// Send throttle value
|
||||||
|
|
@ -106,7 +108,7 @@ dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
||||||
// A throttle value of 0 is a disarm command
|
// A throttle value of 0 is a disarm command
|
||||||
if (throttle == 0)
|
if (throttle == 0)
|
||||||
{
|
{
|
||||||
return sendCommand(dshotCommands_e::DSHOT_CMD_MOTOR_STOP);
|
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constrain throttle to the valid DShot range
|
// Constrain throttle to the valid DShot range
|
||||||
|
|
@ -121,7 +123,7 @@ dshot_result_t DShotRMT::sendThrottlePercent(float percent)
|
||||||
{
|
{
|
||||||
if (percent < 0.0f || percent > 100.0f)
|
if (percent < 0.0f || percent > 100.0f)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_PERCENT_NOT_IN_RANGE};
|
return {false, DSHOT_PERCENT_NOT_IN_RANGE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map percent to DShot throttle range
|
// Map percent to DShot throttle range
|
||||||
|
|
@ -135,7 +137,7 @@ dshot_result_t DShotRMT::sendCommand(uint16_t command_value)
|
||||||
// Validate the integer command value before casting
|
// Validate the integer command value before casting
|
||||||
if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX)
|
if (command_value < DSHOT_CMD_MOTOR_STOP || command_value > DSHOT_CMD_MAX)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_COMMAND_NOT_VALID};
|
return {false, DSHOT_COMMAND_NOT_VALID};
|
||||||
}
|
}
|
||||||
return sendCommand(static_cast<dshotCommands_e>(command_value));
|
return sendCommand(static_cast<dshotCommands_e>(command_value));
|
||||||
}
|
}
|
||||||
|
|
@ -165,11 +167,11 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command)
|
||||||
// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay.
|
// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay.
|
||||||
dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us)
|
dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us)
|
||||||
{
|
{
|
||||||
dshot_result_t result = {false, dshot_msg_code_t::DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
dshot_result_t result = {false, DSHOT_UNKNOWN, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
||||||
|
|
||||||
if (!_isValidCommand(command))
|
if (!_isValidCommand(command))
|
||||||
{
|
{
|
||||||
result.result_code = dshot_msg_code_t::DSHOT_INVALID_COMMAND;
|
result.result_code = DSHOT_INVALID_COMMAND;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +200,7 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_co
|
||||||
|
|
||||||
if (result.success)
|
if (result.success)
|
||||||
{
|
{
|
||||||
result.result_code = dshot_msg_code_t::DSHOT_COMMAND_SUCCESS;
|
result.result_code = DSHOT_COMMAND_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -207,11 +209,11 @@ dshot_result_t DShotRMT::sendCommand(dshotCommands_e command, uint16_t repeat_co
|
||||||
// Get telemetry data
|
// Get telemetry data
|
||||||
dshot_result_t DShotRMT::getTelemetry()
|
dshot_result_t DShotRMT::getTelemetry()
|
||||||
{
|
{
|
||||||
dshot_result_t result = {false, dshot_msg_code_t::DSHOT_TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
dshot_result_t result = {false, DSHOT_TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
||||||
|
|
||||||
if (!_is_bidirectional)
|
if (!_is_bidirectional)
|
||||||
{
|
{
|
||||||
result.result_code = dshot_msg_code_t::DSHOT_BIDIR_NOT_ENABLED;
|
result.result_code = DSHOT_BIDIR_NOT_ENABLED;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +235,7 @@ dshot_result_t DShotRMT::getTelemetry()
|
||||||
result.success = true;
|
result.success = true;
|
||||||
result.erpm = erpm;
|
result.erpm = erpm;
|
||||||
result.motor_rpm = motor_rpm;
|
result.motor_rpm = motor_rpm;
|
||||||
result.result_code = dshot_msg_code_t::DSHOT_TELEMETRY_SUCCESS;
|
result.result_code = DSHOT_TELEMETRY_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,8 +277,6 @@ dshot_result_t DShotRMT::_executeCommand(dshotCommands_e command)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Private Packet Management Functions
|
// Private Packet Management Functions
|
||||||
dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) const
|
dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) const
|
||||||
{
|
{
|
||||||
|
|
@ -338,7 +338,7 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
||||||
// Ensure enough time has passed since the last transmission
|
// Ensure enough time has passed since the last transmission
|
||||||
if (!_isFrameIntervalElapsed())
|
if (!_isFrameIntervalElapsed())
|
||||||
{
|
{
|
||||||
return {true, dshot_msg_code_t::DSHOT_NONE};
|
return {true, DSHOT_NONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
_encoded_frame_value = _buildDShotFrameValue(packet);
|
_encoded_frame_value = _buildDShotFrameValue(packet);
|
||||||
|
|
@ -355,12 +355,12 @@ dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
||||||
|
|
||||||
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK)
|
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &tx_config) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_TRANSMISSION_FAILED};
|
return {false, DSHOT_TRANSMISSION_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
_recordFrameTransmissionTime(); // Reset the timer for the next frame
|
_recordFrameTransmissionTime(); // Reset the timer for the next frame
|
||||||
|
|
||||||
return {true, dshot_msg_code_t::DSHOT_TRANSMISSION_SUCCESS};
|
return {true, DSHOT_TRANSMISSION_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function needs to be fast, as it generates the RMT symbols just before sending
|
// This function needs to be fast, as it generates the RMT symbols just before sending
|
||||||
|
|
|
||||||
123
src/DShotRMT.h
123
src/DShotRMT.h
|
|
@ -1,134 +1,107 @@
|
||||||
/**
|
/**
|
||||||
* @file DShotRMT.h
|
* @file DShotRMT.h
|
||||||
* @brief Optimized DShot signal generation using ESP32 RMT with bidirectional support
|
* @brief DShot signal generation using ESP32 RMT with bidirectional support
|
||||||
* @author Wastl Kraus
|
* @author Wastl Kraus
|
||||||
* @date 2025-09-18
|
* @date 2025-06-11
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <atomic>
|
||||||
|
#include <driver/gpio.h>
|
||||||
#include <driver/rmt_tx.h>
|
#include <driver/rmt_tx.h>
|
||||||
#include <driver/rmt_rx.h>
|
#include <driver/rmt_rx.h>
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include "dshot_definitions.h"
|
#include "dshot_definitions.h"
|
||||||
#include <driver/rmt_encoder.h>
|
|
||||||
#include "dshot_config.h"
|
#include "dshot_config.h"
|
||||||
|
|
||||||
// DShotRMT Class Definition
|
//
|
||||||
class DShotRMT
|
class DShotRMT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Constructor for DShotRMT with GPIO number.
|
// Constructor with GPIO number
|
||||||
explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count);
|
||||||
|
|
||||||
// Constructor for DShotRMT with Arduino pin number.
|
// Constructor using pin number
|
||||||
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~DShotRMT();
|
~DShotRMT();
|
||||||
|
|
||||||
// Public Core Functions
|
// Initialize DShotRMT
|
||||||
// Initializes the DShot RMT channels and encoder.
|
|
||||||
dshot_result_t begin();
|
dshot_result_t begin();
|
||||||
|
|
||||||
// Sends a throttle value as a percentage (0.0-100.0) to the ESC.
|
// Send throttle value
|
||||||
dshot_result_t sendThrottlePercent(float percent);
|
|
||||||
|
|
||||||
// Sends a raw throttle value (48-2047) to the ESC. A value of 0 sends a motor stop command.
|
|
||||||
dshot_result_t sendThrottle(uint16_t throttle);
|
dshot_result_t sendThrottle(uint16_t throttle);
|
||||||
|
|
||||||
// Sends a DShot command (0-47) to the ESC.
|
// Send throttle value as a percentage
|
||||||
dshot_result_t sendCommand(dshotCommands_e command);
|
dshot_result_t sendThrottlePercent(float percent);
|
||||||
// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay.
|
|
||||||
dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us);
|
|
||||||
// Sends a DShot command (0-47) to the ESC by accepting an integer value.
|
// Sends a DShot command (0-47) to the ESC by accepting an integer value.
|
||||||
dshot_result_t sendCommand(uint16_t command_value);
|
dshot_result_t sendCommand(uint16_t command_value);
|
||||||
|
|
||||||
// Retrieves telemetry data from the ESC.
|
// Sends a DShot command (0-47) to the ESC.
|
||||||
|
dshot_result_t sendCommand(dshotCommands_e command);
|
||||||
|
|
||||||
|
// Sends a DShot command (0-47) to the ESC with a specified repeat count and delay.
|
||||||
|
dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us);
|
||||||
|
|
||||||
|
// Get telemetry data
|
||||||
dshot_result_t getTelemetry();
|
dshot_result_t getTelemetry();
|
||||||
|
|
||||||
// Sets the motor spin direction. 'true' for reversed, 'false' for normal.
|
// Reverse motor direction directly
|
||||||
dshot_result_t setMotorSpinDirection(bool reversed);
|
dshot_result_t setMotorSpinDirection(bool reversed);
|
||||||
|
|
||||||
// Sends a command to the ESC to save its current settings. Use with caution as this writes to ESC's non-volatile memory.
|
// Use with caution
|
||||||
dshot_result_t saveESCSettings();
|
dshot_result_t saveESCSettings();
|
||||||
|
|
||||||
// Public Utility & Info Functions
|
// Getters for DShot info
|
||||||
// Sets the motor magnet count for RPM calculation.
|
|
||||||
void setMotorMagnetCount(uint16_t magnet_count);
|
|
||||||
|
|
||||||
// Gets the current DShot mode.
|
|
||||||
dshot_mode_t getMode() const { return _mode; }
|
dshot_mode_t getMode() const { return _mode; }
|
||||||
|
|
||||||
// Checks if bidirectional DShot is enabled.
|
|
||||||
bool isBidirectional() const { return _is_bidirectional; }
|
bool isBidirectional() const { return _is_bidirectional; }
|
||||||
|
uint16_t getThrottleValue() const { return _last_throttle; }
|
||||||
// Gets the encoded frame value.
|
|
||||||
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
||||||
|
|
||||||
// Gets the last transmitted throttle value.
|
|
||||||
uint16_t getThrottleValue() const { return _packet.throttle_value; }
|
|
||||||
|
|
||||||
// Testing return "verbose" messages
|
|
||||||
const char *getDShotMsg(dshot_result_t &result) const { return (_get_result_code_str(result.result_code)); }
|
|
||||||
|
|
||||||
// Deprecated Methods
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- UTILITY METHODS ---
|
|
||||||
bool _isValidCommand(dshotCommands_e command) const;
|
|
||||||
dshot_result_t _executeCommand(dshotCommands_e command);
|
|
||||||
|
|
||||||
// Core Configuration Variables
|
|
||||||
gpio_num_t _gpio;
|
gpio_num_t _gpio;
|
||||||
dshot_mode_t _mode;
|
dshot_mode_t _mode;
|
||||||
bool _is_bidirectional;
|
bool _is_bidirectional;
|
||||||
uint16_t _motor_magnet_count;
|
uint16_t _motor_magnet_count;
|
||||||
const dshot_timing_us_t &_dshot_timing;
|
dshot_timing_us_t _dshot_timing;
|
||||||
uint64_t _frame_timer_us = 0;
|
|
||||||
|
|
||||||
// Timing & Packet Variables
|
|
||||||
rmt_ticks_t _rmt_ticks{};
|
|
||||||
uint16_t _last_throttle = dshotCommands_e::DSHOT_CMD_MOTOR_STOP;
|
|
||||||
uint64_t _last_transmission_time_us = 0;
|
|
||||||
uint64_t _last_command_timestamp = 0;
|
|
||||||
uint16_t _encoded_frame_value = 0;
|
|
||||||
dshot_packet_t _packet{};
|
|
||||||
uint16_t _pulse_level = 1; // DShot protocol: Signal is idle-low, so pulses start by going HIGH.
|
|
||||||
uint16_t _idle_level = 0; // DShot protocol: Signal returns to LOW after the high pulse.
|
|
||||||
|
|
||||||
// RMT Hardware Handles
|
|
||||||
rmt_channel_handle_t _rmt_tx_channel = nullptr;
|
rmt_channel_handle_t _rmt_tx_channel = nullptr;
|
||||||
rmt_channel_handle_t _rmt_rx_channel = nullptr;
|
rmt_channel_handle_t _rmt_rx_channel = nullptr;
|
||||||
rmt_encoder_handle_t _dshot_encoder = nullptr;
|
rmt_encoder_handle_t _dshot_encoder = nullptr;
|
||||||
|
rmt_ticks_t _rmt_ticks;
|
||||||
|
uint16_t _pulse_level = 1; // Default to high
|
||||||
|
uint16_t _idle_level = 0; // Default to low
|
||||||
|
uint64_t _last_transmission_time_us = 0;
|
||||||
|
uint64_t _frame_timer_us = 0;
|
||||||
|
uint16_t _last_throttle = 0;
|
||||||
|
dshot_packet_t _packet;
|
||||||
|
uint16_t _encoded_frame_value = 0;
|
||||||
|
uint64_t _last_command_timestamp = 0;
|
||||||
|
|
||||||
|
// Telemetry related
|
||||||
|
std::atomic<uint16_t> _last_erpm_atomic = 0;
|
||||||
|
std::atomic<bool> _telemetry_ready_flag_atomic = false;
|
||||||
|
rmt_rx_event_callbacks_t _rx_event_callbacks = {
|
||||||
|
.on_recv_done = _on_rx_done,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Private helper functions
|
||||||
// Bidirectional / Telemetry Variables
|
bool _isValidCommand(dshotCommands_e command) const;
|
||||||
rmt_rx_event_callbacks_t _rx_event_callbacks{};
|
dshot_result_t _executeCommand(dshotCommands_e command);
|
||||||
std::atomic<uint16_t> _last_erpm_atomic{0};
|
|
||||||
std::atomic<bool> _telemetry_ready_flag_atomic{false};
|
|
||||||
|
|
||||||
// Private Packet Management Functions
|
|
||||||
dshot_packet_t _buildDShotPacket(const uint16_t &value) const;
|
dshot_packet_t _buildDShotPacket(const uint16_t &value) const;
|
||||||
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const;
|
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const;
|
||||||
uint16_t _calculateCRC(const uint16_t &data) const;
|
uint16_t _calculateCRC(const uint16_t &data) const;
|
||||||
void _preCalculateRMTTicks();
|
void _preCalculateRMTTicks();
|
||||||
|
|
||||||
// Private Frame Processing Functions
|
|
||||||
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
||||||
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols) const;
|
uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const;
|
||||||
|
bool IRAM_ATTR _isFrameIntervalElapsed() const;
|
||||||
// Private Timing Control Functions
|
|
||||||
bool _isFrameIntervalElapsed() const;
|
|
||||||
void _recordFrameTransmissionTime();
|
void _recordFrameTransmissionTime();
|
||||||
|
|
||||||
// Static Callback Functions
|
// Static Callback Functions
|
||||||
static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
static bool IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Include utility functions after class definition
|
#include "dshot_utils.h" // Workround for util functions
|
||||||
#include "dshot_utils.h"
|
|
||||||
|
|
@ -17,15 +17,15 @@ dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch
|
||||||
|
|
||||||
if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK)
|
if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED};
|
return {false, DSHOT_TX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rmt_enable(*out_channel) != DSHOT_OK)
|
if (rmt_enable(*out_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_TX_INIT_FAILED};
|
return {false, DSHOT_TX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, dshot_msg_code_t::DSHOT_TX_INIT_SUCCESS};
|
return {true, DSHOT_TX_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize the RMT RX channel
|
// Function to initialize the RMT RX channel
|
||||||
|
|
@ -43,19 +43,16 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch
|
||||||
.signal_range_max_ns = DSHOT_PULSE_MAX_NS,
|
.signal_range_max_ns = DSHOT_PULSE_MAX_NS,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK)
|
return {false, DSHOT_RX_INIT_FAILED};
|
||||||
{
|
|
||||||
return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK)
|
if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_CALLBACK_REGISTERING_FAILED};
|
return {false, DSHOT_CALLBACK_REGISTERING_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rmt_enable(*out_channel) != DSHOT_OK)
|
if (rmt_enable(*out_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_RX_INIT_FAILED};
|
return {false, DSHOT_RX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the receiver to wait for incoming telemetry data
|
// Start the receiver to wait for incoming telemetry data
|
||||||
|
|
@ -63,10 +60,10 @@ dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_ch
|
||||||
size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
||||||
if (rmt_receive(*out_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK)
|
if (rmt_receive(*out_channel, rx_symbols, rx_size_bytes, &rmt_rx_config) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_RECEIVER_FAILED};
|
return {false, DSHOT_RECEIVER_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, dshot_msg_code_t::DSHOT_RX_INIT_SUCCESS};
|
return {true, DSHOT_RX_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize the DShot RMT encoder
|
// Function to initialize the DShot RMT encoder
|
||||||
|
|
@ -91,8 +88,8 @@ dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_t
|
||||||
|
|
||||||
if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK)
|
if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, dshot_msg_code_t::DSHOT_ENCODER_INIT_FAILED};
|
return {false, DSHOT_ENCODER_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, dshot_msg_code_t::DSHOT_ENCODER_INIT_SUCCESS};
|
return {true, DSHOT_ENCODER_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ typedef struct rmt_ticks
|
||||||
} rmt_ticks_t;
|
} rmt_ticks_t;
|
||||||
|
|
||||||
// Enum class for specific error and success codes
|
// Enum class for specific error and success codes
|
||||||
enum class dshot_msg_code_t
|
enum dshot_msg_code_t
|
||||||
{
|
{
|
||||||
DSHOT_NONE = 0,
|
DSHOT_NONE = 0,
|
||||||
DSHOT_UNKNOWN,
|
DSHOT_UNKNOWN,
|
||||||
|
|
@ -161,90 +161,3 @@ const dshot_timing_us_t DSHOT_TIMING_US[] = {
|
||||||
{0.83, 0.67}, // DSHOT1200
|
{0.83, 0.67}, // DSHOT1200
|
||||||
{0.00, 0.00} // DSHOT_MODE_MAX (dummy entry)
|
{0.00, 0.00} // DSHOT_MODE_MAX (dummy entry)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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";
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,93 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "DShotRMT.h" // Include the full class definition
|
#include "DShotRMT.h" // Include the full class definition
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
// Helper to get result code string
|
||||||
|
inline const char *_get_result_code_str(dshot_msg_code_t code)
|
||||||
|
{
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case DSHOT_NONE:
|
||||||
|
return NONE;
|
||||||
|
case DSHOT_UNKNOWN:
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
case DSHOT_TX_INIT_FAILED:
|
||||||
|
return TX_INIT_FAILED;
|
||||||
|
case DSHOT_RX_INIT_FAILED:
|
||||||
|
return RX_INIT_FAILED;
|
||||||
|
case DSHOT_ENCODER_INIT_FAILED:
|
||||||
|
return ENCODER_INIT_FAILED;
|
||||||
|
case DSHOT_CALLBACK_REGISTERING_FAILED:
|
||||||
|
return CALLBACK_REGISTERING_FAILED;
|
||||||
|
case DSHOT_RECEIVER_FAILED:
|
||||||
|
return RECEIVER_FAILED;
|
||||||
|
case DSHOT_TRANSMISSION_FAILED:
|
||||||
|
return TRANSMISSION_FAILED;
|
||||||
|
case DSHOT_THROTTLE_NOT_IN_RANGE:
|
||||||
|
return THROTTLE_NOT_IN_RANGE;
|
||||||
|
case DSHOT_PERCENT_NOT_IN_RANGE:
|
||||||
|
return PERCENT_NOT_IN_RANGE;
|
||||||
|
case DSHOT_COMMAND_NOT_VALID:
|
||||||
|
return COMMAND_NOT_VALID;
|
||||||
|
case DSHOT_BIDIR_NOT_ENABLED:
|
||||||
|
return BIDIR_NOT_ENABLED;
|
||||||
|
case DSHOT_TELEMETRY_FAILED:
|
||||||
|
return TELEMETRY_FAILED;
|
||||||
|
case DSHOT_INVALID_MAGNET_COUNT:
|
||||||
|
return INVALID_MAGNET_COUNT;
|
||||||
|
case DSHOT_INVALID_COMMAND:
|
||||||
|
return INVALID_COMMAND;
|
||||||
|
case DSHOT_TIMING_CORRECTION:
|
||||||
|
return TIMING_CORRECTION;
|
||||||
|
case DSHOT_INIT_FAILED:
|
||||||
|
return INIT_FAILED;
|
||||||
|
case DSHOT_INIT_SUCCESS:
|
||||||
|
return INIT_SUCCESS;
|
||||||
|
case DSHOT_TX_INIT_SUCCESS:
|
||||||
|
return TX_INIT_SUCCESS;
|
||||||
|
case DSHOT_RX_INIT_SUCCESS:
|
||||||
|
return RX_INIT_SUCCESS;
|
||||||
|
case DSHOT_ENCODER_INIT_SUCCESS:
|
||||||
|
return ENCODER_INIT_SUCCESS;
|
||||||
|
case DSHOT_ENCODING_SUCCESS:
|
||||||
|
return ENCODING_SUCCESS;
|
||||||
|
case DSHOT_TRANSMISSION_SUCCESS:
|
||||||
|
return TRANSMISSION_SUCCESS;
|
||||||
|
case DSHOT_TELEMETRY_SUCCESS:
|
||||||
|
return TELEMETRY_SUCCESS;
|
||||||
|
case DSHOT_COMMAND_SUCCESS:
|
||||||
|
return COMMAND_SUCCESS;
|
||||||
|
default:
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper to quick print DShot result codes
|
// Helper to quick print DShot result codes
|
||||||
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
|
inline void printDShotResult(dshot_result_t &result, Stream &output = Serial)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue