craigm26 / craigm26/OpenCastor
Flashable ESP32 image: the six-dollar robot, browser-flashed, no toolchain
- Dominant language
- Python
- Stars
- 28
- Forks
- 5
- PR merge metrics
- No merged PRs in 30d
Description
## Why
The Pi image (#931) makes the ten-minute clock beatable on a Pi 5 — but the cheapest COMMON hardware is a ~$6 ESP32 devkit, and for a fixed-function vehicle a Pi is overkill. The ESP32 natively has 16 LEDC PWM channels (steering + ESC with no PCA9685 at all), Wi-Fi, and enough headroom to verify Ed25519 envelopes on-device (libsodium/monocypher both run on it). Browser flashing over WebSerial (ESPHome-web-installer style) means the path needs no SD card, no toolchain, no terminal — arguably a FASTER ten minutes than the Pi.
## What
An `opencastor-esp32` firmware plus a web-flasher page:
- Flash from Chrome via WebSerial (ESP Web Tools / esptool-js) — plug in USB, click Install
- First boot: captive-portal Wi-Fi provisioning (the phone joins the setup AP, picks the home network)
- Device generates its identity, then serves the pairing QR at `http://castor-.local` — same payload shape `castor pair` writes
- On-device RCAN verifier: the firmware IS the gateway for this class — read + actuate tiers only, same signed-envelope contract, same refusals; no unsigned control path, no debug write surface
- Drive on its own PWM with neutral-on-timeout deadman IN FIRMWARE (mirror rc-car-actuator's shutdown-neutral semantics)
- No local models — the phone is the only brain; chat rides phone tiers
## Authority rule
Identical to the whole stack: declaring is not permitting, and the signed envelope is the ONLY path to the PWM pins. A firmware build with a bypass (telnet, unauthenticated HTTP control, OTA that skips signing) is a non-starter.
## Acceptance
- Blank ESP32 devkit → browser-flashed → Wi-Fi provisioned → QR scanned → app paired and a signed drive.set twitches a servo — under ten minutes wall-clock, zero installed tools
- Kill power or drop Wi-Fi mid-command → outputs at neutral (bench-verify, same discipline as the PCA9685 latch lesson)
- Envelope refusal behavior matches the Python gateway on the same malformed/over-budget inputs (golden-vector test shared between the two)
## Pointers
- #931 (the Pi path this mirrors), castor/pairing.py `capability_surface_from_manifest` (QR payload projection)
- rc-car-actuator `pca9685.py` / `install_shutdown_neutral` — the neutral/deadman semantics to port
- robot-md-gateway envelope verification (the contract the firmware verifier must match byte-for-byte)
Contributor guide
Research direction
Start with #931, castor/pairing.py capability_surface_from_manifest, rc-car-actuator pca9685.py and install_shutdown_neutral, and robot-md-gateway envelope verification. Compare the existing pairing, neutral/deadman, and envelope contracts before scoping the ESP32 firmware and browser flasher. Done means the stated browser-to-paired-drive flow, neutral-on-failure behavior, and shared malformed-envelope vectors pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, python
- Domain
- embedded-iot, networking, robotics, security, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100