Documentation

WS2812B 5x5 RGB LED Matrix Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual
Documentation / WS2812B 5x5 RGB LED Matrix Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

WS2812B 5x5 RGB LED Matrix Module for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

Overview

This 5x5 matrix packs 25 WS2812B addressable RGB LEDs onto one compact board — a tiny full-color display driven by a single GPIO pin. Every pixel has its own controller chip, so each of the 25 LEDs takes any 24-bit color independently: icons, digits, sprites, spectrum bars, dice faces, emoji-style expressions, game-of-life animations, or simply a very bright, very colorful status panel.

Electrically it's a standard WS2812B chain folded into a grid: DIN in, DOUT out, 5V and GND for power. Your library sees it as a 25-pixel strip; a tiny x/y helper function turns "row 2, column 3" into the right strip index, and from there it behaves like a miniature screen. DOUT lets you chain matrices side by side into wider panels, or chain into rings and strips — it's all one protocol.

Like all WS2812Bs, it wants a 5V supply sized for the worst case (25 LEDs x 60 mA ≈ 1.5 A at full white — though real animations at sane brightness draw a small fraction of that), a 330-470 ohm resistor in the data line, and a fat capacitor across power. Libraries: Adafruit NeoPixel or FastLED on Arduino/ESP32, MicroPython's built-in neopixel on ESP32/Pico, rpi_ws281x on the Pi.

At a Glance

LEDs
25x WS2812B (5x5 grid)
Control
1 data pin (800 kHz)
Color
24-bit per pixel
Supply Voltage
5V DC
Max Current
~1.5 A (all white, full)
Chainable
DOUT → next DIN

Specifications

Parameter Value
LED Type WS2812B integrated RGB + driver, 5050 package
Layout 5 columns x 5 rows = 25 pixels
Supply Voltage 5V DC (4.5 - 5.5V)
Current Draw ~1 mA/LED idle; up to ~60 mA/LED full white (~1.5 A total)
Data Protocol Single-wire 800 kHz, GRB byte order
Pads DIN, DOUT, VCC/5V, GND
Pixel Order Sequential from DIN — run the index test below to map your board
Recommended Extras 330-470 ohm on DIN; 470-1000 uF across 5V/GND
Logic Level 5V nominal; 3.3V data usually fine (shift if flickery)
Refresh Hundreds of full-frame updates per second at 25 pixels

Wiring Guide

Wire DIN to your GPIO through a 330-470 ohm resistor, 5V and GND to power (capacitor across them), and leave DOUT free unless you're chaining to another matrix. Grounds must be common between the LED supply and the controller.

Arduino Wiring

Matrix Pad Arduino Pin Details
DIN D6 via 330 ohm
VCC 5V (demos) / external 5V 2A (full) Cap across supply
GND GND
Tip: Run the index-test sketch first — it lights pixels 0, 1, 2… in order so you can see exactly how your board snakes (row-by-row vs serpentine) before writing any graphics code.

ESP32 Wiring

Matrix Pad ESP32 Pin Details
DIN GPIO 13 via 330 ohm Any output GPIO
VCC VIN (USB 5V) LEDs need 5V power, not 3V3
GND GND
Note: 3.3V data into 5V-powered pixels works on most boards; if you get sparkles, use a 74AHCT125 level shifter or drop the matrix supply through a 1N4007 diode.

Raspberry Pi Wiring

Matrix Pad Pi Pin Details
DIN Pin 12 (GPIO 18) via 330 ohm PWM0, required by rpi_ws281x
VCC Pin 2 (5V) / external 5V
GND Pin 6 (GND)
Tip: rpi_ws281x needs sudo and conflicts with onboard audio — set dtparam=audio=off in config.txt and reboot.

Raspberry Pi Pico Wiring

Matrix Pad Pico Pin Details
DIN GP0 (pin 1) via 330 ohm
VCC VBUS (pin 40, 5V)
GND GND (pin 38)

Code Examples

Each example includes the xy() mapping helper plus a demo: an index test, a color-cycling heart icon, and a bouncing pixel. Set SERPENTINE to match what the index test shows on your board.

Arduino

