Skip to content

RaftCoreLEDPixels

Rob Dobson edited this page Nov 1, 2026 · 4 revisions

Raft Core LEDPixels

LEDPixels drives addressable LED strips (WS2812B, SK6812, WS2805 and similar) on ESP32 chips using the RMT peripheral. It splits the LEDs into named segments, and each segment can run its own animated pattern or be set directly from code or through the REST API.

The source is in RaftCore at components/core/LEDPixels.

  • Configuration: see LEDPixels JSON Configuration for every strip and segment setting.
  • REST API: LEDPixelsDevice registers the led endpoint, described below.
  • Patterns: animations are classes derived from LEDPatternBase, described below.

Architecture

                         LEDPixelsDevice  (RaftDevice: JSON config, "led" REST endpoint)
                               │ owns
                               ▼
                           LEDPixels
          ┌────────────────────┼─────────────────────────┐
          ▼                    ▼                         ▼
   pixel buffer        LEDSegment[]  (LEDPixelIF)    ESP32RMTLedStrip[]
 std::vector<LEDPixel>  name, start, num,            one per strip: RMT TX channel,
 (all strips joined)    brightness, colour order,    encoder, power pin
          ▲             current LEDPatternBase*
          │ writes             │ setRGB/setHSV/show
          └────────────────────┘
Class Role
LEDPixels Top-level object. Owns the pixel buffer, segments, strip drivers and the pattern registry.
LEDPixelsDevice RaftDevice wrapper. Reads the device JSON, registers the built-in Status pattern, runs LEDPixels::loop() and provides the led REST endpoint. Applications subclass it to add patterns and endpoints.
LEDSegment A named range of pixels. It implements LEDPixelIF (the interface patterns draw through), applies brightness, colour order and optional index mapping, and runs one pattern at a time.
LEDPatternBase Base class for patterns. A pattern draws on one segment through LEDPixelIF.
ESP32RMTLedStrip Hardware driver for one strip: RMT channel, bit timing, power control, and skipping frames that haven't changed.
LEDPixel One pixel, holding up to 5 channel bytes in wire order.
LEDPixHSV HSV colour helper (hue 0..359, saturation 0..100, value 0..100), with conversions to RGB and RGBWW and interpolation.
LEDPixelConfig, LEDStripConfig, LEDSegmentConfig Configuration objects, filled from JSON or set directly.

Update cycle

  1. LEDPixelsDevice::loop() calls LEDPixels::loop() on every main loop.
  2. For each segment, LEDSegment::loop() stops the pattern if its forMs time has run out; otherwise it calls the pattern's loop().
  3. The pattern checks its own timer and, when it's time to draw, writes pixels through LEDPixelIF and calls _pixels.show(). For a segment, show() only sets a "show required" flag.
  4. If the flag is set, LEDPixels calls show(), which sends the whole pixel buffer to every strip.
  5. ESP32RMTLedStrip::showPixels() copies its part of the buffer and skips the transmission if the bytes are the same as the last frame sent. Otherwise it powers the strip on if needed and starts a non-blocking RMT transmission.

Because unchanged frames are skipped, a pattern can call show() on every update without causing extra RMT traffic.

Using LEDPixels in an application

RaftCore doesn't register LEDPixelsDevice in the device factory itself. The application registers it, or a subclass, under the class name used in the SysType (normally LEDPixels):

#include "LEDPixelsDevice.h"

// In main.cpp, before the Raft framework starts
deviceFactory.registerDevice("LEDPixels", LEDPixelsDevice::create);

Then add a device entry to the SysType:

"DevMan": {
    "Devices": [
        {
            "class": "LEDPixels",
            "name": "LEDs",
            "strips": [ { "pin": 18, "num": 12 } ],
            "segments": [ { "name": "ring", "start": 0, "num": 12, "pattern": "Status" } ]
        }
    ]
}

To add your own patterns or endpoints, subclass LEDPixelsDevice. See Example: an application LED device.

LEDPixelsDevice::getLEDPixels() gives access to the LEDPixels object, so other code can drive a pixel directly, e.g. a low-power indication (RaftCore after 1.65.0). Call it from the task that runs the device, with any patterns on those pixels stopped, or the next pattern update overwrites the pixel.

REST API (led)

