Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Open Andon Lights

A modular LED indicator system for visualizing system status and metrics. Combines ESP8266 firmware driving 3×12 WS2812B LED rings with a Python web controller that communicates over USB serial.

Hardware

  • Microcontroller: WeMos D1 Mini (ESP8266)
  • LEDs: 3 × WS2812B addressable LED rings (12 LEDs each)
  • Communication: USB Serial via CH340 (115200 baud)

Architecture

┌─────────────────┐  USB Serial (JSON)  ┌──────────────────┐
│  ESP8266         │ ◄─────────────────► │  Python Controller│
│                  │                     │                   │
│  ┌────────────┐  │                     │  ┌─────────────┐  │
│  │ Module     │  │                     │  │ FastAPI +   │  │
│  │ Registry   │  │                     │  │ HTMX UI     │  │
│  └─────┬──────┘  │                     │  └──────┬──────┘  │
│        │         │                     │        │         │
│  ┌─────┴──────┐  │                     │  ┌─────┴──────┐  │
│  │ Built-in   │  │                     │  │ Modules:   │  │
│  │ Modules    │  │                     │  │ - manual   │  │
│  │ - manual   │  │                     │  │ - llama.cpp│  │
│  │ - status   │  │                     │  └────────────┘  │
│  └─────┬──────┘  │                     │                  │
│        │         │                     │                  │
│  ┌─────┴──────┐  │                     │                  │
│  │ FastLED    │  │                     │                  │
│  │ Ring 0-2   │  │                     │                  │
│  └────────────┘  │                     │                  │
└─────────────────┘                     └──────────────────┘

LED Mapping

Ring LEDs Purpose
0 (Green) 12 Prompt throughput — pulse intensity maps to tokens/s
1 (Yellow) 12 Generation activity — chase animation, speed + color shift
2 (White) 12 Slot utilization — per-slot status (free/busy/error)

Wiring

                    5V POWER SUPPLY (5V / 3A min)
                    ┌─────────────────────────────┐
                    │  VCC ───────────────────────┼──→ 5V (all rings)
                    │  GND ───────────────────────┼──→ GND (all rings + WeMos)
                    └─────────────────────────────┘

WE MOS D1 MINI                     LED RINGS (WS2812B, 12 LEDs each)
┌───────────────┐                  ┌──────────┐    ┌──────────┐    ┌──────────┐
│               │                  │ Ring 0   │    │ Ring 1   │    │ Ring 2   │
│  D1 (GPIO5) ──┼──────────────────┼→ DIN     │    │ DIN      │    │ DIN      │
│               │                  │          │    │          │    │          │
│  5V           │──────────────────┼──────────┼────┼──────────┼────┼──────────┤
│  GND ─────────┼──────────────────┼──────────┼────┼──────────┼────┼──────────┤
│               │                  │          │    │          │    │          │
│               │                  │ DOUT ────┼──→│          │    │          │
│               │                  └──────────┘    │ DOUT ───┼──→│          │
└───────────────┘                                  └──────────┘    └──────────┘

Key points:

  1. External 5V supply required — 36 LEDs × 60mA = ~2.16A max. The WeMos USB cannot power the LEDs. Use a 5V/3A power supply (e.g., phone charger).

  2. Daisy-chain data: D1 (WeMos) → Ring 0 DIN → Ring 0 DOUT → Ring 1 DIN → Ring 1 DOUT → Ring 2 DIN. Ring 2 DOUT is left unconnected.

  3. Common ground: All grounds must be tied together (WeMos + power supply + all rings).

  4. Pin mapping: Data on D1 (GPIO5). 3 rings × 12 LEDs, GRB color order.

  5. Level shifter (optional but recommended): WS2812B expects 5V logic on DIN; the ESP8266 outputs 3.3V. It often works without a level shifter, but for reliability add a 74HCT245 or simple MOSFET level shifter on the data line.

  6. Decoupling capacitor: Place a 470–1000µF capacitor across 5V/GND at the first ring to handle power spikes.

Protocol

Newline-delimited JSON over serial at 115200 baud. Commands include a command field and optional id for request/response matching.

Example command:

{"cmd":"led.set","ring":0,"index":5,"r":255,"g":0,"b":0}

Example response:

{"cmd":"led.set","status":"ok","id":1}

Firmware Setup

Install PlatformIO

# Option 1: pip (recommended)
pip install platformio

# Option 2: Homebrew (macOS/Linux)
brew install platformio

# Option 3: Standalone installer
curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/remote-install.py | python3

Verify installation:

pio --version

Build

cd firmware
pio run

First build will download the ESP8266 Arduino framework and dependencies (FastLED, ArduinoJson). Subsequent builds are faster.

Flash

# Flash to connected ESP8266
pio run --target upload

# Flash to specific port
pio run --target upload --environment esp01 --upload-port /dev/ttyUSB0

If upload fails, put the ESP8266 in boot mode: hold BOOT, press RESET, release RESET then release BOOT, then retry.

Monitor Serial Output

pio device monitor --baud 115200

Dependencies

Managed automatically by PlatformIO via platformio.ini:

  • FastLED — LED driver library
  • ArduinoJson — JSON serialization (v6.x)

Controller Setup

Prerequisites

  • Python 3.10+
  • pip or uv for package management

Install

cd controller
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

Configuration

Edit config.yaml to set your serial port, server address, and module settings:

serial:
  port: "/dev/ttyUSB0"
  baudrate: 115200

server:
  host: "0.0.0.0"
  port: 8081

# Auto-activate a module on startup (e.g. "manual", "llama_cpp", or null)
default_module: null

