DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
dshot.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file dshot.h
6 * @brief DShot ESC digital throttle protocol codec (DETWS_ENABLE_DSHOT).
7 *
8 * DShot is the digital replacement for analog PWM on brushless-motor ESCs (drones, robotics). Each
9 * command is a 16-bit frame - 11 bits of value, 1 telemetry-request bit, and a 4-bit CRC:
10 *
11 * bits 15..5 value (0 = disarm / command context, 1..47 = special commands, 48..2047 = throttle)
12 * bit 4 telemetry-request
13 * bits 3..0 CRC = xor of the three nibbles of (value<<1 | telemetry)
14 *
15 * For **bidirectional / "extended" DShot** (the ESC sends RPM/telemetry back on the same wire) the CRC
16 * is inverted. This is the wire codec: `detws_dshot_encode` builds the 16-bit frame and
17 * `detws_dshot_decode` validates the CRC and unpacks it. The physical layer (the bit-timed pulse train
18 * at 150/300/600/1200 kbit via the ESP32 RMT peripheral) is the app's transport - `detws_dshot_bit_ns`
19 * gives the high-time for a 0/1 bit at a given rate so a driver can program the RMT symbols.
20 *
21 * Pure, zero heap, no stdlib, host-testable.
22 */
23
24#ifndef DETERMINISTICESPASYNCWEBSERVER_DSHOT_H
25#define DETERMINISTICESPASYNCWEBSERVER_DSHOT_H
26
27#include "ServerConfig.h"
28#include <stddef.h>
29#include <stdint.h>
30
31#if DETWS_ENABLE_DSHOT
32
33/** @brief The standard DShot special commands (value field 0..47; throttle starts at 48). */
34struct DshotCmd
35{
36 static constexpr uint8_t DSHOT_CMD_MOTOR_STOP = 0; ///< disarm / zero throttle.
37 static constexpr uint8_t DSHOT_CMD_BEACON1 = 1; ///< beep (1..5 = rising tones).
38 static constexpr uint8_t DSHOT_CMD_BEACON5 = 5;
39 static constexpr uint8_t DSHOT_CMD_ESC_INFO = 6; ///< request ESC info (telemetry bit must be set).
40 static constexpr uint8_t DSHOT_CMD_SPIN_DIRECTION_1 = 7; ///< set spin direction normal (send 6x).
41 static constexpr uint8_t DSHOT_CMD_SPIN_DIRECTION_2 = 8; ///< set spin direction reversed (send 6x).
42 static constexpr uint8_t DSHOT_CMD_3D_MODE_OFF = 9; ///< disable bidirectional 3D mode (send 6x).
43 static constexpr uint8_t DSHOT_CMD_3D_MODE_ON = 10; ///< enable bidirectional 3D mode (send 6x).
44 static constexpr uint8_t DSHOT_CMD_SETTINGS_REQUEST = 11;
45 static constexpr uint8_t DSHOT_CMD_SAVE_SETTINGS = 12; ///< persist settings (send 6x).
46 // The throttle / value domain is the 11-bit value field (0..2047), so these need a 16-bit type -
47 // uint8_t would silently truncate 2047 to 255.
48 static constexpr uint16_t DSHOT_THROTTLE_MIN = 48; ///< first real throttle step.
49 static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047; ///< last throttle step (2000 steps of resolution).
50 static constexpr uint16_t DSHOT_VALUE_MAX = 2047; ///< widest value the 11-bit field holds.
51};
52
53/**
54 * @brief Build a 16-bit DShot frame.
55 * @param value11 the 11-bit value (0..2047): a throttle (48..2047) or a special command (0..47).
56 * @param telemetry request telemetry on this frame.
57 * @param bidirectional bidirectional/extended DShot (the CRC is inverted).
58 * @return the 16-bit frame `(value<<5) | (telemetry<<4) | crc`, ready to clock out MSB-first. @p value11
59 * above 2047 is masked to 11 bits.
60 */
61uint16_t detws_dshot_encode(uint16_t value11, bool telemetry, bool bidirectional);
62
63/**
64 * @brief Validate + unpack a 16-bit DShot frame.
65 * @param frame the received 16-bit frame.
66 * @param value11 out: the 11-bit value (may be null).
67 * @param telemetry out: the telemetry-request bit (may be null).
68 * @param bidirectional interpret the CRC as the inverted (bidirectional) form.
69 * @return true if the CRC is valid.
70 */
71bool detws_dshot_decode(uint16_t frame, uint16_t *value11, bool *telemetry, bool bidirectional);
72
73/**
74 * @brief High-time (ns) of a bit at a DShot rate. A DShot bit is one pulse per bit-period; a "1" is
75 * high for ~3/4 of the period, a "0" for ~3/8 (the ESC samples the width).
76 * @param rate_kbit one of 150, 300, 600, 1200. Others return 0.
77 * @param bit the bit value (false = 0, true = 1).
78 * @return the high time in nanoseconds, or 0 for an unknown rate.
79 */
80uint32_t detws_dshot_bit_ns(uint16_t rate_kbit, bool bit);
81
82/** @brief The legacy analog-PWM ESC protocols (pulse width carries the throttle), for detws_esc_pwm_ns. */
83enum class DetwsEscPwm : uint8_t
84{
85 DETWS_ESC_PWM, ///< standard servo PWM: 1000-2000 us.
86 DETWS_ESC_ONESHOT125, ///< OneShot125: 125-250 us.
87 DETWS_ESC_ONESHOT42, ///< OneShot42: 42-84 us.
88 DETWS_ESC_MULTISHOT, ///< Multishot: 5-25 us.
89};
90
91/**
92 * @brief Pulse width (ns) for an analog-PWM ESC protocol at a given throttle.
93 * @param throttle_1000 throttle 0..1000 (clamped); 0 = min pulse (idle/arm), 1000 = max.
94 * @param mode one of DetwsEscPwm.
95 * @return the pulse high-time in nanoseconds, linearly mapped across the mode's [min, max] range. These
96 * are driven by the MCPWM peripheral; this is the pure throttle->width mapping the driver needs.
97 */
98uint32_t detws_esc_pwm_ns(uint16_t throttle_1000, DetwsEscPwm mode);
99
100#endif // DETWS_ENABLE_DSHOT
101#endif // DETERMINISTICESPASYNCWEBSERVER_DSHOT_H
User-facing configuration for DeterministicESPAsyncWebServer.