matrix5x5_arduino.ino
// WS2812B 5x5 Matrix - Arduino Example
// DIN->D6 via 330 ohm, VCC->5V, GND->GND
// Library: "Adafruit NeoPixel"

#include <Adafruit_NeoPixel.h>

#define PIN   6
#define W     5
#define H     5
#define NUM   (W * H)
bool SERPENTINE = false;        // set true if the index test zigzags

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

int xy(int x, int y) {          // (0,0) = first pixel at DIN
  if (SERPENTINE && (y % 2 == 1)) return y * W + (W - 1 - x);
  return y * W + x;
}

// 5x5 heart bitmap
const uint8_t HEART[5] = {0b01010, 0b11111, 0b11111, 0b01110, 0b00100};

void indexTest() {
  for (int i = 0; i < NUM; i++) {
    px.clear();
    px.setPixelColor(i, 0, 150, 255);
    px.show();
    delay(150);
  }
}

void drawHeart(uint32_t color) {
  px.clear();
  for (int y = 0; y < H; y++)
    for (int x = 0; x < W; x++)
      if (HEART[y] & (1 << (W - 1 - x)))
        px.setPixelColor(xy(x, y), color);
  px.show();
}

void setup() {
  px.begin();
  px.setBrightness(50);
  indexTest();                  // watch the order once at startup
}

void loop() {
  drawHeart(px.Color(255, 0, 40));  delay(500);
  drawHeart(px.Color(255, 80, 0));  delay(500);
  drawHeart(px.Color(150, 0, 255)); delay(500);
}

ESP32 (MicroPython)

matrix5x5_esp32.py
# WS2812B 5x5 Matrix - ESP32 MicroPython Example
# DIN->GPIO 13 via 330 ohm, VCC->VIN(5V), GND->GND

from machine import Pin
import neopixel, time

W = H = 5
SERPENTINE = False
px = neopixel.NeoPixel(Pin(13), W * H)
BRIGHT = 0.2

def xy(x, y):
    if SERPENTINE and y % 2 == 1:
        return y * W + (W - 1 - x)
    return y * W + x

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

HEART = [0b01010, 0b11111, 0b11111, 0b01110, 0b00100]
SMILE = [0b01010, 0b01010, 0b00000, 0b10001, 0b01110]

def draw(bitmap, color):
    px.fill((0, 0, 0))
    for y in range(H):
        for x in range(W):
            if bitmap[y] & (1 << (W - 1 - x)):
                px[xy(x, y)] = scale(color)
    px.write()

def bounce(loops=3):
    x, y, dx, dy = 0, 0, 1, 1
    for _ in range(loops * 20):
        px.fill((0, 0, 0))
        px[xy(x, y)] = scale((0, 255, 120))
        px.write()
        x += dx; y += dy
        if x in (0, W - 1): dx = -dx
        if y in (0, H - 1): dy = -dy
        time.sleep_ms(100)

while True:
    draw(HEART, (255, 0, 40));  time.sleep(0.6)
    draw(SMILE, (255, 160, 0)); time.sleep(0.6)
    bounce()

Raspberry Pi (Python)

matrix5x5_rpi.py
#!/usr/bin/env python3
# WS2812B 5x5 Matrix - Raspberry Pi Example (rpi_ws281x)
# DIN->GPIO18 (pin 12) via 330 ohm; run with sudo
# Install: sudo pip3 install rpi_ws281x

import time
from rpi_ws281x import PixelStrip, Color

W = H = 5
SERPENTINE = False
strip = PixelStrip(W * H, 18, brightness=50)
strip.begin()

def xy(x, y):
    if SERPENTINE and y % 2 == 1:
        return y * W + (W - 1 - x)
    return y * W + x

HEART = [0b01010, 0b11111, 0b11111, 0b01110, 0b00100]

def clear():
    for i in range(W * H):
        strip.setPixelColor(i, 0)

def draw(bitmap, color):
    clear()
    for y in range(H):
        for x in range(W):
            if bitmap[y] & (1 << (W - 1 - x)):
                strip.setPixelColor(xy(x, y), color)
    strip.show()

def column_sweep(color, loops=3):
    for _ in range(loops):
        for x in range(W):
            clear()
            for y in range(H):
                strip.setPixelColor(xy(x, y), color)
            strip.show()
            time.sleep(0.12)

