Skip to content

Neopixel Strip

A length of addressable strip, an ESP8266, and a power supply. Every pixel is independently controllable, so the strip can be one colour, a gradient, a slow sunrise on a bedroom shelf, or a red chase down the hallway when a door is left open. In Home Assistant it is a single light entity with brightness, a colour wheel and a dropdown of ten effects.

This is cove lighting, under-cabinet lighting and behind-the-TV lighting that answers to an automation instead of a remote you have lost. The controller costs about as much as a smart bulb and drives as much strip as your supply can feed.

It has been built twice. The ESPHome config is the one to build today — it is a file rather than a sketch, it updates over WiFi, and it needs no MQTT broker. The original Arduino sketch is still in the folder at the bottom, because a lot of people are still running it and it does animations ESPHome has no equivalent for.

Pick your voltage first

Everything else follows from this, so decide before you buy anything.

5 V WS2812B gives you control of every single LED and runs off one rail, so the board and the strip share a supply. It is the right choice up to about five metres. Past that the far end goes dim and yellow, because the voltage has dropped on its way down the copper, and you end up injecting power every couple of metres to fix it.

12 V WS2811 groups the LEDs in threes — one controllable pixel per three LEDs — and loses far less voltage over a long run. You give up resolution and gain the ability to light a whole room’s perimeter without a power tap every two metres. This is what the legacy sketch’s 186-pixel run was.

Parts

Some links below are Amazon affiliate links. As an Amazon Associate I earn from qualifying purchases — at no extra cost to you. It helps keep these guides free. Outside the US, they should send you to your own Amazon store; if one doesn't, searching the part number there will find it.

Wiring

Every pin below comes from bruh-neopixel-strip.yaml. The GPIO column is there because ESPHome’s logs talk in GPIO numbers even when the silkscreen says D.

SignalNodeMCU pinGPIOGoes to
Strip data inRX3The strip’s DIN, through the 470 Ω resistor
Board 5 V inVINThe 5 V rail, or the buck’s output on a 12 V build
Board groundGNDSupply ground — shared with the strip, always
Strip +VStraight off the supply, never off the board
Strip groundSupply ground
Status LED2The ESP module’s own blue LED, claimed by status_led:

The strip does not get its power from the NodeMCU. The board’s regulator exists to run the board; a metre of pixels at full white pulls more than it can give, and the board browns out and reboots in a loop.

Ground is the connection people skip and it is the one that has to be there. The data signal is measured against ground, so a board and a strip on separate supplies with no ground between them means the strip is reading noise.

The data pin is not a free choice in this config. method: ESP8266_DMA uses the ESP8266’s I2S peripheral, and that peripheral can only drive GPIO3 — which is why the config says GPIO3, and why moving the wire to a nicer-looking pin makes the strip go dark. Changing the pin means changing the method too: ESP8266_UART1 gives you GPIO2 (already taken by status_led: here), ESP8266_UART0 gives you GPIO1, and ESP8266_BIT_BANG takes any pin at the cost of timing a busy WiFi stack can disturb.

GPIO3 is also the board’s serial RX. Nothing breaks, but the strip flashes nonsense while you flash the board over USB, because the USB-serial chip is driving that same wire. Pull the data lead if it bothers you.

The legacy sketch uses a different pin. #define DATA_PIN 5 is GPIO5, which is D1 on this board. Flashing the Arduino sketch onto hardware you wired for the ESPHome config means moving the data lead from RX to D1.

Assembly

  1. Cut the strip at a copper pad, never between them. Every strip has its cut lines marked; on a 12 V WS2811 strip they come every three LEDs, so your length is quantised whether you like it or not. Measure the run, cut, then count the pixels — that number goes in the config.

  2. Check the arrows. Addressable strip is directional. Data enters at the end the arrows point away from, and a strip wired backwards is completely dead with no other symptom to go on. Fit the connector to the input end.

  3. Put JST pigtails on both ends of every segment. Ten extra minutes now, and an hour saved every time you move something later.

  4. Seat the strip in the channel and snap the lens on. Clean the aluminium first — the adhesive on the back of the strip is mediocre and lets go in a warm room about a month in. A dab of clear silicone at each end helps.

  5. Build the controller in the box. Barrel jack through a gland, buck module set to 5 V before anything is connected to its output, board on perfboard, three-wire lead out the other gland. Meter the buck’s output with nothing attached — these arrive set to wherever the last person left the trimmer.

  6. Feed the strip at the strip, not through the box. The supply goes straight to the strip’s power pads. Only the data line and a ground reference need to reach the controller.

  7. Flash it, watch the log, then close the box. Not the other way round.

Configuration

Copy bruh-neopixel-strip.yaml into your ESPHome directory and put a secrets.yaml beside it. This config uses exactly three keys — wifi_ssid, wifi_password and ota_password. There is a secrets.yaml.example in the repo with every key these configs use.

Then change the name, and the two lines that describe your particular strip:

substitutions:
device_name: "Kitchen Strip"
device_id: "kitchen-strip"
light:
- platform: neopixelbus
type: GRB
num_leds: 24

num_leds is the count of controllable pixels, not LEDs. On a 12 V WS2811 strip those differ by a factor of three: a 5 m reel of 60 LEDs/m is 100 pixels, not 300. Set it too low and the tail of the strip stays dark; set it too high and the extra pixels are simply thrown away.

type is the order the three colour dies are wired in inside the package, and it varies by strip. GRB is what the config ships with and what most WS2812B use; the legacy sketch is set to BRG for the strip it was written against. If you send red and get blue, this is the line, and it is the only line.

The effects are already in the file: flicker, strobe, random, rainbow, scan, colour wipe, twinkle, random twinkle, fireworks and addressable flicker. They arrive as the light entity’s effect dropdown. Delete the ones you will never use — a dropdown of ten is a dropdown nobody reads.

Flash over USB the first time; every flash after that is over WiFi. The device turns up in Home Assistant on its own, with a WiFi signal sensor, a status binary sensor and a restart switch alongside the light.

Sizing the power supply

Take the strip’s rated watts per metre — printed on the reel and in the listing — and multiply by the metres you are running. That is the load at full white on every pixel, which is the only figure worth designing for, because it is what your first light.turn_on with no colour set will produce. Buy a supply with headroom above that number, not one that matches it.

If the total is more than one supply can give, the fix is not thicker wire. Feed the run from both ends, or split it into segments and feed each one — which is why the 12 V brick above ships with an eight-way splitter. Every ground still ties back together.

Troubleshooting

The first pixel is a different colour to the rest, or flickers. This is the 3.3 V data line driving a 5 V input, and it is the most common problem with this build by a distance. Put the 470 Ω resistor in series at the board end, keep the lead from board to first pixel under about 20 cm, and add the 1000 µF capacitor across the strip’s power at the first pixel. If it still misbehaves, buffer the data through a 74AHCT125. Don’t reach for a bidirectional auto-direction level shifter such as a TXS0108E — they sense direction with deliberately weak drivers and are the wrong shape for a one-way 800 kHz line.

Nothing lights at all. Nine times in ten the data is on the wrong end of the strip. Check the arrows. Then check that board ground and supply ground are actually joined.

Red comes out blue. Change type: in the config. See above.

The far end is dim and orange. Voltage drop. Inject power at the far end from the same supply, or move to a 12 V strip.

The board reboots every time the light turns on. The strip is being fed from the NodeMCU. Take it off and give the strip its own feed.

The strip strobes randomly while you flash over USB. Expected — the data pin is the serial RX line. It settles as soon as the board reboots.

The legacy Arduino sketch

bruh_mqtt_json_ota_rgb_leds_with_effects_github.ino is the original: an MQTT JSON light with a pile of hand-written FastLED animations that ESPHome has no equivalent for — candy cane, fire, lightning, police lights, ripple, juggle, cyclon rainbow, noise, confetti. If those are why you are here, this is the file you want.

It publishes state to bruh/porch and listens on bruh/porch/set, using Home Assistant’s JSON light schema: state, color, brightness, effect, transition and flash. Change SENSORNAME and both topic strings at the top, along with the WiFi, MQTT and OTA credentials. Data is on DATA_PIN 5 (D1), NUM_LEDS is 186 as shipped, and COLOR_ORDER is BRG.

It needs ArduinoJson 5.x. The sketch uses StaticJsonBuffer and parseObject, which version 6 removed — install 5.13.5 from the Library Manager and it compiles, install the current version and it will not. The other two libraries are FastLED and PubSubClient.

OTA is on port 8266 with its own password, so after the first USB flash you can push updates to it from the Arduino IDE over WiFi, the same as the ESPHome build.

Files & downloads

ESPHome configuration

Copy this into your ESPHome directory and adjust the substitutions at the top. Secrets are referenced by name — see secrets.yaml.example .

bruh-neopixel-strip.yaml 68 lines
bruh-neopixel-strip.yaml
# BRUH Neopixel Strip
# ────────────────────────────────────────────────────────────
# Simple addressable RGB LED strip controller with
# multiple light effects and WiFi integration.
#
# Hardware:
# - Board: ESP8266
# - Output: WS2812B/NeoPixel strip (24 LEDs)
# ────────────────────────────────────────────────────────────
substitutions:
device_name: "BRUH Light"
device_id: "bruh-light"
esphome:
name: ${device_id}
esp8266:
board: nodemcuv2
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ota:
password: !secret ota_password
platform: esphome
api:
status_led:
pin:
number: GPIO2
logger:
binary_sensor:
- platform: status
name: "${device_name} Status"
light:
- platform: neopixelbus
type: GRB
pin: GPIO3
num_leds: 24
name: "${device_name}"
variant: 800KBPS
method: ESP8266_DMA
effects:
- flicker:
- strobe:
- random:
- addressable_rainbow:
- addressable_scan:
- addressable_color_wipe:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_fireworks:
- addressable_flicker:
sensor:
- platform: wifi_signal
name: "${device_name} WiFi Signal"
update_interval: 300s
switch:
- platform: restart
name: "${device_name} Restart"

View on GitHub · Download raw

Every file for this project on GitHub → Also in the folder: arduino-legacy/ — legacy Arduino sketches, 2 files.