Creative Hardware

Addressable LED Strips: Getting Started

One data wire, three hundred individually controllable pixels, and about six lines of code — plus the power arithmetic nobody warns you about until your strip browns out.

The moment addressable LEDs click is when you realise the strip is not a light. It’s a one-dimensional display, and you write to it the way you’d write to a row of pixels.

1. Addressable versus analog

An analog RGB strip has four wires and one colour at a time. You set the whole strip red. Cut it in half and both halves are red. Fine for uplighting, useless for anything expressive.

An addressable strip — WS2812B is the common one, sold as “NeoPixel” by Adafruit — puts a tiny controller chip inside each LED package. The strip has three wires: 5V, ground, and data. Each chip reads the first 24 bits that arrive, keeps them as its own colour, and passes everything after that down the line to the next chip.

Consequences worth internalising:

  • You address every pixel independently with one microcontroller pin.
  • The strip is directional. There are arrows printed on it. Data flows one way; wire it backwards and nothing lights.
  • Because each chip forwards the remainder, one dead pixel kills everything downstream of it.

2. What you need

  • A WS2812B strip. Start with a 1-metre, 60-LED strip; they cut between any two pads.
  • An Arduino Uno/Nano or an ESP32. Either works. ESP32 is faster and has WiFi, which matters later.
  • A 5V power supply sized per section 3 — not the USB port, once you pass about 10 pixels.
  • A 330–470Ω resistor for the data line and a 1000µF capacitor across the power rails.
  • Wire, and a soldering iron, unless your strip came with a connector.

3. Power, first, before you buy anything

This is the section people skip and then post about on forums.

A WS2812B pixel draws about 60mA at full white — 20mA per colour channel. So:

60 LEDs × 60mA = 3.6A at 5V = 18 watts

A 5-metre 300-LED strip at full white wants 18 amps. That is a serious power supply, not a phone charger.

In practice you never run full white at full brightness. Two habits make it manageable:

  1. Cap global brightness in software. FastLED.setBrightness(64) — a quarter of maximum — is still bright indoors and cuts your current to roughly a quarter.
  2. Size the supply for what you’ll actually draw, then leave 30% headroom, and use FastLED.setMaxPowerInVoltsAndMilliamps() to make the library enforce it.

Inject power at both ends of any run longer than about a metre. The copper in the strip has resistance; feed 5V only at one end and the far end sags, which shows up as a colour shift — the far pixels go orange or pink when you asked for white. That’s not a broken strip, that’s volts disappearing into the trace.

4. Wiring

5V supply (+)  ──┬──> strip 5V
                 └──> 1000µF capacitor (+)

5V supply (−)  ──┬──> strip GND
                 ├──> capacitor (−)
                 └──> Arduino GND     ← this one
Arduino pin 6  ──[330Ω]──> strip DIN

Three things in that diagram matter more than the rest:

  • Common ground. The Arduino’s ground and the power supply’s ground must be connected. If they aren’t, the data signal has no reference and the strip does something between nothing and seizures. This is the single most common failure.
  • The resistor on the data line damps reflections on the first pixel’s input. Cheap insurance.
  • The capacitor across the rails absorbs the inrush when a lot of pixels switch on at once, which otherwise dips the rail and glitches the data.

Power the strip from the supply, not through the Arduino. The board’s regulator will not survive it.

5. First code

Install FastLED through the Arduino IDE’s Library Manager (Tools → Manage Libraries → search FastLED).

#include <FastLED.h>

#define NUM_LEDS   60
#define DATA_PIN   6

CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(64);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 2000);
}

void loop() {
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Black;
  }
  leds[beatsin16(30, 0, NUM_LEDS - 1)] = CRGB::Cyan;
  FastLED.show();
}

That’s a single cyan pixel swinging back and forth on a sine. The shape of every FastLED sketch is the same: write into the leds array, then call FastLED.show(). Nothing reaches the strip until show().

Note the GRB in addLeds. WS2812B expects green first. If your reds come out green, that parameter is why.

6. Two ideas that unlock everything else

HSV, not RGB. CHSV(hue, sat, val) lets you sweep hue as a single number, and hue is the thing you actually want to animate. Rainbows, colour cycles, and palettes all fall out of it:

fill_rainbow(leds, NUM_LEDS, millis() / 20, 7);

Never use delay(). It blocks. Drive animation from millis() or from FastLED’s beat functions (beatsin8, beatsin16), which take a BPM and give you a smoothly varying value. That way multiple animations can run at different rates in the same loop, and the strip stays responsive to sensors or MIDI.

7. When it doesn’t work

  • Nothing lights at all → check the data arrow direction, then check common ground.
  • First pixel works, rest don’t → data is arriving but the chain is broken; suspect the solder joint or a dead pixel right after the first.
  • Far end goes orange/pink → voltage drop. Inject power at the far end too.
  • Random flicker → usually ground, sometimes a data wire run too long. Keep the microcontroller close to the strip’s input, or use a level shifter for runs over about 30cm.
  • Colours are wrong but consistent → wrong colour order in addLeds. Try RGB or BRG.

8. Where to go next

Once a strip works, the interesting move is to stop thinking of it as a line. Zigzag a strip into a grid and you have a matrix (FastLED’s XY() mapping handles the serpentine indexing). Wrap it around an object and you have a volume you address with one index. Feed it audio via FFT and you have an audio-reactive piece. The code barely changes; only the mapping from your idea to array indices does.