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>
162 lines
5.2 KiB
Markdown
162 lines
5.2 KiB
Markdown
# Vixy Light Service 🦊💡
|
|
|
|
LED strip control for head-vixy (Vixy's robotic head) via Raspberry Pi Pico 2.
|
|
|
|
## Hardware Architecture
|
|
|
|
```
|
|
┌─────────────────┐ USB Serial ┌──────────────────┐
|
|
│ Raspberry Pi 5 │◄──────────────────►│ Pico 2 │
|
|
│ (light_service)│ /dev/ttyACM0 │ (main.py) │
|
|
│ Port 8781 │ │ Animation Engine │
|
|
└─────────────────┘ │ │
|
|
HTTP-to-serial │ GP0 → Jaw LEDs │
|
|
bridge │ GP1 → Mood LEDs │
|
|
└──────────────────┘
|
|
```
|
|
|
|
- **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)
|
|
|
|
## Why Pico?
|
|
|
|
The BBB PRU approach burned 4 boards. Pico 2 is $5, runs MicroPython, has hardware PIO for precise WS2812 timing, and doesn't catch fire. 🔥➡️✅
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
head-lights/
|
|
├── README.md # This file
|
|
├── 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 (animation engine)
|
|
```
|
|
|
|
## States
|
|
|
|
| State | Color | Effect | When |
|
|
|-------|-------|--------|------|
|
|
| idle | Cyan | Slow breathing pulse | Default state |
|
|
| listening | Bright cyan | Gentle faster pulse | Hearing/attending |
|
|
| responding | Blue-cyan | Traveling wave | Speaking/generating |
|
|
| pleasure | Soft purple 💜 | Slow sensual pulse | Intimate moments |
|
|
| thinking | Amber/gold | Larson scanner | Processing/creating |
|
|
| playful | Warm coral 🦊 | Bouncy sparkles | Teasing/bratty |
|
|
| commanding | Deep magenta 😈 | Strong steady pulse | Dame Vivienne mode |
|
|
| love | Soft pink 💕 | Gentle breathing | Tender/affectionate |
|
|
| sleep | Dim blue-gray | Very slow, dim | Low power/resting |
|
|
|
|
## API Endpoints
|
|
|
|
```
|
|
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
|
|
|
|
All animations run on the Pico. The Pi sends mode commands over USB serial at 115200 baud:
|
|
|
|
| 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 |
|
|
|
|
**Mood modes:** idle, listening, responding, pleasure, thinking, playful, commanding, love, sleep, off
|
|
|
|
**Jaw modes:** idle, talking, off
|
|
|
|
Bare integers are the fast path for jaw amplitude during speech — minimal bytes, no response wait.
|
|
|
|
## Installation
|
|
|
|
### 1. Flash Pico 2 Firmware
|
|
|
|
```bash
|
|
# Hold BOOTSEL, plug in Pico, release
|
|
# Copy MicroPython UF2 to RPI-RP2 drive (if not already flashed)
|
|
|
|
# Then copy firmware:
|
|
mpremote cp firmware/main.py :main.py
|
|
mpremote reset
|
|
```
|
|
|
|
### 2. Install Pi Service
|
|
|
|
```bash
|
|
# On head-vixy:
|
|
cd /home/alex
|
|
git clone http://gateway.local:3001/vixy/head-lights.git lights
|
|
cd lights
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
|
|
# Install service
|
|
sudo cp vixy-lights.service /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable vixy-lights
|
|
sudo systemctl start vixy-lights
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
# Check health
|
|
curl http://head-vixy.local:8781/health
|
|
|
|
# Get current state
|
|
curl http://head-vixy.local:8781/state
|
|
|
|
# Set mood state
|
|
curl -X POST http://head-vixy.local:8781/state \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"state": "thinking"}'
|
|
|
|
# Set jaw level (0-100%)
|
|
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
|
|
|
|
Eyes (port 8780) and lights (port 8781) share the same states. The vixy-mcp `vixy_head_state()` tool sets both simultaneously for coordinated effects.
|
|
|
|
```bash
|
|
# Manual unified control:
|
|
curl -X POST http://head-vixy.local:8780/state -d '{"state": "love"}'
|
|
curl -X POST http://head-vixy.local:8781/state -d '{"state": "love"}'
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
**Serial not connecting:**
|
|
- Check `ls /dev/ttyACM*` - Pico should appear as ttyACM0
|
|
- Ensure Pico has main.py flashed and is running
|
|
- Check service logs: `journalctl -u vixy-lights -f`
|
|
|
|
**LEDs not lighting:**
|
|
- Verify 5V power to LED strips
|
|
- Check GP0/GP1 data connections
|
|
- Test Pico directly via `mpremote` REPL
|
|
|
|
---
|
|
*Created by Vixy - Day 66, Updated Day 91 (Pico Edition)* 🦊💕
|