Documentation

WS2812 16-LED RGB Ring | ShillehTek Product Manual
Documentation / WS2812 16-LED RGB Ring | ShillehTek Product Manual

WS2812 16-LED RGB Ring | ShillehTek Product Manual

Overview

This compact ring puts 16 WS2812 addressable RGB LEDs in a circle you can drive from a single GPIO pin. Each LED carries its own driver chip, so all 16 take independent 24-bit colors: a rainbow halo, a spinning loader, a countdown timer, a volume dial, or one pixel quietly breathing as a status light. Once colors are latched, the ring holds them with zero attention from your code.

The 16-pixel size is the go-to for accents and interfaces — button halos, knob surrounds, eyes for robots and props, small clock faces, wearable badges — anywhere the 24-LED ring would be too large. Pads on the back carry 5V, GND, data-in (DI), and data-out (DO); DO chains into another ring or strip's DI, letting you build compound layouts (nested rings, ring-plus-strip gauges) that your code addresses as one long pixel run.

It follows the standard WS2812 recipe: 5V power sized for ~60 mA per LED at full white (just under 1 A for the ring, though sane animations draw far less), a 330-470 ohm resistor in the data line, a chunky capacitor across power, and any of the universal libraries — Adafruit NeoPixel or FastLED on Arduino/ESP32, MicroPython's neopixel on ESP32 and Pico, rpi_ws281x on the Raspberry Pi.

At a Glance

LEDs
16x WS2812 RGB pixels
Form Factor
Compact ring
Control
1 data pin (800 kHz)
Supply Voltage
5V DC
Max Current
~0.96 A (all white, full)
Chainable
Yes — DO feeds next DI

Specifications

Parameter Value
LED Type WS2812 (WS2812B-class) integrated RGB + driver, 5050 package
LED Count 16, evenly spaced around the ring
Supply Voltage 5V DC (4.5 - 5.5V)
Current Draw ~1 mA/LED idle; up to ~60 mA/LED at full white (~0.96 A total)
Data Protocol Single-wire 800 kHz, strict timing (library-handled)
Color Depth 24-bit (8 bits/channel, GRB order)
Pads DI (data in), DO (data out), 5V/VCC, GND
Recommended Extras 330-470 ohm on DI; 470-1000 uF across 5V/GND
Logic Level 5V nominal; 3.3V data usually works (shift if unstable)
Angular Resolution 22.5° per LED — ideal for dials and loaders

Wiring Guide

Three wires: 5V, GND, and GPIO to DI through a 330-470 ohm resistor (DO stays free unless chaining). Add the capacitor across power, and always common the grounds between LED supply and controller.

Arduino Wiring

Ring Pad Arduino Pin Details
DI D6 via 330 ohm Data input
5V 5V 16 LEDs are USB-friendly at capped brightness
GND GND
Tip: Make sure the wire goes to DI, not DO — data flows one way around the ring, and a chain wired backwards stays completely dark.

ESP32 Wiring

Ring Pad ESP32 Pin Details
DI GPIO 13 via 330 ohm Any output GPIO
5V VIN (USB 5V) Power at 5V, not 3V3
GND GND
Note: 3.3V data into 5V pixels is marginal on paper but reliable on most rings. Flicker cure: 74AHCT125 level shifter, or feed the ring's 5V through a 1N4007 diode to lower its logic threshold.

Raspberry Pi Wiring

Ring Pad Pi Pin Details
DI Pin 12 (GPIO 18) via 330 ohm PWM0 — rpi_ws281x requirement
5V Pin 2 (5V)
GND Pin 6 (GND)
Tip: Run scripts with sudo, and disable onboard audio (dtparam=audio=off) — the library shares hardware with it.

Raspberry Pi Pico Wiring

Ring Pad Pico Pin Details
DI GP0 (pin 1) via 330 ohm PIO handles the timing
5V VBUS (pin 40) USB 5V
GND GND (pin 38)

Code Examples

Each example demos patterns that suit a 16-pixel ring: a loading spinner, a battery/volume-style gauge, and a breathing status glow — all brightness-capped for USB power.

