Files
head-lights/README.md
Alex c74371a24a 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>
2026-01-31 21:34:03 -06:00

5.2 KiB

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

# 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

# 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

# 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.

# 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) 🦊💕