Skip to content

somakeit/LEDSign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LED Sign Controller

Network-controlled RGB LED panels using ESP32-C3 microcontrollers. Supports multiple panels in a grid configuration with 5 brightness levels per color channel (125 colors).

Hardware

Panel Specs

  • Resolution: 64x16 pixels per panel
  • Colors: 5 levels per channel (125 colors total)
  • Driver ICs: MBI5034 (16-bit shift registers)
  • Scan: 1/4 scan multiplexing
  • Power: 5V, 2A+ per panel

Reference: https://wiki.london.hackspace.org.uk/view/LED_tiles_V2

Wiring (ESP32-C3)

GPIO Function
8 Data 1 (top rows)
1 Data 2 (bottom rows)
4 Clock
7 Latch
2 Output Enable
3 Address A0
6 Address A1

Power

  • Use 5V 2A+ power supply per panel
  • Connect ground between PSU, panel, and ESP32
  • Do not power the panel through USB

Firmware Setup

1. Configure WiFi

Edit src/main.cpp:

const char WIFI_SSID[] = "YourNetwork";
const char WIFI_PASS[] = "YourPassword";

2. Build & Upload

Requires PlatformIO:

pio run -t upload

On boot, each panel displays its IP address. After 10 seconds idle, it shows the IP again.

Python Setup

cd python
pip install pillow

Configuration

Create displays.json to define your panel layout:

{
    "brightness": 128,
    "displays": [
        {"ip": "192.168.1.100", "x": 0, "y": 0},
        {"ip": "192.168.1.101", "x": 64, "y": 0},
        {"ip": "192.168.1.102", "x": 0, "y": 16},
        {"ip": "192.168.1.103", "x": 64, "y": 16}
    ]
}

This creates a 2x2 grid (128x32 pixels total).

Python Library

Single Panel

from ledsign import LEDSign

sign = LEDSign("192.168.1.100")
sign.set_pixel(0, 0, (4, 0, 0))  # Red at max brightness
sign.set_pixel(1, 0, (2, 2, 0))  # Yellow at 50%
sign.send()
sign.close()

Multiple Panels

from ledsign import LEDSignArray

panels = [
    ("192.168.1.100", 0, 0),
    ("192.168.1.101", 64, 0),
]
sign = LEDSignArray(panels)
sign.set_pixel(65, 5, (0, 4, 0))  # Green on second panel
sign.send()
sign.close()

Colors

Values are 0-4 per channel:

from ledsign import RED, GREEN, BLUE, WHITE, Color

sign.set_pixel(0, 0, RED)              # Predefined color
sign.set_pixel(1, 0, (4, 2, 0))        # Orange as tuple
sign.set_pixel(2, 0, Color(1, 1, 1))   # Dim gray

Images

from PIL import Image

img = Image.open("image.png")
sign.load_image(img, dither=True)
sign.send()

Brightness

sign.set_brightness(128)  # 0-255, affects all pixels

Sync Mode (Multi-Panel)

For synchronized updates across panels:

sign = LEDSignArray(panels)
sign.set_sync_mode(True)

while True:
    # Update buffer...
    sign.send_synced()  # Sends to all, then broadcasts sync

Scripts

Demos

python demos.py matrix      # Matrix rain
python demos.py fire        # Animated fire
python demos.py plasma      # Plasma waves
python demos.py starfield   # Flying stars
python demos.py spectrum    # Audio spectrum (fake)
python demos.py pong        # Auto-playing pong
python demos.py gradient    # Color test
python demos.py life        # Game of Life
python demos.py wave        # Sine waves
python demos.py clock       # Digital clock
python demos.py scroller    # Scrolling text

Options:

python demos.py matrix --ip=192.168.1.100         # Single panel
python demos.py matrix --config=my_config.json    # Custom config

GIF Player

python gifplayer.py animation.gif
python gifplayer.py animation.gif --loop=3        # Play 3 times
python gifplayer.py animation.gif --speed=2.0     # Double speed
python gifplayer.py animation.gif --scale=fill    # fill/fit/stretch

Plasma Effect

python plasma.py
python plasma.py --duration=30
python plasma.py --ip=192.168.1.100

Snake Game

python snake.py

Controls: Arrow keys or WASD

FPS Test

python fpstest.py
python fpstest.py --duration=10

Protocol

UDP port 5000.

Frame Packet (L5) - 1028 bytes

Offset Size Description
0-1 2 Magic "L5"
2-3 2 Frame number (uint16 LE)
4-1027 1024 Pixel data

Pixel encoding: r*25 + g*5 + b where r,g,b are 0-4.

Brightness (LB) - 4 bytes

Offset Size Description
0-1 2 Magic "LB"
2 1 Brightness 0-255
3 1 Reserved

Sync Mode (LM) - 4 bytes

Offset Size Description
0-1 2 Magic "LM"
2 1 0=immediate, 1=sync
3 1 Reserved

Sync Command (LY) - 4 bytes

LY\x00\x00 - Display buffered frame. Use broadcast (x.x.x.255) to sync all panels.

How It Works

Display Refresh

The panel uses 1/4 scan with time-division multiplexing for brightness levels:

  1. Timer ISR cycles through 4 banks × 4 brightness levels
  2. For each level, pixels with brightness >= threshold are lit
  3. Variable timing per level creates perceived brightness differences

Data Format

Each refresh cycle shifts 384 bits per data line:

  • 8 groups of 48 bits
  • Order: Blue A, Blue B, Green A, Green B, Red A, Red B

Row mapping per bank:

Bank D1 Rows D2 Rows
0 0, 4 8, 12
1 1, 5 9, 13
2 2, 6 10, 14
3 3, 7 11, 15

Troubleshooting

No display: Check WiFi credentials, verify IP via serial monitor

Flickering: Reduce brightness, check power supply

Laggy: Run fpstest.py - expect 200+ fps network, 50+ fps with rendering

Out of sync: Use set_sync_mode(True) and send_synced()

License

MIT

About

ESP32 controller for the LED Sign

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors