Skip to content

RmtLedDriver

Source: RmtLedDriver.h

RmtLedDriver

class RmtLedDriver
src/light/drivers/RmtLedDriver.h:30

Inherits: DriverBase

Output driver: WS2812B-class addressable LEDs over the ESP32 RMT peripheral, one GPIO and one RMT TX channel per strand fed consecutive slices of the source buffer.

The default on classic-ESP32 and S3 boards, and the example future drivers copy. It fuses the correction and the wire-byte encode into one pass, then hands per-pin slices to the platform.

Prior art: WS2812B on FastLED and WLED, and the clockless RMT techniques of hpwit (Yves Bazin).

Flicker on LEDs that should be off is signal integrity rather than firmware. The playbook is on the drivers page.

Public Attributes

char pins = "" : Comma-separated GPIO list, one RMT TX channel per pin.

char ledsPerPin = "" : Comma-separated lights-per-pin, matched to pins by position.

uint8_t timing = 0 : Wire timing; the default satisfies WS2812, WS2812B and SK6812 at once.

uint16_t t0hNs = 350 : The three numbers behind timing, shown only when it is set to custom.

uint16_t t1hNs = 700 : Custom timing: how long a 1 bit stays high, in nanoseconds.

uint16_t periodNs = 1250 : Custom timing: the whole bit cell, in nanoseconds.

bool loopbackTest = false : On-device loopback self-test: transmit a known pattern over a jumper and verify it.

int8_t loopbackTxPin = -1 : Optional TX override for the test, so it runs on a dedicated jumper.

int8_t loopbackRxPin = -1 : Jumper this to the TX pin for the test (unset = -1 by default; bench used pin 5).

bool loopbackFrame = false : Whole-frame stress variant: transmit real frames back to back and verify every bit.

Public Methods

inline RmtLedDriver() : Default to the GRB preset, which is how WS2812 and SK6812 strips are physically wired.

virtual inline void defineDriverControls() override : Bind the window, the two pin text lists, and the loopback self-test controls.

virtual inline bool affectsPrepare(const char * name) const override : Which controls re-parse and re-init the RMT channels live, through the prepare sweep.

virtual inline void onControlChanged(const char * name) override : React to a control change, re-running the loopback while its mode is on.

virtual inline void setup() override : One-time wiring: parse the pin lists; the acquire lives in [prepare()].

virtual inline void release() override : Release the RMT channels, free the frame buffer, and clear the shared error state.

virtual inline void prepare() override : Re-parse, resize the frame buffer and re-init the channels, off the hot path.

virtual inline void onCorrectionChanged() override : Re-derive the per-pin offsets when the preset changes the output channel count.

virtual inline void setSourceBuffer(Buffer * buf) override : Point the driver at the source frame buffer, re-parsing and resizing to match.

virtual inline void tick() override : Fuse the correction and the encode in one pass, then start every pin before waiting.

inline bool waitForPins() : Wait on every pin that started, and report whether they all finished.

inline const uint8_t * frameBuffer() const : Test-only accessors, which pin the buffer lifecycle and the multi-pin slice arithmetic.

inline size_t frameCapacity() const : Bytes allocated in the symbol buffer. Test-only.

inline uint8_t pinCount() const : Number of parsed output pins (0 = idle). Test-only.

inline nrOfLightsType pinLightCount(uint8_t i) const : Lights on pin i (0 if out of range). Test-only.

inline size_t pinFrameOffsetBytes(uint8_t i) const : Byte offset of pin i's slice in the symbol buffer (0 if out of range). Test-only.

inline const LedDriverConfig & wireTimingForTest() const : The wire timing the timing control resolved to, in the nanoseconds a datasheet states.

Public Static Attributes

constexpr uint8_t kMaxPins = 8 : Hard cap on the pin arrays: the largest RMT TX group of any supported chip.

constexpr uint8_t kTimingCustom = 3 : The index of custom in the table below: the one option that reads the three fields.

constexpr const char * kTimingOptions = { "800kHz WS2812B/SK6812", "400kHz WS2811", "800kHz WS2811 fast", "custom", } : The presets, in the order the select lists them.

constexpr uint32_t kResolutionHz = 40'000'000 : The RMT tick clock this driver requests, 25 ns per tick.

More info

The wire contract

One-wire NRZ at 800 kHz. Each bit is a 1.25 µs cell starting high then dropping low, the high duration encoding the bit, MSB-first per byte. Correction applies channel order before the encode, and a frame latches on 300 µs of idle-low. Timings convert to RMT ticks from the granted resolution, never hard-coded.

Which RMT API

The peripheral half uses the modern RMT driver, not the legacy channel-numbered one. That is not a preference: the legacy driver was removed entirely in ESP-IDF v6, which is the build IDF. On chips whose RMT has a DMA backend, the whole-frame loopback capture uses it.

RmtLedDriver card