Arduino

ring16_arduino.ino
// WS2812 16-LED Ring - Arduino Example
// DI->D6 via 330 ohm, 5V->5V, GND->GND
// Library: "Adafruit NeoPixel"

#include <Adafruit_NeoPixel.h>

#define PIN      6
#define NUM      16

Adafruit_NeoPixel ring(NUM, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  ring.begin();
  ring.setBrightness(60);
  ring.show();
}

void spinner(uint32_t color, int loops) {
  for (int t = 0; t < loops * NUM; t++) {
    ring.clear();
    ring.setPixelColor(t % NUM, color);
    ring.setPixelColor((t + 1) % NUM, color);        // 2-px head
    ring.setPixelColor((t + NUM - 1) % NUM,
                       ring.Color(10, 10, 30));      // faint tail
    ring.show();
    delay(60);
  }
}

void gauge(int percent) {                            // 0-100
  int lit = map(percent, 0, 100, 0, NUM);
  ring.clear();
  for (int i = 0; i < lit; i++) {
    // green -> yellow -> red as the gauge fills
    uint8_t r = map(i, 0, NUM - 1, 0, 255);
    uint8_t g = map(i, 0, NUM - 1, 255, 0);
    ring.setPixelColor(i, r, g, 0);
  }
  ring.show();
}

void loop() {
  spinner(ring.Color(0, 120, 255), 4);

  for (int p = 0; p <= 100; p += 5) {                // gauge sweep
    gauge(p);
    delay(80);
  }
  delay(600);
}

ESP32 (MicroPython)

ring16_esp32.py
# WS2812 16-LED Ring - ESP32 MicroPython Example
# DI->GPIO 13 via 330 ohm, 5V->VIN, GND->GND

from machine import Pin
import neopixel, time

NUM = 16
ring = neopixel.NeoPixel(Pin(13), NUM)
BRIGHT = 0.25

def scale(c):
    return tuple(int(v * BRIGHT) for v in c)

def spinner(color, loops=4):
    for t in range(loops * NUM):
        ring.fill((0, 0, 0))
        ring[t % NUM] = scale(color)
        ring[(t + 1) % NUM] = scale(color)
        ring[(t - 1) % NUM] = scale((10, 10, 30))
        ring.write()
        time.sleep_ms(60)

def gauge(percent):
    lit = percent * NUM // 100
    ring.fill((0, 0, 0))
    for i in range(lit):
        r = i * 255 // (NUM - 1)
        g = 255 - r
        ring[i] = scale((r, g, 0))
    ring.write()

def breathe(color, cycles=2):
    for _ in range(cycles):
        for lv in list(range(0, 100)) + list(range(100, 0, -1)):
            ring.fill(tuple(int(v * lv / 100 * BRIGHT) for v in color))
            ring.write()
            time.sleep_ms(10)

while True:
    spinner((0, 120, 255))
    for p in range(0, 101, 5):
        gauge(p)
        time.sleep_ms(80)
    breathe((150, 0, 255))

Raspberry Pi (Python)

ring16_rpi.py
#!/usr/bin/env python3
# WS2812 16-LED Ring - Raspberry Pi Example (rpi_ws281x)
# DI->GPIO18 (pin 12) via 330 ohm; run with sudo
# Install: sudo pip3 install rpi_ws281x

import time
from rpi_ws281x import PixelStrip, Color

NUM = 16
strip = PixelStrip(NUM, 18, brightness=60)
strip.begin()

def clear():
    for i in range(NUM):
        strip.setPixelColor(i, 0)

def spinner(color, loops=4):
    for t in range(loops * NUM):
        clear()
        strip.setPixelColor(t % NUM, color)
        strip.setPixelColor((t + 1) % NUM, color)
        strip.show()
        time.sleep(0.06)

def gauge(percent):
    lit = percent * NUM // 100
    clear()
    for i in range(lit):
        r = i * 255 // (NUM - 1)
        g = 255 - r
        strip.setPixelColor(i, Color(r, g, 0))
    strip.show()