try:
    while True:
        draw(HEART, Color(255, 0, 40))
        time.sleep(0.8)
        column_sweep(Color(0, 120, 255))
except KeyboardInterrupt:
    clear(); strip.show()
    print("Stopped by user")

Raspberry Pi Pico (MicroPython)

matrix5x5_pico.py
# WS2812B 5x5 Matrix - Pico MicroPython Example
# DIN->GP0 via 330 ohm, VCC->VBUS(5V), GND->GND

from machine import Pin
import neopixel, time

W = H = 5
SERPENTINE = False
px = neopixel.NeoPixel(Pin(0), W * H)
BRIGHT = 0.2

def xy(x, y):
    if SERPENTINE and y % 2 == 1:
        return y * W + (W - 1 - x)
    return y * W + x

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

DIGITS = {                     # tiny 5x5 digits 0-3 (add more!)
    0: [0b01110, 0b10001, 0b10001, 0b10001, 0b01110],
    1: [0b00100, 0b01100, 0b00100, 0b00100, 0b01110],
    2: [0b01110, 0b10001, 0b00110, 0b01000, 0b11111],
    3: [0b11110, 0b00001, 0b00110, 0b00001, 0b11110],
}

def draw(bitmap, color):
    px.fill((0, 0, 0))
    for y in range(H):
        for x in range(W):
            if bitmap[y] & (1 << (W - 1 - x)):
                px[xy(x, y)] = scale(color)
    px.write()

count = 0
while True:
    draw(DIGITS[count % 4], (0, 200, 255))
    count += 1
    time.sleep(1)

Frequently Asked Questions

My icon draws scrambled — rows reversed or diagonal garbage. Why?
Your board's physical pixel order doesn't match the xy() assumption. Run the index test (lights 0,1,2… in sequence) and watch: if every other row runs right-to-left, set SERPENTINE = true; if the order starts at a different corner, adjust the mapping (flip x or y). Two minutes with the test pins down any layout permanently.
Can I scroll text on a 5x5?
Yes, with compromises — 5 pixels of height fits a compact 3x5 or 5x5 font one character at a time, and you scroll the message horizontally through the frame. It's charming for short words and numbers. For real sentences, chain several matrices side by side (DOUT to DIN) and extend the xy() mapping across boards, or step up to an 8x32 panel.
How bright dare I go on USB power?
The examples cap at ~20% brightness, which keeps even all-on frames comfortably inside a USB port's budget and is already very bright indoors. Full white at 100% is ~1.5 A — that's external-supply territory (5V 2A, grounds common, capacitor across the pads). As a rule: brightness above 50% plus whole-frame fills is when supplies start mattering.
One pixel is stuck on a wrong color / dead, the rest work.
Each WS2812B regenerates the data signal for the next one, so a damaged pixel usually shows as one bad LED and sometimes garbage after it. Reflow the suspect LED's pads first (cold joints are common), and check it isn't just your bitmap. A truly dead pixel can be replaced with any 5050 WS2812B — or bypassed by wiring its DIN pad to its DOUT pad, shortening the chain by one.
Colors are swapped (red shows green).
WS2812Bs expect GRB byte order and every library defaults to it — but if you've overridden the color order (NEO_RGB, FastLED's RGB template parameter), a red request renders green. Set the order back to GRB and pure red/green/blue test fills will land correctly.
Can I run animations and read sensors at the same time?
Yes — a 25-pixel frame takes under a millisecond to transmit, so even 60 fps animation leaves the CPU mostly idle. Interrupt-heavy work (software serial, precise timers) can conflict during the strict-timing transmit moment on AVR Arduinos; structure loops as "compute frame → show() → do everything else" and it coexists fine. ESP32/Pico drive pixels with hardware (RMT/PIO), so they barely notice.
How do I chain two matrices into a 5x10?
Wire matrix A's DOUT to matrix B's DIN, common 5V/GND (inject power to both), and declare 50 pixels in code. Pixels 0-24 are matrix A, 25-49 are B. Extend the mapping: for x >= 5, index = 25 + xy(x-5, y). Keep the boards oriented identically, and the pair behaves as one wide canvas.

Related Tutorials