Progress Log
Control Center: Telemetry, Missions, Rover Live, and Pi API
April 12, 202609:34Build Log
Control Center: Telemetry, Missions, Rover Live, and Pi API
Date: 2026-04-12 Type: Build Log
Context
The Tick Slayer 3000 app had parts tracking, blog, and build docs — but no way to interact with or monitor the rover itself. The goal was to define the data schema for missions, telemetry, and diagnostics from the app side first, so the Pi software knows exactly what to send. This also establishes the command/control layer for remote operation.
What Changed
Convex Schema — 6 new tables
missions— session-level records: route name, status (planned/in_progress/completed/aborted), start/end timestamps, GPS positions, battery start/end, weather, notescaptures— per-stop data: GPS lat/lon, battery voltage, image storage ID, filename, timestamp. Indexed by mission + timediagnostics— pre-flight checks and runtime events: type (camera/gps/i2c/battery/disk_space/wifi/general), pass/warning/fail status, message, numeric valuetick_counts— manual post-session tick logging: count, temperature, humidity, time of day, cloth condition, notesrover_status— singleton heartbeat from Pi: position, battery, WiFi signal, disk free, CPU temp, GPS satellites, state (idle/running/returning/error/offline), uptime. Dashboard considers rover "offline" if lastSeen > 30srover_commands— command queue (dashboard writes, Pi polls): start_mission, stop, return_home, take_photo, pause, resume. Lifecycle: pending → acknowledged → completed/failed
Convex Functions
convex/missions.ts— list, get, getByMissionId, start (Pi calls), complete (Pi calls), update (admin), removeconvex/captures.ts— listByMission, get, ingest (Pi upload script), generateUploadUrl (for image storage)convex/diagnostics.ts— listByMission, listRecent, log (Pi calls)convex/tickCounts.ts— listByMission, listAll, create (admin), updateconvex/rover.ts— heartbeat (Pi), getCurrent (dashboard), sendCommand (admin), pendingCommands (Pi polls), ackCommand (Pi), completeCommand (Pi), recentCommands (dashboard)
Control Center UI — 5 pages
/control-center— Dashboard with Rover Live panel (pulsing online indicator, 6-item telemetry grid, state badge, command buttons, recent command history), stats cards, latest mission summary, recent diagnostics/control-center/missions— Table listing all missions with date, route, status, capture count, duration, battery, GPS start/control-center/missions/[id]— Single scrollable view (no tabs, user preference): live panel for active missions, 5 stat cards, battery drain sparkline from capture data, mission metadata, GPS track as coordinate chips, tick count cards, unified activity timeline with filter chips (All/Captures/Diagnostics/Commands/Status)/control-center/diagnostics— All diagnostics across missions with pass/warn/fail counts and color-coded table/control-center/tick-log— All tick counts with summary stats (total, sessions, avg per session), linked back to parent missions
Sidebar Navigation
Added "Control Center" as a collapsible group with sub-items: Dashboard, Missions, Diagnostics, Tick Log. Uses the Gauge icon.
Documentation
- Guide 9: Pi ↔ Dashboard API Integration (
docs/articles/build-09-pi-api-integration.md) — Complete reference for the Pi-side developer: every Convex mutation/query with Python examples, schema reference tables, data flow diagram, offline queue pattern, command lifecycle, and CLI testing commands - Registered in
article-index.tsunder the Software category
Seed Data
seed:seedMission— Creates a completed "Backyard Perimeter" mission with 8 captures (GPS track around a yard), 6 pre-flight diagnostics (5 pass, 1 WiFi warning), and a tick count (3 ticks: 2 deer, 1 lone star)seed:seedRoverStatus— Creates an idle rover heartbeat with realistic telemetry values
Files Modified
convex/schema.ts— Added missions, captures, diagnostics, tick_counts, rover_status, rover_commands tables with indexesconvex/missions.ts— New: mission CRUD + Pi lifecycle mutationsconvex/captures.ts— New: capture queries + Pi ingest mutation + file upload URLconvex/diagnostics.ts— New: diagnostic queries + Pi log mutationconvex/tickCounts.ts— New: tick count CRUD (admin-gated writes)convex/rover.ts— New: heartbeat, command queue, status queriesconvex/seed.ts— Added seedMission and seedRoverStatus mutationssrc/components/app-sidebar.tsx— Added Control Center collapsible nav groupsrc/app/control-center/page.tsx— New: dashboard with Rover Live panelsrc/app/control-center/missions/page.tsx— New: missions listsrc/app/control-center/missions/[id]/page.tsx— New: mission detail with timelinesrc/app/control-center/diagnostics/page.tsx— New: standalone diagnostics viewsrc/app/control-center/tick-log/page.tsx— New: tick log with trend statssrc/app/docs/article-index.ts— Registered Guide 9 under Software categorydocs/articles/build-09-pi-api-integration.md— New: full Pi API integration guide
Key Takeaways
- App-first schema design — Building the dashboard and API before the Pi software means the data contract is well-defined. The Pi just needs to call the documented mutations
- Rover Live uses a singleton pattern —
rover_statushas one row that gets upserted on every heartbeat, not an append-only log. The 30-second timeout for "online" detection is simple and effective - Command queue is pull-based — The Pi polls
pendingCommandsevery ~3 seconds. No WebSocket or push needed since the Pi already heartbeats on that interval - Mission detail is one view, filterable — User explicitly didn't want tabs. The unified activity timeline interleaves captures, diagnostics, commands, and status events chronologically with filter chips
- Offline queue pattern — Captures always save locally first (image + JSON sidecar), then try to upload. Failed uploads stay queued for the next sync cycle
- Tick counts are manual — Camera can't see the underside of the cloth, so tick counting is a human step: flip cloth after mission, count, log from dashboard
- Deployed to production — Convex prod + Vercel prod + seeded with sample data. Live at tickslayer3000.com/control-center
