Repository navigation
RaftCoreLEDPixels
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:
LEDPixelsDeviceregisters theledendpoint, described below. -
Patterns: animations are classes derived from
LEDPatternBase, described below.
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. |
-
LEDPixelsDevice::loop()callsLEDPixels::loop()on every main loop. - For each segment,
LEDSegment::loop()stops the pattern if itsforMstime has run out; otherwise it calls the pattern'sloop(). - The pattern checks its own timer and, when it's time to draw, writes pixels through
LEDPixelIFand calls_pixels.show(). For a segment,show()only sets a "show required" flag. - If the flag is set,
LEDPixelscallsshow(), which sends the whole pixel buffer to every strip. -
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.
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.
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. |
| 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.
| 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"}.
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.
# 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"
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.
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.
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.
// 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, usuallyRaft::isTimeout(millis(), _lastLoopMs, _refreshRateMs)._refreshRateMsis only a member for derived classes to use; the base class doesn't read it. -
setup()is passednullptrwhen 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:
0to_pixels.getNumPixels() - 1. - Patterns run on the main task.
setPattern()andstopPattern()must also be called from the main task, because the pattern object is deleted there without a lock.
| 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.
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 segmentA 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.
// 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:
- From SysType config: the segment's
patternandpatternParamskeys. Patterns must be registered beforeLEDPixels::setup()(in aLEDPixelsDevicesubclass, before callingLEDPixelsDevice::setup()), or the initial pattern won't be found. - From the REST API:
led/<segment>/pattern/<name>?<params>. - From code:
LEDPixels::setPattern().
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).
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.
-
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 callshow()as well. -
Use HSV for fades and rainbows.
LEDPixHSV::interpolate(a, b, factor)blends between two colours, andLEDPixHSV::fromRGB()converts parameters given as hex. -
Read state through the
NamedValueProviderrather 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().
This example builds on LEDPixelsDevice in a typical application. It:
- subclasses
LEDPixelsDevice, - registers the application's patterns before calling the base
setup(), - installs an application
NamedValueProviderso patterns can show live values, - reads an extra setting from the device JSON,
- adds an
indicatorREST endpoint next toled.
// 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.
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
| 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. |
| 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. |
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.
| 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
}
});#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();
}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 areGRB). -
Initial pattern doesn't start: the pattern was registered after
setup(), or the name is misspelt. Check withled/<seg>/listpatterns. -
LEDs stay on after
led/<seg>/off:offonly stops the pattern. Sendsetall/000000as well. -
Strip goes dark after a while with
pwrPinset:offAfterMsturns power off whatever the pixels show. -
Flicker on long strips: increase
memBlockSymbols, or setblockingShowtotrue.
Getting Started
- Quick Start
- Architecture at a Glance
- Writing Your First SysMod
- Adding a Comms Channel
- Adding an I2C Device Type
Scaffolding & Building
- Raft CLI
- Web Apps (Device Types, Flasher)
- SysTypes
- Top-Level SysType
- Build Process
- WebUI Build Pipeline
- File System
- Partitions & Flash
- Local Dev Libraries
- Library Developer Guide
Architecture
Built-in SysMods
- NetworkManager
- WiFi Troubleshooting
- BLEManager
- WebServer
- MQTTManager
- SerialConsole
- CommandSerial
- CommandSocket
- CommandFile
- FileManager
- LogManager
- ESPOTAUpdate
- StatePublisher
- Remote Logging
- Data Source Registration
Comms & Protocols
- Stack Overview
- Comms Channels
- ProtocolExchange
- RICREST Protocol
- Real-Time Streams
- Adding REST Endpoints
- Built-in REST Endpoints
- File Download (OKTO)
- OTA Update Flow
Devices & Buses
- DeviceManager
- Device Manager REST API
- Device Factory & Classes
- Device Type Records
- Adding an I2C Device Type
- Device Data Publishing
- Data Logger
- I2C Bus
- I2C Device Scanning
- I2C ID & Polling
- MotorControl Overview
- MotorControl Config
- MotorControl Commands
- Camera
ROS 2
Helpers
Reference