Move all animations to Pico firmware, Pi becomes HTTP-serial bridge

Pico runs all 9 mood animations and jaw modes locally instead of
receiving per-LED serial commands from the Pi. New protocol: bare
integers for fire-and-forget jaw amplitude, "mood X" / "jaw X" for
mode switches. Cuts light_service.py from 409 to 193 lines.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex
2026-01-31 21:34:03 -06:00
parent 135ab1138c
commit c74371a24a
4 changed files with 442 additions and 349 deletions

View File

@@ -8,14 +8,15 @@ LED strip control for head-vixy (Vixy's robotic head) via Raspberry Pi Pico 2.
┌─────────────────┐ USB Serial ┌──────────────────┐
│ Raspberry Pi 5 │◄──────────────────►│ Pico 2 │
│ (light_service)│ /dev/ttyACM0 │ (main.py) │
│ Port 8781 │ │
└─────────────────┘ │ GP0 → Jaw LEDs
│ GP1Mood LEDs │
│ Port 8781 │ │ Animation Engine
└─────────────────┘ │
HTTP-to-serial │ GP0Jaw LEDs
bridge │ GP1 → Mood LEDs │
└──────────────────┘
```
- **Raspberry Pi 5** - Runs Python service, HTTP API, animation logic
- **Raspberry Pi Pico 2** - Controls LED strips via PIO, receives serial commands
- **Raspberry Pi 5** - Thin HTTP-to-serial bridge, forwards mode commands
- **Raspberry Pi Pico 2** - Runs all LED animations locally via PIO
- **Mood strip**: 56x WS2812B LEDs on GP1
- **Jaw strip**: 14x WS2812B LEDs on GP0 (index 0 damaged, use 1-13)
@@ -28,11 +29,12 @@ The BBB PRU approach burned 4 boards. Pico 2 is $5, runs MicroPython, has hardwa
```
head-lights/
├── README.md # This file
├── light_service.py # Pi service (animations + HTTP API)
├── light_service.py # Pi service (HTTP-to-serial bridge)
├── requirements.txt # Python deps (pyserial)
├── vixy-lights.service # systemd unit file
├── docs/plans/ # Design documents
└── firmware/
└── main.py # Pico 2 MicroPython firmware
└── main.py # Pico 2 MicroPython firmware (animation engine)
```
## States
@@ -49,7 +51,6 @@ head-lights/
| love | Soft pink 💕 | Gentle breathing | Tender/affectionate |
| sleep | Dim blue-gray | Very slow, dim | Low power/resting |
## API Endpoints
```
@@ -57,30 +58,26 @@ GET /health - Service health (includes serial connection status)
GET /state - Current light state + jaw level
POST /state - Set state: {"state": "listening"}
POST /jaw/level - Set jaw level: {"level": 0-100}
POST /jaw/mode - Set jaw mode: {"mode": "talking"}
```
**Port: 8781**
## Pico Serial Protocol
Commands sent over USB serial at 115200 baud:
All animations run on the Pico. The Pi sends mode commands over USB serial at 115200 baud:
```
<strip> <index> <r> <g> <b> - Set single LED
<strip> -1 - Trigger strip update (show)
<strip> clear - Clear strip
<strip> fill <r> <g> <b> - Fill entire strip (mood only)
```
| Command | Example | Response | Description |
|---------|---------|----------|-------------|
| `<integer>` | `72` | *(none)* | Jaw amplitude 0-100, fire-and-forget |
| `mood <mode>` | `mood thinking` | `OK` | Switch mood animation |
| `jaw <mode>` | `jaw talking` | `OK` | Switch jaw animation mode |
Strip IDs: 0 = Jaw, 1 = Mood
**Mood modes:** idle, listening, responding, pleasure, thinking, playful, commanding, love, sleep, off
Examples:
```
1 0 255 0 0 # Set mood LED 0 to red
1 -1 # Show mood strip
0 clear # Clear jaw strip
1 fill 0 255 255 # Fill mood with cyan
```
**Jaw modes:** idle, talking, off
Bare integers are the fast path for jaw amplitude during speech — minimal bytes, no response wait.
## Installation
@@ -122,7 +119,7 @@ curl http://head-vixy.local:8781/health
# Get current state
curl http://head-vixy.local:8781/state
# Set state
# Set mood state
curl -X POST http://head-vixy.local:8781/state \
-H "Content-Type: application/json" \
-d '{"state": "thinking"}'
@@ -131,6 +128,11 @@ curl -X POST http://head-vixy.local:8781/state \
curl -X POST http://head-vixy.local:8781/jaw/level \
-H "Content-Type: application/json" \
-d '{"level": 75}'
# Set jaw mode
curl -X POST http://head-vixy.local:8781/jaw/mode \
-H "Content-Type: application/json" \
-d '{"mode": "talking"}'
```
## Unified Head Control