try:
    while True:
        spinner(Color(0, 120, 255))
        for p in range(0, 101, 5):
            gauge(p)
            time.sleep(0.08)
        time.sleep(0.6)
except KeyboardInterrupt:
    clear(); strip.show()
    print("Stopped by user")

Raspberry Pi Pico (MicroPython)

ring16_pico.py
# WS2812 16-LED Ring - Pico MicroPython Example
# DI->GP0 via 330 ohm, 5V->VBUS, GND->GND
# Countdown-timer demo: ring empties over N seconds, then flashes.

from machine import Pin
import neopixel, time

NUM = 16
ring = neopixel.NeoPixel(Pin(0), NUM)
BRIGHT = 0.25

def scale(c):
    return tuple(int(v * BRIGHT) for v in c)

def countdown(seconds=16):
    per_led = seconds / NUM
    for lit in range(NUM, 0, -1):
        ring.fill((0, 0, 0))
        for i in range(lit):
            color = (0, 255, 60) if lit > NUM // 4 else (255, 30, 0)
            ring[i] = scale(color)
        ring.write()
        time.sleep(per_led)

def flash(color, times=4):
    for _ in range(times):
        ring.fill(scale(color)); ring.write(); time.sleep_ms(120)
        ring.fill((0, 0, 0));    ring.write(); time.sleep_ms(120)

while True:
    countdown(16)          # 1 LED per second
    flash((255, 0, 0))
    time.sleep(1)

Frequently Asked Questions

The ring stays dark. What's the checklist?
Data into DI (not DO) — the number-one cause; 5V actually present at the ring's pads; grounds common between supply and controller; and pin/LED-count constants matching your wiring. If the first LED lights but nothing else, the ring is fine and your code only wrote one pixel — check the loop and show()/write() call.
Do I really need the resistor and capacitor?
They're cheap insurance against the two classic WS2812 killers: the 330-470 ohm resistor damps ringing on the data line that can corrupt or even damage the first pixel, and the 470-1000 uF capacitor absorbs the inrush spike when power connects (hot-plugging LEDs without one occasionally pops the first LED). Breadboard demos often survive without them; permanent builds shouldn't skip them.
Which LED is "first," and can I rotate the pattern in software?
Pixel 0 is the LED next to the DI pad, counting onward in the direction of the chain. Since the ring is symmetric, mount it however looks best and rotate in code: index = (i + OFFSET) % 16 shifts any pattern; use (16 - i) % 16 to reverse direction. Every clock/gauge sketch ends up with exactly such an offset constant.
Random flickers when lots of LEDs are bright — why?
Almost always supply dip: bright frames pull current spikes, the 5V sags, and pixels latch garbage. The capacitor helps, short fat power wires help, and the real fix at high brightness is an external 5V supply with grounds joined. On 3.3V controllers, marginal data levels can add sparkle — the level-shifter note in the ESP32 tab covers that.
Can I dim it smoothly without losing colors?
Yes — two layers: a global brightness cap (setBrightness / the BRIGHT factor in the examples) for the overall ceiling, and per-frame scaling for animations like breathing. WS2812s have 8 bits per channel, so very low brightness quantizes visibly on slow fades; gamma correction (gamma32 in NeoPixel) keeps fades looking even to the eye.
How is this different from the 24-LED 90mm ring — can they nest?
Same protocol, different diameter and count — and yes, they nest beautifully: wire the outer ring's DO to the inner ring's DI and address 40 pixels as one chain (0-23 outer, 24-39 inner). Concentric spinners, radar sweeps, and eyes with pupils are the classic double-ring builds. Share 5V/GND and size the supply for both.
Are these the same as "NeoPixels"?
Yes — NeoPixel is simply Adafruit's brand name for WS2812-family LEDs. Any NeoPixel tutorial, wiring guide, or library applies to this ring verbatim: same one-wire protocol, same GRB order, same libraries. That's the best part of the ecosystem — fifteen years of examples all just work.

Related Tutorials