dshot-codec 0.1.0

Encodes Dshot commands and decodes Dshot telemetry
Documentation

dshot-codec Rust Cratelicense License open source

dshot-codec supports the encoding and decoding of Dshot data.

dshot-codec implements the hardware-independent part of Dshot, it does no hardware manipulation itself. Rather it provides a foundation that can be used to write Dshot device drivers.

More specifically dshot-codec supports the encoding of commands sent from the Flight Controller(FC) to the ESC (Electronic Speed Controller), and the decoding of telemetry sent from the ESC to the FC.

It has a number of structs and enums to do this:

Purpose Mode
DshotCommandFrame Send an RPM value or a command to the ESC Unidirectional or Bidirectional
DshotSpeed enum, protocol speed Unidirectional or Bidirectional
DshotCommand enum of commands available (eg Beep1) Unidirectional or Bidirectional
DshotError enum, Error handling Unidirectional or Bidirectional
DshotTelemetryFrame Decoded data received from the ESC Bidirectional
TelemetryType enum type of telemetry requested/received Bidirectional
GcrFrame Used in decoding ESC telemetry Bidirectional
NrziFrame Used in decoding ESC telemetry Bidirectional

Sending a command

DshotCommandFrame is used to send a command (ie set the motor eRPM, or Beep) from the FC to the ESC.

It is used in both unidirectional and bidirectional mode (see the table below to see how this is achieved).

Unidirectional vs Bidirectional Dshot Modes

Operational Aspect Unidirectional (Throttle) Unidirectional (Commands) Bidirectional (Throttle & Commands)
Telemetry Bit false (Set to 0) true (Set to 1) true (Set to 1)
XOR Checksum Mode Standard Bitwise Inverted Bitwise Inverted
ESC Action Executes throttleRemains silent Executes commandReturns a ghost reply Executes commandReturns a telemetry frame
FC Pin Mode Permanent Output Permanent Output Flips from Output to Input right after TX
Repetition Gate Streams continuously Must repeat ~10 times to execute Commands must repeat ~10 times to execute
FC Software Action Fire-and-forget stream Fire-and-forget stream Transmits, then pauses ~30µs to capture GcrFrame

Decoding telemetry data from the ESC

The ESC sends data in an NRZI encoded bitstream.

On STM32 microcontrollers this data is captured in a 21-bit NrziFrame which is decoded to a 20-bit GcrFrame.

On Raspberry Pi Pico microcontrollers PIO is used to capture this data directly in a GcrFrame.

This GcrFrame is then decoded to a DshotTelemetryFrame which can then be directly used by the host software.

dshot-codec contains methods for decoding NrziFrames and GcrFrames.

The process is illustrated below:

STM32 microcontrollers

[ Microcontroller Pin via Input Capture ]
                    │
                    ▼
             [ 21-bit NRZI ]
                    │  (Strip leading zero, decode NRZI transitions)
                    ▼
             [ 20-bit GCR ]
                    │  (Split into 4x 5-bit chunks, apply GCR lookup)
                    ▼
       [ 16-bit DshotTelemetryFrame ]

Raspberry Pi microcontrollers

These use PIO to capture the pin transitions directly as GCR

[ Microcontroller Pin via PIO ]
                    │
                    ▼
             [ 20-bit GCR ]
                    │  (Split into 4x 5-bit chunks, apply GCR lookup)
                    ▼
        [ 16-bit DshotTelemetryFrame ]

NRZI frames

NRZI stands for Non-Return-to-Zero, Inverted.

It is a method of mapping digital binary bits (0s and 1s) into physical voltage changes on a wire. In standard digital communication (like normal Dshot commands), a high voltage represents a 1 and a low voltage represents a 0. This is known as standard NRZ (Non-Return-to-Zero).

NRZI works differently by focusing on the transitions (edges) rather than the absolute voltage levels:

  • A 1 bit forces the signal wire to change state (if it was High, it flips to Low; if it was Low, it flips to High).
  • A 0 bit forces the signal wire to stay the same (no change in voltage level).

Why DShot Telemetry uses GCR + NRZI

Microcontrollers read incoming data by measuring the time between voltage transitions.

If an ESC sent a long string of 0 bits over normal wiring, the voltage line would just sit perfectly flat for a long time. The microcontroller's internal clock would lose synchronization, and incorrectly read the incoming data packet.

By combining GCR and NRZI, the DShot protocol ensures synchronization:

  • GCR ensures that there are never have more than two 0 bits in a row in hte data stream.
  • Because there are mostly 1 bits, NRZI forces the physical wire to constantly flip back and forth between HIGH and LOW.

These constant flips act like a heartbeat, keeping the microcontroller's input capture timers synchronized with the ESC's transmission clock.

Capture Mechanism

Architecture Primary Hardware Peripheral Raw Data Form in RAM
RP2040 / RP2350 PIO + DMA u32 containing raw bit values
STM32 Timer Input Capture + DMA [u32; 21] array of clock timestamps
ESP32 RMT Array of RmtPulse elements specifying the microsecond duration of each high/low peak

Dshot specification

The Dshot protocol is based on W2812B (NeoPixel) protocol.

See also: DSHOT - the missing Handbook.

See https://en.wikipedia.org/wiki/Run-length_limited#GCR:_(0,2)_RLL for details of the GCR encoding.

Variants

Protocol Effective Baud Rate Frame Duration Max Theoretical Refresh Rate
Dshot150 150 Kbps 106.7 μ s 9.37 kHz
Dshot300 300 Kbps 53.3 μ s 18.75 kHz
Dshot600 600 Kbps 26.7 μ s 37.50 kHz

Dshot150 means 150 kilobytes/second, Dshot300 means 300 kilobytes/second

  • T0 is the width of the pulse
  • T1 is the width of gap to the next pulse

WS2812B specification is

    T0H = 400ns +/- 150ns
    T1H = 800ns +/- 150ns
    T0L = 850ns +/- 150ns
    T1L = 450ns +/- 150ns
    TxH+TxL = 1250ns +/- 600ns (T0H + T0L or T1H + T1L)
    W2818B_T0H = 400
    W2818B_T1H = 800
    W2818B_T = 1250

Dshot150 specification is

    T0H = 2500ns (data low pulse width)
    T0L = 4180ns (data low gap width)
    T1H = 5000ns (data high pulse width)
    T1L = 1680ns (data high gap width)
    TxH+TxL = 6680ns  (T0H + T0L or T1H + T1L)

Dshot300 specification is

    T0H = 1250ns (data low pulse width)
    T0L = 2090ns (data low gap width)
    T1H = 2500ns (data high pulse width)
    T1L =  840ns (data high gap width)
    TxH+TxL = 3340ns  (T0H + T0L or T1H + T1L)

Dshot600 specification is

    T0H =  625ns (data low pulse width)
    T0L = 1045ns (data low gap width)
    T1H = 1250ns (data high pulse width)
    T0L =  420ns (data hig gap width)
    TxH+TxL = 1670ns  (T0H + T0L or T1H + T1L)

no_std

This crate is no_std, that it does not link to the standard library and so does not depend on an operating system and uses no allocation. This means it is suitable for embedded system.

License

Licensed under either of:

at your option.