LEDPixelsDevice registers one GET endpoint, led. Like all Raft REST endpoints it can be called over any channel: HTTP (http://<ip>/api/led/...), the serial console, BLE or WebSocket via RICREST, and so on.

led/<segment>/<cmd>[/<data>[/<data2>]][?<name>=<value>&...]
Part Description
<segment> The segment's name (case-insensitive), or its index (0, 1, ...). An unknown name returns {"rslt":"fail","error":"invalidElement"}. An index that is out of range is accepted but does nothing. With no segments configured, the single segment is called All (index 0).
<cmd> One of the commands below (case-insensitive).
<data> Command data: a colour, a list of colours, a pixel index, or a pattern name.
?args Query arguments. Only used by pattern, where they become the pattern's parameters.

Colour formats

Format Example Meaning
RRGGBB FF8000 24-bit RGB in hex. On a 5-channel strip both white channels are set to 0.
RRGGBBCCWW 000000FF40 5-channel colour: red, green, blue, cold white, warm white. On a 3-channel strip the white values are dropped.

setall and setled accept an optional leading #. Over HTTP a # must be URL-encoded as %23 (otherwise the browser treats it as a fragment), so it's simplest to leave it out. The segment's brightnessPC is applied to every colour set through the API.

Commands

Command Data Description
setall (also color, colour) <colour> Stop the segment's pattern and set every pixel in the segment to one colour.
setleds <colour><colour>... Stop the segment's pattern and set pixels 0, 1, 2, ... from a list of colours joined together with no separators. Pixels beyond the list keep their current value. See the note on colour width.
setled (also set) <index>/<colour> Stop the segment's pattern and set one pixel. The index is relative to the segment's start.
off Stop the segment's pattern. The pixels currently keep their last colours. To turn the LEDs off, use setall/000000.
pattern <name> Clear all pixels (in every segment), then start the named pattern on the segment. Query arguments are passed to the pattern as JSON parameters. An empty or unknown name leaves the segment stopped and blank.
listpatterns List the names of all registered patterns.

Any other command returns {"rslt":"fail"}.

Responses

All commands return the standard Raft JSON result:

{"req":"led/ring/setall/FF0000","rslt":"ok"}

listpatterns adds a patterns array. This response is from the example application, which registers three patterns of its own as well as the built-in Status:

{"req":"led/ring/listpatterns","patterns":["Chase","Breathe","Gauge","Status"],"rslt":"ok"}

setled and pattern return ok even if the pixel index or pattern name doesn't exist. Use listpatterns to check which names are available.

Examples

# Set the whole "ring" segment to red
led/ring/setall/FF0000

# Same, addressing the segment by index and using the "colour" alias
led/1/colour/FF0000

# Warm white only on an RGBWW strip
led/ring/setall/0000000080

# Set pixel 3 of the ring to green
led/ring/set/3/00FF00

# Set the first four pixels to red, green, blue, white (4 x 6 hex chars)
led/ring/setleds/FF000000FF000000FFFFFFFF

# Set the first two pixels of an RGBWW ring (2 x 10 hex chars)
led/ring/setleds/FF00000000000000FF00

# Stop any pattern, then blank the ring
led/ring/off
led/ring/setall/000000

# Start the built-in Status pattern with a 1.5 s cycle in dim blue
led/status/pattern/Status?rateMs=1500&rgb=000040

# Run a pattern (from the example application) for 5 seconds; the segment is cleared when it ends
led/ring/pattern/Breathe?rgb1=000000&rgb2=FF0000&rateMs=1000&forMs=5000

# List registered patterns
led/ring/listpatterns

Over HTTP:

curl "http://192.168.1.50/api/led/status/pattern/Status?rateMs=1500&rgb=000040"

setleds colour width

The width of each colour in setleds is worked out from the total length of the data:

  • If the length is a multiple of 10 but not a multiple of 6, each colour is 10 characters (RRGGBBCCWW).
  • Otherwise each colour is 6 characters (RRGGBB).

So a length that is a multiple of 30 (three 10-character colours, or five 6-character ones) is read as 6-character colours. To set 3, 6, 9, ... RGBWW pixels, send one more pixel (for example the existing value), or set them individually with setled.

Pattern parameters from the API

The query arguments of a pattern request are converted to a flat JSON object and passed to the pattern's setup(). For example:

led/ring/pattern/Gauge?namedValue=battPC&l=0&h=100

passes

{"namedValue":"battPC","l":"0","h":"100"}

Values are sent as JSON strings. A value starting with [ or { is inserted as raw JSON. The getLong(), getDouble() and getBool() accessors on RaftJson read numeric strings as numbers, so patterns don't need to handle this.

One parameter name is handled by the segment for every pattern:

Parameter Description
forMs Run the pattern for this many ms, then stop it and clear the segment. 0 or missing means run until replaced, which is the default unless LEDPixels::setPatternRunTimeDefaultMs() sets one.

Other parameter names depend on the pattern. Older help text mentions speed and brightness, but neither the built-in pattern nor the example patterns below read them.

LED Patterns

A pattern is a small state machine that draws on one segment. Patterns are registered with a name and a factory function. When a pattern is started on a segment, a new instance is created, its setup() is called once with the JSON parameters, and then its loop() is called on every main loop until it is replaced, stopped, or times out. The instance is deleted when it stops.

LEDPatternBase

// Factory function type
typedef LEDPatternBase* (*LEDPatternCreateFn)(NamedValueProvider* pNamedValueProvider, LEDPixelIF& pixels);

class LEDPatternBase
{
public:
    LEDPatternBase(NamedValueProvider* pNamedValueProvider, LEDPixelIF& pixels);
    virtual ~LEDPatternBase();

    // Called once after creation with the parameters as JSON (may be nullptr)
    virtual void setup(const char* pParamsJson = nullptr) = 0;

    // Called on every main loop - do your own timing
    virtual void loop() = 0;

protected:
    uint32_t _refreshRateMs = 30;                 // For the derived class's own timing
    NamedValueProvider* _pNamedValueProvider;     // Never null (null provider if none set)
    LEDPixelIF& _pixels;                          // The segment to draw on
};

Notes:

  • loop() is called as often as the main loop runs, so the pattern must check its own timer, usually Raft::isTimeout(millis(), _lastLoopMs, _refreshRateMs). _refreshRateMs is only a member for derived classes to use; the base class doesn't read it.
  • setup() is passed nullptr when a pattern is started from code without parameters. When it is started from the API or from SysType config it gets a JSON object, which may be {}. Initialise defaults in the member declarations so that both cases work.
  • Pixel indices are relative to the segment: 0 to _pixels.getNumPixels() - 1.
  • Patterns run on the main task. setPattern() and stopPattern() must also be called from the main task, because the pattern object is deleted there without a lock.

LEDPixelIF: drawing on a segment

Method Description
setRGB(idx, r, g, b, applyBrightness=true) Set a pixel from 8-bit R, G, B values.
setRGB(idx, 0xRRGGBB, applyBrightness=true) Set a pixel from a packed 24-bit RGB value.
setRGB(idx, const LEDPixel&) Copy a raw pixel (already in wire order). Brightness is not applied.
setHSV(idx, h, s, v) / setHSV(idx, LEDPixHSV&) Set from HSV: hue 0..359, saturation 0..100, value 0..100. Brightness is applied.
setRGBWW(idx, r, g, b, cw, ww, applyBrightness=true) Set all 5 channels. On 3-channel strips the white values are dropped.
clear() Set every pixel in the segment to off and request a show.
getNumPixels() Number of pixels in the segment.
show() Request that the pixel buffer is sent to the hardware after this loop.
stop() Ask for the pattern to be stopped. LEDPixels stops it, clears the segment and shows, on its next loop. Use this for one-shot patterns that end themselves.

The colour order of the segment's strip is applied automatically, so patterns always work in RGB, RGBWW or HSV.

NamedValueProvider: driving patterns from live data

Patterns often need to show device state: battery level, BLE connection, a sensor reading. Instead of linking patterns to other modules, LEDPixels passes them a NamedValueProvider:

class NamedValueProvider
{
public:
    // Return the value of a named parameter, and set isValid if the value is known
    virtual double getNamedValue(const char* pModule, const char* param, bool& isValid) const;
};

The application implements this interface and installs it with:

_ledPixels.setDefaultNamedValueProvider(&myProvider);   // all segments without their own provider
_ledPixels.setNamedValueProvider(segmentIdx, &other);   // one segment

A pattern created with no provider gets a null provider whose values are always invalid, so _pNamedValueProvider is never null. Always check isValid and choose a sensible fallback.

Registering and starting patterns

// Register (replaces any existing pattern with the same name, case-insensitive)
_ledPixels.addPattern("Chase", &LEDPatternChase::create);

// Start on a segment, by index
int32_t ringIdx = _ledPixels.getSegmentIdx("ring");
_ledPixels.setPattern(ringIdx, "Chase", R"({"rateMs":40,"rgb":"#0000FF","forMs":10000})");

// Stop (and clear) the pattern on one segment, or on all segments
_ledPixels.stopPattern(ringIdx, true);
_ledPixels.stopPatterns(true);

A pattern can be started in three ways:

  1. From SysType config: the segment's pattern and patternParams keys. Patterns must be registered before LEDPixels::setup() (in a LEDPixelsDevice subclass, before calling LEDPixelsDevice::setup()), or the initial pattern won't be found.
  2. From the REST API: led/<segment>/pattern/<name>?<params>.
  3. From code: LEDPixels::setPattern().

Built-in pattern: Status

LEDPixelsDevice::setup() registers one pattern, Status (LEDPatternStatus.h). It slowly "breathes" every pixel in the segment between off and a peak colour, to show the device is running. It doesn't use a named-value provider.

Parameter Description Default
rateMs Full breathe cycle (off → peak → off) in ms. 2000
transPts Number of brightness steps in each half of the cycle (minimum 1). The update interval is (rateMs / 2) / transPts, with a minimum of 20 ms. 20
rgb Peak colour as hex (#RRGGBB or RRGGBB). #004000 (dim green)
led/status/pattern/Status?rateMs=3000&rgb=200020

LEDPixelsDevice::setup() registers Status after any patterns a subclass registered before calling it. Because addPattern() replaces a pattern with the same name, a subclass that wants its own Status must register it after LEDPixelsDevice::setup() returns (and then restart it on the segment, since setup will already have started the built-in one).

Writing a pattern: complete example

This pattern lights a block of pixels that moves around the segment like a chaser, with a fading tail. It shows parameter parsing, timing, HSV drawing and a timed self-stop.

#pragma once

#include "RaftCore.h"
#include "LEDPatternBase.h"
#include "LEDPixelIF.h"
#include "LEDPixHSV.h"

class LEDPatternChase : public LEDPatternBase
{
public:
    LEDPatternChase(NamedValueProvider* pNamedValueProvider, LEDPixelIF& pixels) :
        LEDPatternBase(pNamedValueProvider, pixels)
    {
    }

    // Factory function used with LEDPixels::addPattern()
    static LEDPatternBase* create(NamedValueProvider* pNamedValueProvider, LEDPixelIF& pixels)
    {
        return new LEDPatternChase(pNamedValueProvider, pixels);
    }

    // Parse parameters (pParamsJson may be nullptr)
    virtual void setup(const char* pParamsJson = nullptr) override final
    {
        if (pParamsJson)
        {
            RaftJson paramsJson(pParamsJson, false);
            _refreshRateMs = paramsJson.getLong("rateMs", _refreshRateMs);
            _tailLen = paramsJson.getLong("tail", _tailLen);
            _laps = paramsJson.getLong("laps", _laps);
            String rgbStr = paramsJson.getString("rgb", "#0040FF");
            _hsv = LEDPixHSV::fromRGB(Raft::getRGBFromHex(rgbStr).toUint());
        }
        if (_tailLen < 1)
            _tailLen = 1;
    }

    // Called every main loop
    virtual void loop() override final
    {
        if (!Raft::isTimeout(millis(), _lastLoopMs, _refreshRateMs))
            return;
        _lastLoopMs = millis();

        uint32_t numPix = _pixels.getNumPixels();
        if (numPix == 0)
            return;

        // Draw head and fading tail
        _pixels.clear();
        for (uint32_t i = 0; i < _tailLen; i++)
        {
            uint32_t pixIdx = (_headPos + numPix - i) % numPix;
            uint32_t v = _hsv.v * (_tailLen - i) / _tailLen;
            _pixels.setHSV(pixIdx, _hsv.h, _hsv.s, v);
        }
        _pixels.show();

        // Advance; stop after the requested number of laps (0 = forever)
        _headPos = (_headPos + 1) % numPix;
        if (_headPos == 0 && _laps > 0 && ++_lapCount >= _laps)
            _pixels.stop();
    }

private:
    uint32_t _tailLen = 4;
    uint32_t _laps = 0;
    LEDPixHSV _hsv = LEDPixHSV::fromRGB(0x0040FF);
    uint32_t _lastLoopMs = 0;
    uint32_t _headPos = 0;
    uint32_t _lapCount = 0;
};

Register it and use it:

_ledPixels.addPattern("Chase", &LEDPatternChase::create);
led/ring/pattern/Chase?rateMs=40&tail=6&rgb=FF2000&laps=3

The segment's brightnessPC is applied on top of the pattern's colours, so patterns can use full-range colours and the SysType sets the overall level.

Tips for pattern authors

  • Keep loop() short. It runs on the main task and is called for every segment on every loop.
  • Use _pixels.clear() to blank. It also requests a show, so a pattern that turns its pixels off doesn't need to call show() as well.
  • Use HSV for fades and rainbows. LEDPixHSV::interpolate(a, b, factor) blends between two colours, and LEDPixHSV::fromRGB() converts parameters given as hex.
  • Read state through the NamedValueProvider rather than calling other modules directly, so the pattern can be reused and tested.
  • Patterns can't see other segments. If two areas need to be coordinated, make one segment cover both and map the indices inside the pattern, or use setPixelMappingFn().

Example: an application LED device

This example builds on LEDPixelsDevice in a typical application. It:

  1. subclasses LEDPixelsDevice,
  2. registers the application's patterns before calling the base setup(),
  3. installs an application NamedValueProvider so patterns can show live values,
  4. reads an extra setting from the device JSON,
  5. adds an indicator REST endpoint next to led.

Device class

// MyLEDPixelsDevice.h
#include "LEDPixelsDevice.h"

class MyLEDPixelsDevice : public LEDPixelsDevice
{
public:
    MyLEDPixelsDevice(const char* pClassName, const char* pDevConfigJson)
        : LEDPixelsDevice(pClassName, pDevConfigJson) {}

    static RaftDevice* create(const char* pClassName, const char* pDevConfigJson)
    {
        return new MyLEDPixelsDevice(pClassName, pDevConfigJson);
    }

    virtual void setup() override;
    virtual void addRestAPIEndpoints(RestAPIEndpointManager& endpointManager) override;

private:
    int32_t _indicatorSegmentIdx = 0;
    RaftRetCode apiIndicator(const String& reqStr, String& respStr, const APISourceInfo& sourceInfo);
};
// MyLEDPixelsDevice.cpp
#include "MyLEDPixelsDevice.h"
#include "LEDPatternChase.h"
#include "LEDPatternBreathe.h"
#include "LEDPatternGauge.h"

void MyLEDPixelsDevice::setup()
{
    // Register patterns first so initial patterns in the SysType can be found
    _ledPixels.addPattern("Chase", &LEDPatternChase::create);
    _ledPixels.addPattern("Breathe", &LEDPatternBreathe::create);
    _ledPixels.addPattern("Gauge", &LEDPatternGauge::create);

    // Live values for patterns (e.g. battery level, connection state, sensor readings)
    _ledPixels.setDefaultNamedValueProvider(&myNamedValueProvider);

    // Base setup: registers Status, reads strips/segments, starts initial patterns,
    // sends an all-off frame
    LEDPixelsDevice::setup();

    // App-specific setting from the same device JSON (segment name)
    int32_t segIdx = _ledPixels.getSegmentIdx(deviceConfig.getString("indicatorSeg", "status"));
    _indicatorSegmentIdx = segIdx < 0 ? 0 : segIdx;
}

void MyLEDPixelsDevice::addRestAPIEndpoints(RestAPIEndpointManager& endpointManager)
{
    // Keep the standard "led" endpoint
    LEDPixelsDevice::addRestAPIEndpoints(endpointManager);

    // Add an app-specific endpoint
    endpointManager.addEndpoint("indicator", RestAPIEndpoint::ENDPOINT_CALLBACK, RestAPIEndpoint::ENDPOINT_GET,
            std::bind(&MyLEDPixelsDevice::apiIndicator, this,
                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
            "indicator/<colour> - set the indicator segment to a colour");
}

RaftRetCode MyLEDPixelsDevice::apiIndicator(const String& reqStr, String& respStr, const APISourceInfo& sourceInfo)
{
    // Stop any pattern and set the whole indicator segment to one colour
    String colourStr = RestAPIEndpointManager::getNthArgStr(reqStr.c_str(), 1);
    Raft::RGBValue rgb = Raft::getRGBFromHex(colourStr);
    _ledPixels.stopPattern(_indicatorSegmentIdx, false);
    for (uint32_t i = 0; i < _ledPixels.getNumPixels(_indicatorSegmentIdx); i++)
        _ledPixels.setRGB(_indicatorSegmentIdx, i, rgb.r, rgb.g, rgb.b);
    _ledPixels.show();
    return Raft::setJsonBoolResult(reqStr.c_str(), respStr, true);
}
// main.cpp
deviceFactory.registerDevice("LEDPixels", MyLEDPixelsDevice::create);

The subclass can use the protected _ledPixels member directly, and deviceConfig to read extra settings. A matching SysType is the status plus ring example on the configuration page.

The example's patterns

These patterns belong to the example application, not RaftCore. Chase is the complete example above. The other two show common designs:

Name Behaviour Parameters (defaults)
Chase A block of pixels with a fading tail that moves around the segment. rateMs (30), tail (4), laps (0 = forever), rgb (#0040FF)
Breathe Fades every pixel back and forth between two colours, interpolating in HSV. rateMs (1000: half cycle), transPts (10), rgb1 (#000000), rgb2 (#004000)
Gauge Bar graph of a named value between low and high limits, shaded green to red. Reads the value through the NamedValueProvider on every update. refreshRateMs (30), namedValue, l (low limit), h (high limit)

Examples:

led/ring/pattern/Chase?rateMs=40&tail=6&rgb=FF2000
led/ring/pattern/Breathe?rateMs=1500&rgb1=000010&rgb2=0000FF&forMs=8000
led/ring/pattern/Gauge?namedValue=battPC&l=0&h=100
indicator/FF0000

C++ API reference: LEDPixels

Setup and service

Method Description
bool setup(const RaftJsonIF& config) Configure from JSON (format). Returns false if there are no valid strips or a strip driver fails.
bool setup(LEDPixelConfig& config) Configure from a config object.
void loop() Service strip drivers (power-off, peripheral stop) and segment patterns, and show when needed. Call often.

Patterns

Method Description
void addPattern(const String& name, LEDPatternCreateFn createFn) Register a pattern. An existing pattern with the same name (case-insensitive) is replaced.
void getPatternNames(std::vector<String>& names) const Append all registered pattern names to names.
void setPattern(uint32_t segIdx, const String& name, const char* pParamsJson = nullptr) Stop the current pattern (clearing the segment) and start name. An empty name only stops it. Main task only.
void stopPattern(uint32_t segIdx, bool clearPixels) Stop the pattern on one segment, optionally clearing its pixels. Main task only.
void stopPatterns(bool clearPixels) Stop patterns on all segments.
void setPatternRunTimeDefaultMs(uint32_t ms) Default run time for patterns started with setPattern() without forMs (0 = forever).
void setDefaultNamedValueProvider(NamedValueProvider* p) Provider for every segment that doesn't have its own. Call before setup() so initial patterns get it.
void setNamedValueProvider(uint32_t segIdx, NamedValueProvider* p) Provider for one segment. It is used by patterns started after this call.

Segments and pixels

All per-segment methods take a segment index and do nothing if it is out of range. Pixel indices are relative to the segment.

Method Description
uint32_t getNumSegments() const Number of segments.
int32_t getSegmentIdx(const String& name) const Index of a segment by name (case-insensitive), or -1.
uint32_t getNumPixels() const Total pixels across all strips.
uint32_t getNumPixels(uint32_t segIdx) const Pixels in a segment.
void setRGB(segIdx, ledIdx, r, g, b, applyBrightness=true) Set a pixel from R, G, B.
void setRGB(segIdx, ledIdx, uint32_t rgb, applyBrightness=true) Set a pixel from 0xRRGGBB.
void setRGB(segIdx, ledIdx, const LEDPixel& pix) Copy a raw pixel.
void setHSV(segIdx, ledIdx, h, s, v) Set a pixel from HSV (0..359, 0..100, 0..100).
void setRGBWW(segIdx, ledIdx, r, g, b, cw, ww, applyBrightness=true) Set all 5 channels.
void clear(bool showAfterClear) Clear every pixel in all segments.
void clear(uint32_t segIdx, bool showAfterClear) Clear one segment.
void setPixelMappingFn(uint32_t segIdx, LEDPixelMappingFn fn) Replace the segment's index mapping (by default start + ledIdx) with uint32_t fn(uint32_t ledIdx), which returns an index into the whole pixel buffer. Use it for serpentine matrices, reversed strips, or rings whose pixel 0 isn't at the top.

The setters only change the buffer. Call show() afterwards, unless a running pattern will show anyway. Pixel indices are checked against the whole pixel buffer, not the segment, so an index past the end of a segment writes into the next segment.

Output

Method Description
bool show() Send the buffer to every strip. Returns false if any strip skipped the update (for example a previous transmission timed out).
void waitUntilShowComplete() Block until every strip has finished transmitting.
void setShowCB(LEDPixelsShowCB cb) Install a callback that runs before and after each strip is shown.
// The first argument is the strip index (named segmentIdx in the typedef)
typedef std::function<void(uint32_t stripIdx, bool postShow, std::vector<LEDPixel>& pixels)> LEDPixelsShowCB;

_ledPixels.setShowCB([](uint32_t stripIdx, bool postShow, std::vector<LEDPixel>& pixels) {
    if (!postShow && stripIdx == 0)
    {
        // e.g. switch a multiplexer or level shifter before strip 0 is sent
    }
});

Example: direct use without the device wrapper

#include "LEDPixels.h"
#include "LEDPatternStatus.h"

LEDPixels _ledPixels;

void MySysMod::setup()
{
    _ledPixels.addPattern("Status", &LEDPatternStatus::create);
    _ledPixels.setup(config);   // SysMod config with "strips" and "segments" - any RaftJsonIF works
}

void MySysMod::loop()
{
    _ledPixels.loop();
}

void MySysMod::showError()
{
    int32_t seg = _ledPixels.getSegmentIdx("status");
    if (seg < 0)
        return;
    _ledPixels.stopPattern(seg, false);
    for (uint32_t i = 0; i < _ledPixels.getNumPixels(seg); i++)
        _ledPixels.setRGB(seg, i, 0xFF0000);
    _ledPixels.show();
}

Debugging

Uncomment these defines in the source files to enable logging:

Define File Logs
DEBUG_LED_PIXELS_SETUP LEDPixelsDevice.cpp Setup result, pixel and segment counts.
DEBUG_BUS_PIXELS_API LEDPixelsDevice.cpp Each led request with the parsed segment, command and arguments.
DEBUG_LED_PATTERN_REGISTRY LEDPixels.cpp Each addPattern().
DEBUG_LED_PIXELS_LOOP_SHOW LEDPixels.cpp Shows and stop requests triggered by segments.
DEBUG_LED_PIXEL_VALUES (+ DEBUG_LED_PIXEL_NONZERO, _START_PIXEL, _NUM_PIXELS, _NONZERO_ONLY) LEDPixels.cpp Pixel channel values on every show.
DEBUG_LED_FLICKER_DIAG LEDPixels.cpp Timing and first-pixel snapshots for each show, to trace flicker.
DEBUG_LED_SEGMENT_PATTERN_START_STOP, _TRACE, _DURATION LEDSegment.h Pattern start, stop, lookup failures and timeouts.
DEBUG_ESP32RMTLEDSTRIP_SETUP, _SEND, _INIT_RMT, _DEINIT_AFTER_TX, _POWER_CTRL, _FLICKER_DIAG ESP32RMTLedStrip.cpp Driver config, transmissions, RMT setup and teardown, power switching.
DEBUG_LED_PATTERN_STATUS_SETUP LEDPatternStatus.h Built-in Status parameters.

Common problems:

  • Wrong colours (red and green swapped): set the strip's colorOrder (most WS2812B parts are GRB).
  • Initial pattern doesn't start: the pattern was registered after setup(), or the name is misspelt. Check with led/<seg>/listpatterns.
  • LEDs stay on after led/<seg>/off: off only stops the pattern. Send setall/000000 as well.
  • Strip goes dark after a while with pwrPin set: offAfterMs turns power off whatever the pixels show.
  • Flicker on long strips: increase memBlockSymbols, or set blockingShow to true.

Clone this wiki locally