Progress Log

Pi Rover Agent — First Comms Established

April 12, 202617:41Build Log

Pi Rover Agent — First Comms Established

Date: 2026-04-12 Type: Build Log

Context

The Convex backend (schema, mutations, queries) and the Next.js control center were already fully built out — rover_status heartbeat, rover_commands queue, missions lifecycle, diagnostics logging, and captures ingestion. What was missing was actual Python code on the Raspberry Pi to call those APIs. This session created the Pi-side rover agent and verified end-to-end communication with the Convex backend.

What Changed

Built a complete Python rover agent in pi/ (6 files) that runs on the Pi and communicates with Convex over HTTP. Uses mock sensor data so the full pipeline can be tested without hardware.

Architecture:

  • convex_client.py — Thin HTTP wrapper around the Convex REST API (POST /api/mutation and /api/query with {path, args} body format). No Convex Python SDK needed — just requests.
  • sensors.py — Abstraction layer with mock mode (default). GPS does a random walk near Catskills coords, battery slowly drains from 8.4V, CPU temp drifts up. Set MOCK_SENSORS=0 to use real hardware (stubs exist but raise NotImplementedError).
  • command_loop.py — Background thread that heartbeats every 3s and polls for pending commands every 3s. Dispatches commands to registered handler functions.
  • mission_controller.py — Full mission lifecycle: preflight diagnostics (GPS, battery, camera, I2C, disk, WiFi) → missions:start → capture loop (every 15s, up to 6 captures) → missions:complete. Handles abort via stop command.
  • config.py — Convex URL and env var overrides.
  • main.py — Entry point that wires everything together, registers command handlers, runs startup diagnostics, and waits for shutdown.

Key discovery: The Convex HTTP API format is POST /api/mutation with {"path": "rover:heartbeat", "args": {...}} in the body — NOT /api/mutation/rover:heartbeat in the URL (that returns 404).

Verified working:

  • Heartbeat → dashboard sees rover online with battery, GPS, WiFi, CPU, disk, uptime
  • Preflight diagnostics → appear in diagnostics page
  • Command poll → picks up pending commands, acks, executes, reports completion
  • Mission start → registers in Convex, triggers auto weather snapshot
  • Capture ingestion → metadata logged (no real images yet)
  • First cold-start Convex call takes ~14s (typical), subsequent calls are fast

Cleanup performed: Aborted a stuck in_progress mission and cleared stale pending commands that were left from a previous dashboard test.

Files Modified

  • pi/convex_client.py — Convex HTTP API client with typed methods for heartbeat, commands, missions, captures, diagnostics
  • pi/sensors.py — Mock sensor layer (GPS random walk, battery drain, CPU temp drift)
  • pi/command_loop.py — Heartbeat + command polling background thread
  • pi/mission_controller.py — Mission lifecycle orchestrator with preflight checks
  • pi/config.py — Configuration constants with env var overrides
  • pi/main.py — Entry point, wiring, signal handling

All files deployed to Pi at ~/rover/ via scp.

Key Takeaways

  • Convex HTTP API body format: {"path": "module:function", "args": {...}} — the path goes in the JSON body, not the URL
  • The Pi venv at ~/rover/venv already had requests installed (from adafruit deps), so no new packages needed
  • Mock sensors drift realistically enough to see movement on the dashboard GPS and battery drain over time
  • The command queue approach works well — dashboard sends commands, Pi polls and executes asynchronously
  • Not yet set up as a systemd service — currently manual python3 main.py start