Logo Mingy

RGB Strip Controller

Controlling a WS2812 strip with ESP32

May 3, 2026 - 3 minute read
feature image Caption

Introduction

Almost all RGB strips you can find use the WS2812 protocol.

Simply put, it involves sending a list of colors to the first LED on the DIN pin, which will then take out the first color and pass on the rest of the data to the next pixel.

This RGB pixel is also known as NeoPixel by Adafruit. Other models also follow this protocol. See SK6812 which offers RGBW lighting (true white light), and WS2813 which has a backup DIN connected 2 pixels back, in case the previous one dies.

What this does

I made firmware for an esp32-c3 to control the RGB strip.

It can receive data from a computer over a serial connection (USB-Serial/JTAG) with a custom protocol to animate the RGB strip however you like. In my testing, it can run 88 WS2812b’s at 60Hz.

Screen capture to pass data to the microcontroller is a work in progress. In the repo there is a folder /wayland-capture that uses Wayland’s client API to grab the screen’s content, run a simple algorithm and send it to the microcontroller. However this is only an implementation for Wayland, not for Windows/Mac/X11.

One thing I’ve seen existing devices do is to put the HDMI connection through it to read the screen’s contents and animate the strip. However this would require somewhat expensive hardware which I’d rather avoid.

While there is no unified way between OSes to grab screen content, a likely solution can be to use screen recording software (e.g. OBS) to create a virtual camera. In my testing, there is a latency of up to 100ms when reading from the virtual camera with a python script, far more than the ~4ms latency when reading from shared memory with the Wayland screen capture method.

The python mss library can also be looked into as a fast-ish cross platform solution.

How to use

First you need the physical components:

  • The RGB strip & power supply
  • An ESP32
  • Soldering implements

Just connect the power supply and the appropriate output pin of the microcontroller to the power and data-in of the RGB strip.

P.S. Looking to design a power saving mode with a switch to cut off the power supply to the strip. Would appreciate any advice.

Stuff I learnt

Lowk forgot many little things I researched while making the firmware. But looking back at the code:

Setting up unique data structures due to the lack of OS features (heap, mutexes) was pretty cool. I used a heapless queue, derived from a ring buffer.

It’s also nice to see the importance of thread safety here. While multithreading isn’t implemented, the Interrupt Service Routine can still “preempt” the main code execution to read from the USB-Serial/JTAG.

Within the rust paradigm, being able to split the queue and the serial interface into the producer and consumer is also a nice bonus that improves robustness.