LED Strips
Wiring WS2812 and SK6812 addressable LED strips to an ESP32 for panel backlighting and multi-colour annunciators, and dividing one strip into independent LED Ranges.
An addressable LED strip puts a tiny controller chip inside every LED. The ESP32 sends a stream of colour bytes down one data wire ; each LED keeps the first three bytes it sees and passes the rest on. Hundreds of individually coloured LEDs therefore cost a single GPIO pin — ideal for panel backlighting, where a classic LED-per-pin design runs out of pins fast.
| Chip | Colours | Byte order | Notes |
|---|---|---|---|
| WS2812 / WS2812B | RGB | GRB | The common strip; 60 LEDs per metre is typical |
| SK6812 (RGB) | RGB | GRB | WS2812-compatible clone, same protocol |
| Single pixels / rings | RGB | Varies (RGB, BRG…) | Same protocol; check the byte order if red and green swap |
Power budget
Each LED draws up to ~60 mA at full white (20 mA per colour). Sixty LEDs at full white draw about 3.6 A — far more than the ESP32's 5 V pin or a USB port can supply. Size the power supply for the brightness you actually use, and cap the brightness so the strip can never exceed it.
| LEDs | Full white | At 25 % brightness cap |
|---|---|---|
| 10 | 0.6 A | 0.15 A |
| 60 | 3.6 A | 0.9 A |
| 150 | 9 A | 2.25 A |
Feed the strip from a separate 5 V supply and connect its GND to the ESP32's GND — the data signal needs a common reference. Wire into the DIN end; the arrows printed on the strip point away from it.
| Strip pad | Connect to | Colour |
|---|---|---|
| DIN | GPIO (via level shifter and 300–500 Ω series resistor) | Orange |
| +5V | 5 V power supply (not the ESP32's 5 V pin) | Red |
| GND | Power supply GND and ESP32 GND | Black |
Protecting the strip
- 300–500 Ω resistor in series with DIN, close to the strip, damps ringing and protects the first LED.
- 1000 µF capacitor (6.3 V or higher) across +5V and GND at the strip absorbs the inrush when it powers up.
- Power injection: the strip's thin copper drops voltage along its length, so the far end turns brown or pink at full white. Feed +5V and GND again every 50–100 LEDs — never the data line.
- Connect GND before +5V, and never hot-plug the data wire while the strip is powered.
The FastLED library generates the timing-critical signal with the ESP32's RMT peripheral. Set the byte order to match your chip and cap the brightness before the first show() .
// WS2812B strip with FastLED — Library Manager → "FastLED"
#include <FastLED.h>
const int DATA_PIN = 18;
const int NUM_LEDS = 30;
CRGB leds[NUM_LEDS];
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS); // GRB is the WS2812B byte order
FastLED.setBrightness(64); // cap: 64/255 keeps current low
FastLED.setMaxPowerInVoltsAndMilliamps(5, 2000); // never draw more than 2 A
}
void loop() {
fill_solid(leds, NUM_LEDS, CRGB(255, 160, 60)); // warm panel backlight
FastLED.show();
delay(1000);
}A panel rarely wants a whole strip to show one thing. The same strip usually carries the backlight and a few annunciators. An LED Range is a contiguous run of LEDs on the strip, lit as one: it starts at an offset counted from the DIN end (the first LED is 0) and covers a count of LEDs.
| Range setting | Meaning |
|---|---|
| Offset | First LED of the run, counted from the data-in end (0–299) |
| Count | How many LEDs the run covers |
| Base colour | The colour at full level ( #rrggbb ) — the tint of the lens or backlight it sits behind; white if not set |
A range is driven with one of two kinds of value:
| Value | Effect | Example |
|---|---|---|
| Level 0–255 | Brightness of the base colour; 0 is dark | Panel backlight following the panel-brightness knob |
Colour #rrggbb | Replaces the base colour | Gear light: off, red in transit, green when down |
The sketch below implements the same idea by hand:
// Three independent "ranges" on one strip
#include <FastLED.h>
const int DATA_PIN = 18;
const int NUM_LEDS = 12;
CRGB leds[NUM_LEDS];
// A run of LEDs lit as one: first LED (from DIN) and how many
struct Range { int offset; int count; CRGB base; };
Range backlight = { 0, 6, CRGB(255, 160, 60) };
Range gearLight = { 7, 1, CRGB::Green };
void setLevel(const Range& r, uint8_t level) { // 0–255 of the range's base colour
CRGB c = r.base;
c.nscale8_video(level);
for (int i = 0; i < r.count; i++) leds[r.offset + i] = c;
}
void setColour(const Range& r, CRGB colour) { // a colour that replaces the base
for (int i = 0; i < r.count; i++) leds[r.offset + i] = colour;
}
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
}
void loop() {
setLevel(backlight, 128); // panel brightness knob at 50 %
setColour(gearLight, CRGB::Red); // gear in transit
FastLED.show();
delay(500);
setColour(gearLight, CRGB::Green); // down and locked
FastLED.show();
delay(500);
} On a CockpitConnect controller you don't write this. Add an LED Strip to the controller in the Flow Builder with its data pin, length (up to 300 LEDs), byte order (GRB by default) and a brightness cap that protects your power supply. Then add an LED Range node per light, with its offset, count and base colour, and bind it to a dataref: a map to 0–255 dims a backlight, a lookup to colours drives a multi-state light. The controller renders the bytes and applies the cap last.
| Application | Range setup | Notes |
|---|---|---|
| Panel backlighting | One long range, warm-white base colour | Level follows the panel-brightness dataref |
| Landing gear lights | One LED per gear leg | Colour value: off, red in transit, green down and locked |
| Annunciator panel | One short range per legend | Amber or red base colour; level 0 or 255 |
| Glareshield flood light | Strip under the glareshield lip | Level follows the flood-light knob |
| Master caution / warning | One or two LEDs behind a lens | Flash with a blink pattern in the base colour |