<< Click to Display Table of Contents >>

Navigation:  Bits and PCs Blog - 2026.05.14 >

Serial RGB LEDs, WS2812 - 2026.05.14

Previous pageReturn to chapter overviewNext page

A while ago, I started a project with a new microcontroller board and wanted to use the on-board LED as an indicator. Naturally, I used the familiar digitalWrite(LED_BUILTIN, 1), loaded the program into the controller, and suddenly...   Nothing happened!

Looking at the board I realized it was one of those fancy 3-colour LEDs, a WS2812. These are known as 'RGB' LEDs because they are red-green-blue, although some of them are green-red-blue just to confuse you.

The microcontroller was an ESP32, so it didn't take long to find the rgbLedWrite() method that's part of the ESP32 HAL (Hardware Application Layer) in file '..\cores\esp32\esp32-hal-rgb-led.c'. This uses the ESP32's on-chip 'Remote Control Transmitter' (RMT) to generate the signals for the LED with method rmtWrite(...). The problem is it only works for one LED. You could use rmtWriteRepeated() to write the same colour to multiple LEDs - which is OK for single-colour 'NEOPIXEL' style displays on 3D Printers. But what if you want to use the RMT for something else? Or you want a fancy colour animation?

If you need a multicoloured animation you can use an array of RMT control data. Each bit sent to the LEDs needs a 32-bit rmt_data_t value, and each LED needs 24 bits. So each LED needs 24 * 4 = 96 bytes. This cannot be reduced because the RMT data must be prepared in memory before it is sent.

There's a nice non-blocking method called rmtWriteAsync() which does not wait until it's all sent, so you can poll it - this is perfect. I've provided some code to do this below, which runs only on the ESP32. ESP32s have lots of RAM, so for ESP32 applications this is the best solution if you don't need the RMT for something else.

For other MCUs, or to free up the RMT and/or use less RAM, you can use the bit-bang version below which controls the output using nanosecond delays tuned to your CPU's speed. This uses just 4 bytes per LED instead of 96. It needs a fast processor, 64MHz or faster, because slower processors can't do the nanosecond delays. Versions for two MCU types are provided below (STM32, ESP32).

These LEDs are very bright. Each RGB colour has a one-byte brightness level of 0..255 (0x00..0xFF). At 0xFF it's too bright to look at, 0x0F is better if it's on the desk next to you. RGB values are 24-bits, usually stored as a 32-bit unsigned integer, 0x00rrggbb (0x00000000 .. 0x00FFFFFF).

If anyone is still awake, please read on...

57 Varieties

WS2812 LEDs can be bought as single SMD LEDs, on boards with a huge variety of shapes and sizes, or as flexible strips up to 5m long (that's 300 LEDs, so you'll need a very big 5V power supply). Aliexpress.com is a good place to go for these, if you can wait a couple of weeks for them to arrive.

 

57-varieties

All the arrays have the same 4-pin connections, 5V GND DI DO, the same connections are on each LED. 5V and GND are wired in parallel, DI and DO are wired in series. Some LED strips may be powered by 12V - the LEDs will be grouped and have SMD resistors to distribute the voltage.

Signal Timing

These LEDs need an 800KHz digital signal which has timed pulses to indicate '0' or '1' bits. '1' is high for 700ns (nanoseconds) and low for 350ns. '0' high for 350ns and low for 700ns. These timings are in nanoseconds, so they are very short. The LEDs have a data in pin (DI) and a data out pin (DO), so they can be chained together. You can connect the first DI pin directly to an output of your 3.3V or 5V microcontroller - see note about driving it with 3.3V below.

Below is the timing for a '1' bit (T1) and a '0' bit (T0). T1H and T0L (T1 High and T0 Low) are 700..800ns. T1L and T0H (T1 Low and T0 High) are 350..400nS. The specified timings vary a bit between different versions of the chip, but 700/350ns works well, and so does 800/400ns. The RMT example uses 800/400ns. Some chips will also run at 400KHz, so the delays are doubled and maybe you can use them with slower CPUs. (That's your homework.)

The DI and DO pins of all LEDs are chained together, so the serial data is passed down the chain to each LED until there is a break in the data of at least 50us microseconds. After this break, the LEDs assume the data is ready and will display the colour values they have received. Then they start waiting for new data.

ws2812-timing

5V Power

The LEDs need 5V DC at up to 60mA for each LED (20mA per colour), depending on the brightness. That's a worst case of 20A for a 5m LED array! So you will usually need a separate 5V power supply for the LEDs. If you have only one or two LEDs then you can use the MCU's 5V supply, which may come directly from the USB or may be passed though a regulator with current limitations, so check the data sheet. The DI data pin only takes a few microamps, so you don't need to worry about that.

NEOPIXEL LEDs for 3D printers often have an additional connector on the main board for an external 5V power supply.

5V DC at 20 Amps... The best ones have a fan. Power Factor Correction (PFC) is not needed because it's not an inductive load, so cheap PSUs can be used.

5v-20a-psu

Q. Can you drive WS2812 5V LEDs directly from a 3.3V MCU output?

There's a lot of talk on Internut about this. The WS2812 data sheet says the minimum 'high' level is 0.7 x VDD, which is 3.5V. This implies that you cannot drive it reliably directly from a 3.3V MCU.

The max. current into the LED's DIN pin is +-1 microamp, which is tiny. This means it will not damage a 3.3V GPIO, even if the MCU does not have 5V-tolerant GPIOs.
BUT DO NOT CONNECT IT to the 5V Data Out DO pin!

Will a 3.3V output be enough to drive the LED's DIN input which needs >= 3.5V?

The answer to that is YES, it is OK! I have never had a problem. The data sheet shows an absolute worst-case minimum switching voltage, which never occurs in reality (unless it's duff chip). I'm pretty sure they would not develop a product that could not be driven by a 3.3V MCU.

Tip! ESD Protection

Beware of Electrostatic Discharges. If mounting LEDs on a front panel or using NEOPIXEL LEDs with a 3D printer etc. ensure the MCU output has good ESD protection! If not, think about adding a suitable TVS diode to GND.

While working with these LEDs, I damaged an unprotected MCU output because I was wearing a fleece. A fleece is like a wearable Van der Graaf Generator or Wimshurst machine. For the same reason, never wear silk pyjamas (or a silk negligee) while working with delicate electronic equipment.

LED Animations

These LEDs can produce very bright and very spectacular displays. Any self-respecting alien would be proud to have these LEDs all over his saucer. And from the UAPs that I have observed, many of them already do.

This is the LED ring that's used for the 'SerialRGBLedRMT-Example.ino' sketch below.

Play

Here is a genuine UAP that I filmed in my living room the other day...

Play

It's not a saucer, it's an upside-down candle holder.

 

The Code

https://github.com/mumanchu/SerialRGBLed

This code is for WS2811 driver chips and WS2812 RGB LEDs. It has an advantage over the other WS28xx libraries in that it is very small (100 or 200 lines, including the comments), making it easy to understand and modify. In comparison, the official Adafruit library is over 4000 lines of code, and probably does a lot of stuff that's rarely needed.

It has been tested with several 800KHz LED strips and matrices, and also on these 3D printer display boards with 3 x RGB LEDs driven by WS2811 chips:

BIGTREETECH MINI 12864 V2.0

MAKERBASE MKS MINI 12864 V3

 

Tip: For a single RGB LED on an ESP32, you do not need any of this code. Use the existing rgbLedWrite() method which uses the RMT:

 

void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);

 

ESP32 Remote Control Transmitter (RMT) Version

This version allows the ESP32's on-chip RMT to be used to control multiple LEDs. ESP32s have lots of RAM, so 96 bytes per LED should not be a problem.

It has a non-blocking updateLedsAsync() method which returns immediately, leaving the RMT to do the data transfer in the background. In this case, poll it occasionally with ledsBusy() until it returns false.

  SerialRGBLedRMT.h   [Click to show/hide the code]

Here's an example Sketch that uses SerialRGBLedRMT.h.

  SerialRGBLedRMT-Example.ino   [Click to show/hide the code]

 

The bit-bang versions work only on fast MCUs, 64MHz or faster, because the code is (mostly) in C++ it is not fast enough on old and slow MCUs. They use fast GPIO code which only works only on that specific processor.

Each LED or chip model has slightly different timing, but the chosen timing (700ns/350ns) should work for most 800KHz devices. For 400KHz devices, just modify the code to double the delays.

ESP32 Bit-bang Version

  SerialRGBLedESP32.h   [Click to show/hide the code]

STM32 Bit-bang Version

  SerialRGBLedSTM32.h   [Click to show/hide the code]

 

Data Sheets

These LEDs and driver chips are manufactured by Worldsemi. Their website always seems to download the data sheet onto your computer instead of opening it in the browser.

worldsemi-logo

So here are links to the versions on the Adafruit website, because these will open in a new browser page.

WS2812 LED
https://cdn-shop.adafruit.com/datasheets/WS2812.pdf

WS2811 Driver Chip
https://cdn-shop.adafruit.com/datasheets/WS2811.pdf

 

Joke of the Week

What makes you get into bed in the morning?