llama_cpp:
  server_url: "http://127.0.0.1:8080"
  poll_interval: 1.0

Copy config.example.yaml as a starting point:

cp config.example.yaml config.yaml

Run

# Start with default config.yaml
python -m main

# Override config file
python -m main --config /path/to/custom.yaml

# Override host/port
python -m main --host 0.0.0.0 --port 8081

The controller reads the serial port from config.yaml and attempts to connect on startup. If the device is not yet connected, it will retry every 5 seconds until successful. A background loop watches for disconnections and reconnects automatically.

Run as SystemD Service

Install the service file and start the controller:

# Edit the service file to match your paths and user
# controller/andon-lights.service

# Copy to system (or use --user for user-level service)
sudo cp andon-lights.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now andon-lights.service

# Check status
sudo systemctl status andon-lights.service

# View logs
sudo journalctl -u andon-lights.service -f

For a user-level service (no sudo needed):

mkdir -p ~/.config/systemd/user
cp andon-lights.service ~/.config/systemd/user/
# Edit to remove User/Group lines and adjust paths
systemctl --user daemon-reload
systemctl --user enable --now andon-lights.service

Dependencies

  • FastAPI — Web framework
  • Uvicorn — ASGI server
  • Jinja2 — HTML templating
  • HTMX — Dynamic UI without JavaScript SPA
  • pyserial-asyncio — Async serial communication
  • httpx — HTTP client (for llama.cpp module polling)

Web UI

Access at http://localhost:8081

Pages

  • Dashboard (/) — Connection status, module list, LED preview
  • Manual (/manual) — Per-LED control, ring effects, brightness, color presets
  • Llama.cpp (/llama-cpp) — Server config, live metrics display, slot visualization

API Endpoints

Method Path Description
GET /api/status Connection and module status
GET /api/ports Detect available serial ports
POST /api/connect Connect to serial port
POST /api/disconnect Disconnect from serial port
POST /api/module/activate Activate a module by name
POST /api/manual/led Set individual LED color
POST /api/manual/ring Set entire ring color
POST /api/manual/effect Apply effect to ring
POST /api/manual/brightness Set global brightness
POST /api/manual/all Set all LEDs to same color
POST /api/manual/clear Turn off all LEDs
POST /api/llama-cpp/config Configure llama.cpp settings
GET /api/preview/manual Get manual preview state
GET /api/preview/llama-cpp Get llama.cpp preview state

Module System

Both firmware and controller use a pluggable module architecture. Each module defines:

  • onInit() — One-time initialization
  • onActivate() — Called when module becomes active
  • onUpdate() / tick() — Periodic update loop
  • onCommand() — Handle incoming commands
  • getName() — Module identifier
  • getConfig() — Module configuration

Built-in Modules

Manual — Direct LED control for testing and debugging

  • Set individual LEDs, rings, or all LEDs
  • Apply effects (chase, breathe, pulse, strobe, rainbow, gradient)
  • Adjust brightness and color

Llama.cpp — Visualize llama.cpp server metrics

  • Polls /slots endpoint for slot utilization
  • Polls /metrics endpoint for performance data
  • Maps prompt speed to Ring 0, generation activity to Ring 1, slot status to Ring 2

Project Structure

open-andon-lights/
├── firmware/
│   ├── platformio.ini
│   └── src/
│       ├── config.h              # Hardware configuration
│       ├── main.cpp              # Entry point
│       ├── leds.h / leds.cpp     # FastLED driver
│       ├── effects.h / effects.cpp # LED effects
│       ├── protocol.h / protocol.cpp # JSON-over-serial
│       ├── modules.h / modules.cpp   # Module registry
│       └── builtins/
│           ├── manual_module.h / manual_module.cpp
│           └── status_module.h / status_module.cpp
└── controller/
    ├── pyproject.toml
    ├── config.yaml               # Active configuration
    ├── config.example.yaml       # Example configuration
    ├── andon-lights.service      # SystemD service file
    ├── main.py                   # FastAPI application
    ├── modules/
    │   ├── base.py               # SerialDevice, ModuleRegistry, ABC
    │   ├── config.py             # YAML config loader
    │   ├── manual.py             # Manual control module
    │   └── llama_cpp.py          # Llama.cpp metrics module
    └── web/
        ├── static/
        │   └── style.css         # Dark theme styles
        └── templates/
            ├── index.html        # Dashboard
            ├── manual.html       # Manual controls
            └── llama_cpp.html    # Llama.cpp metrics

Troubleshooting

"No serial ports detected"

  • Ensure the CH340 driver is installed (sudo apt install ch341-udev-rules on Debian/Ubuntu)
  • Check permissions: ls -la /dev/ttyUSB*
  • Add user to dialout group: sudo usermod -aG dialout $USER
  • The controller will retry connection every 5 seconds. Connect the ESP8266 and wait.

Auto-reconnect not working

  • Check that the serial port in config.yaml matches the detected port: ls /dev/ttyUSB*
  • If the port changes (e.g., /dev/ttyUSB0/dev/ttyUSB1), update config.yaml and restart the service
  • For SystemD: sudo systemctl restart andon-lights.service

Firmware upload fails

  • Hold the BOOT button while pressing RESET, release RESET then BOOT
  • Verify correct port: ls /dev/ttyUSB*

LEDs not responding

  • Verify power supply can handle LED current (max ~60mA per LED at full brightness)
  • Check data line connection (GPIO5/D1 on WeMos D1 Mini)
  • Use the manual module to test with a simple solid color

About

Andon lights controller and firmware to show app statuses

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages