Animate NeoPixels in Under 20 Minutes: CMozGlow on the ESP32-S3 Mini
If you’ve ever stared at a WS2812B datasheet and quietly closed the tab, this tutorial is for you. I built CMozGlow because I was tired of rewriting the same “just make the LEDs do a nice thing” code for every wearable project.
CMozGlow is a free Arduino and PlatformIO library made for the CMoz ESP32-S3 Mini. It works with WS2812B-family NeoPixels, including rings, strips and rectangles. Here’s the 20-minute version of getting it running.
What you need
- An ESP32-S3-based board. I’m using the CMoz ESP32-S3 Mini, but any ESP32 with a free GPIO pin works.
- A WS2812B addressable LED module. I’m demonstrating with the 16-LED RGB Circuit Ring. The 8-Bit WS2812 Rectangle or 8-Bit WS2812 Ring Module works the same way, with fewer pixels.
- Jumper wire for data, power and ground.
- PlatformIO.
Step 1: Wire the NeoPixels
Make three connections:
- 5V to the ring’s power pad
- GND to ground
- A free GPIO pin, such as GPIO 2, to the ring’s DIN pad
Power tip: If you’re running more than 8–10 pixels at full brightness from USB alone, watch the current draw. A 16-LED ring at full white can approach 1 A, which is more than USB will comfortably supply. Lower the brightness in software instead of fighting the power supply.
Step 2: Install CMozGlow
Add CMozGlow to your platformio.ini as a library dependency. It sits on top of the standard NeoPixel driver, so the timing-sensitive bit-banging is already handled for you.
Step 3: Add the essential code
#include <CMozGlow.h>
CMozGlow ring(16, 2); // 16 pixels, GPIO 2
void setup() {
ring.begin();
ring.setBrightness(60);
}
void loop() {
ring.rainbowCycle(20);
}That’s a complete rainbow chase. Swap rainbowCycle() for breathe(color, speed) to create a slow pulse, or chase(color, tailLength) for a comet-style trail.
Step 4: Make the animation meaningful
The animation is the easy part. A project starts to feel intentional when you connect that animation to an event, such as a sensor threshold, button press or successful Wi-Fi connection.
In my builds, I often use breathe() for an idle or waiting state and chase() when the project is actively doing something. That gives the wearer an at-a-glance status update without needing a screen.
Scaling up
The same approach works whether you’re driving 8 pixels or 60. CMozGlow loops over the number of pixels supplied in the constructor. If you switch modules or chain more pixels, update that number and your wiring as needed.
Troubleshooting NeoPixels
If the pixels show the wrong colours or stay dark, check these two common causes first:
- RGB versus GRB colour order: WS2812B products can use different channel orders.
- DIN versus DOUT: Make sure your data wire connects to the module’s input pad, not its output.
Check the module’s silkscreen labels before assuming the code is wrong.