Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚦 Traffic Light

An effects server for a fake traffic light built from 3× Adafruit 3649 (64×64 RGB LED) panels, daisy-chained and driven by a Raspberry Pi Zero via an Adafruit RGB Matrix Bonnet/HAT.

The panels are a vertical stack — top = red, middle = yellow, bottom = green — exposed to effects as a single 64×192 canvas of three 64×64 tiles.

A Flask server runs effects in a background render loop and serves a phone-friendly web UI + REST API to switch effects, tweak settings, and auto-rotate. The same code runs on your laptop using a Pillow emulator (live PNG preview in the browser) so you can develop without the hardware.

┌── RED ───┐   tile 0   y[0:64]
│  64×64   │
├── YELLOW ┤   tile 1   y[64:128]
│  64×64   │
├── GREEN ─┤   tile 2   y[128:192]
│  64×64   │
└──────────┘

Quick start (no hardware — runs on your Mac)

Local dev uses uv — it creates an isolated .venv and never touches your global Python.

make deps        # uv sync  → .venv with Flask + Pillow
make run-sim     # emulator backend
# open http://localhost:8080  — live preview + controls

make smoke renders one frame of every effect to catch errors fast.

Two control modes

The light can be driven in either of two modes (toggle in the UI, or POST /api/mode):

  • Whole light — one effect spans the full 64×192 stack (traffic sequence, fish swimming across all three, a tall marquee, …).
  • Per lampeach 64×64 screen runs its own effect independently. Set the top lamp solid red, the middle to scrolling text, the bottom to fish — every screen is individually controllable. (This is also how you get different scrolling text on each panel: just pick text on each lamp.)

Effects

Effect Scope What it does Key settings
traffic whole Solid red/yellow/green, or a timed sequence state, red_s/green_s/yellow_s
solid both Solid colour / lamp circle, optional pulse color, style, pulse
off both Blank (black)
text both Horizontal scrolling marquee text, color, bg, speed, font_size
fish both Fish swimming with bubbles count, bubbles, seed
image both Static image or animated GIF from assets/ file, fit, target, fps

scope: whole = full-light only, both = usable per-lamp or full. Add a new effect by dropping a module in app/effects/, decorating the class with @register, and setting scope (see solid.py).

REST API

Method Path Body Purpose
GET /api/effects List effects + scope + param schemas
GET /api/assets List files in assets/
GET /api/state Mode, fps, backend, unified + per-panel state
POST /api/mode {"mode":"unified"|"panels"} Switch control mode
POST /api/effect {"name","params":{}} Set the whole-light effect
POST /api/params {"params":{}} Tweak the whole-light effect
POST /api/panel {"index","name","params":{}} Set one lamp's effect (index 0=top)
POST /api/panel-params {"index","params":{}} Tweak one lamp's effect
POST /api/rotate {"enabled","interval","order":[]} Auto-rotate (whole-light)
GET /preview.png?scale=N Latest frame as PNG (real & emulator)
# whole light: classic green
curl -X POST localhost:8080/api/effect -H 'Content-Type: application/json' \
  -d '{"name":"traffic","params":{"state":"green"}}'

# per lamp: top=red, middle=scrolling text, bottom=fish
curl -X POST localhost:8080/api/panel -d '{"index":0,"name":"solid","params":{"color":"#ff3014"}}' -H 'Content-Type: application/json'
curl -X POST localhost:8080/api/panel -d '{"index":1,"name":"text","params":{"text":"WALK"}}' -H 'Content-Type: application/json'
curl -X POST localhost:8080/api/panel -d '{"index":2,"name":"fish"}' -H 'Content-Type: application/json'

Hardware

  • 3× Adafruit 3649 — 64×64, 2.5mm pitch, 1/32 scan (5-address, needs the E line).
  • Adafruit RGB Matrix Bonnet (recommended) — handles the E line + 3.3→5V level shifting. Or the RGB Matrix HAT with the 64×64 solder jumper bridged.
  • Flicker fix: solder GPIO 4 ↔ 18 and keep MATRIX_GPIO_MAPPING=adafruit-hat-pwm.
  • Power: each panel pulls up to ~4 A @ 5 V → get a 5 V / 15 A+ supply. Power the panels from it directly (not through the Pi); common the grounds.
  • Wiring: daisy chain OUT→IN through all three panels; Bonnet exposes one chain (MATRIX_CHAIN=3).

First bring-up test (on the Pi)

git clone https://github.com/hzeller/rpi-rgb-led-matrix.git && cd rpi-rgb-led-matrix
make -C examples-api-use
sudo examples-api-use/demo -D0 --led-rows=64 --led-cols=64 --led-chain=3 \
  --led-gpio-mapping=adafruit-hat-pwm --led-slowdown-gpio=2 --led-brightness=50

All three panels lighting as one field = chain good. (Ansible builds the lib for you; this is just the manual smoke test.)

Deploy to the Pi (Ansible)

The Pi must be reachable over WiFi by hostname/IP with SSH key auth.

  1. Edit ansible/inventory.ini — set the hostname/user.
  2. Tune hardware vars in ansible/group_vars/all.yml.
  3. Deploy:
make ping                 # confirm SSH/Ansible reach the Pi
make flash                # full provision + deploy   (make flash PI=192.168.1.42)
make deploy               # re-push app code + restart only
make logs                 # tail the service journal

make flash runs three roles:

  • system — apt packages; blacklists snd_bcm2835 (its PWM conflicts with the matrix — required by hzeller) and reboots if needed.
  • matrix_lib — clones & builds hzeller's python bindings into system Python.
  • app — copies the code to /opt/traffic-light, installs a systemd service (traffic-light, runs as root for GPIO), enables and starts it.

Pi Zero tuning notes

The Zero is single-core ARMv6 — the weakest Pi for this. If you see issues, adjust in group_vars/all.yml and make deploy:

  • Flicker → raise matrix_slowdown_gpio (try 3–4), or lower matrix_pwm_bits.
  • Ghosting / wrong colors → lower matrix_slowdown_gpio.
  • Layout wrong (panels scrambled / sideways) → adjust matrix_pixel_mapper (e.g. "V-mapper:Z", or add Rotate:90) to match your physical cabling.

Layout / project map

app/
  config.py        env-driven config (canvas = 64×192)
  display.py       RealMatrix (rgbmatrix) + EmulatorMatrix (Pillow) backends
  controller.py    render loop, whole-light + per-panel modes, compositing
  server.py        Flask: REST API + web UI + /preview.png
  effects/         traffic, solid, off, text, fish, image  (+ base/registry)
  web/             index.html + app.js control deck (signal-cabinet UI)
ansible/           inventory, group_vars, playbook, roles (system/matrix_lib/app)
assets/            your images & GIFs
scripts/smoke.py   render-every-effect sanity check
Makefile           run-sim / smoke / flash / deploy / logs / ...

About

Code for the Traffic Light project

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages