esp32c3-lab v0.1 plan

PLAN · NO CODE YET

Minimal clone of maqueen-lab for the ESP32-C3 SuperMini

Six labs (drive · servo · leds · neopixels · distance · buzzer), Web Bluetooth over Nordic UART Service, same command vocabulary as maqueen. Hardware ground-truth: Robot-01 v3 schematic. Standalone repo, GitHub Pages.

// status Planning. One file already on disk: assets/styles.css (the styles for this very page). If the plan is rejected, that file is the only cleanup. Reply go to build the rest in the order in § 11.

§ 1Goals

§ 2BLE protocol — copied from maqueen-lab

Service / characteristics

UUID
Service (Nordic UART)6e400001-b5a3-f393-e0a9-e50e24dcca9e
TX (write) — browser → device6e400002-…
RX (notify) — device → browser6e400003-…

ESP32-C3 firmware uses NimBLE-Arduino. Browser uses Web Bluetooth (navigator.bluetooth.requestDevice). Chrome / Edge only.

Wire format

Plain ASCII, lines terminated by \n. Long lines chunked at 20-byte BLE MTU.

TX → device

M:80,80          ← drive motors (left%, right%) [-100..100]
M:STOP           ← halt motors
SRV:1,90         ← servo (index 1..2, angle 0..180)
SWEEP:1,0,180,1500 ← sweep servo i from a to b over T ms
LED:1,1          ← simple LED (index 0..3, 0=off / 1=on)
RGB:0,255,0,0    ← NeoPixel set (idx, r, g, b)
RGB:ALL,0,128,0  ← all 4 NeoPixels green
RGB:CLEAR        ← all off
BUZZ:440,200     ← play freq Hz for duration ms
BUZZ:OFF         ← stop tone
DIST:?           ← request a single ultrasonic reading
STREAM:on        ← enable telemetry streaming
STREAM:off       ← disable
HELLO            ← (built-in) firmware replies HELLO:<version>
FW:?             ← (built-in) firmware replies FW:<version>,<caps>

RX ← device

INFO:CONNECTED   ← greeting
HELLO:0.1.0      ← reply to HELLO
FW:0.1.0,M+S+L+R+B+D ← capabilities
DIST:37          ← distance in cm; "-" = no reading
DIST:-
ECHO:<seq> <verb> ← echo of the command (sequenced)
ERR:<seq> <reason>
BTN:1            ← SW1 (GPIO 0) pressed (only if STREAM:on)
BTN:0            ← released
BATT:3.86        ← battery voltage update

Sequencing & echo (optional, maqueen pattern)

If a TX line begins with a sequence number followed by space — 12 M:80,80 — firmware echoes ECHO:12 M:80,80. Browser uses this to confirm delivery. js/ble-scheduler.js from maqueen handles this; we replicate. Off by default in v0.1 (fire-and-forget).

§ 3Firmware mapping (maqueen verb → Robot-01 hardware)

Per v3 schematic. Same maqueen command, different physical pins.

VerbMaqueen action (micro:bit)Robot-01 action (ESP32-C3)
M:left,rightI²C 0x10 motor regsTB6612FNG: AIN1/AIN2/PWMA (GPIO 1/2/3) for left, BIN1/BIN2/PWMB (GPIO 6/7/10) for right
M:STOPboth = 0brake both motors
SRV:i,angleI²C 0x14 / 0x15 (S1/S2)LEDC PWM 50 Hz on GPIO 6 (J4 = servo 1) or GPIO 10 (J5 = servo 2) ⚠ shares pins with motor B — see § 7
LED:i,ondigital P8 / P12discrete LEDs D5–D8 (GPIO 1, 10) — note: shared with motor pins
RGB:i,r,g,bI²C 0x32 registerFastLED on GPIO 5 → onboard NeoPixel chain D1→D2→D3→D4
RGB:ALL,r,g,bbroadcastfill_solid()
BUZZ:f,msmusic.playTone() on P0ledcWriteTone() on GPIO 4 (transistor stage)
BUZZ:OFFmusic.stopAllSounds()ledcWrite(0)
DIST:?sonarbit P1/P2pulseIn(ECHO) after pulse on TRIG, J6 (GPIO 20/21)
STREAM:on/offperiodic broadcast100 ms loop posting BTN/BATT
HELLO / FW:?reply with versionreply with 0.1.0 + caps string

§ 4File tree (~22 files)

esp32c3-lab/
├── README.md                     ← overview · live URL · quick start
├── PLAN.md / plan.html           ← this plan (md + html)
├── package.json                  ← npm run serve wraps tools/serve.py
├── manifest.json                 ← PWA manifest
├── sw.js                         ← minimal offline-cache service worker
├── .gitignore  ·  .nojekyll      ← repo housekeeping
├── index.html                    ← landing — 6 lab cards, connect, theme/lang pickers
├── assets/
│   └── styles.css                ← ✅ ALREADY WRITTEN — 4 themes + UI components
├── js/
│   ├── ble.js                    ← Web Bluetooth NUS — connect, write, notify, reconnect
│   ├── ble-scheduler.js          ← serializes writes; seq# / echo
│   ├── protocol.js               ← high-level senders: drive(l,r), servo(i,a), led(i,s)…
│   ├── lab-shell.js              ← injects shared chrome (header, status pill, log)
│   ├── i18n.js                   ← EN / FR / AR strings; RTL toggle on AR
│   └── ui.js                     ← theme picker, lang picker, status pill, log scroll
├── labs/
│   ├── drive-lab.html            ← virtual joystick → M:left,right
│   ├── servo-lab.html            ← two sliders → SRV:1,a / SRV:2,a + sweep
│   ├── leds-lab.html             ← four toggles for D5–D8 → LED:i,s
│   ├── neopixels-lab.html        ← four colour pickers → RGB:i,r,g,b
│   ├── distance-lab.html         ← live bar + numeric, polls DIST:? at 5 Hz
│   └── buzzer-lab.html           ← piano keys + freq/duration sliders → BUZZ:f,ms
├── firmware/
│   ├── esp32c3-lab.ino           ← Arduino sketch — NimBLE NUS server + parser
│   ├── platformio.ini            ← pinned platform 6.7.0 + libs
│   └── README.md                 ← wiring · flashing · troubleshooting
├── tools/
│   └── serve.py                  ← Python stdlib http.server → :8000
├── serve.bat / serve.sh          ← launchers
└── .github/workflows/
    └── pages.yml                 ← deploy → gh-pages

§ 5Lab pages — one-line spec each

🕹️ drive-lab

Round joystick (drag/tap) + STOP button

TX: M:l,r throttled 50 ms · M:STOP on release

⚙️ servo-lab

Two sliders + presets (0° / 90° / 180° / sweep)

TX: SRV:i,a · SWEEP:i,a,b,t

💡 leds-lab

4 toggle cards for D5–D8 + all-on/all-off

TX: LED:i,1 / LED:i,0

🌈 neopixels-lab

4 colour pickers + ALL/CLEAR + 6 preset palettes

TX: RGB:i,r,g,b · RGB:ALL,r,g,b · RGB:CLEAR

📡 distance-lab

Big numeric (cm) + horizontal bar + sparkline

TX: DIST:? polled 5 Hz · RX: DIST:<n> / DIST:-

🎹 buzzer-lab

Piano (C-D-E-F-G-A-B-C) + freq/duration sliders

TX: BUZZ:f,ms · BUZZ:OFF

Every lab inherits from lab-shell.js: top header (title, back link, theme/lang pickers, status pill), right rail (or bottom on mobile) with TX/RX Message Log, BLE state banner.

§ 6UI conventions

§ 7Hardware caveats (need to acknowledge)

  1. Servo / motor pin sharing — J4 (GPIO 6) and J5 (GPIO 10) host both TB6612FNG BIN1/PWMB AND the servo headers. Cannot drive motor B AND a servo at the same time. Firmware enters one of two modes at boot, selectable via MODE:motors or MODE:servos (default: motors).
  2. GPIO 3 = PWMA + J3 — same pin drives motor A speed AND any device plugged into J3. Don't plug servos into J3 if you also want motor A.
  3. GPIO 8/9 = I²C — used by OLED in v3 board. Lab firmware doesn't drive the OLED yet.
  4. GPIO 0 = SW1 — read by firmware, broadcast as BTN:1/0 events.
  5. Battery monitoring — no ADC pin wired in v3; firmware returns stub BATT:? in v0.1. v0.2 needs a battery divider on a free GPIO.

§ 8Build / deploy

StepCommand
Local serve./serve.sh (or serve.bat) → http://localhost:8000
Flash firmwareArduino IDE OR pio run -t upload
Pushgit push → GitHub Action mirrors to gh-pages
Livehttps://abourdim.github.io/esp32c3-lab/

§ 9Out of v0.1 (deferred to v0.2+)

§ 10Open questions for you to confirm

  1. Default themepaper (light, classroom-ready) or steel (dark, workshop-ready)? Default chosen: paper.
  2. App title shown in the headerESP32-C3 Lab or Robot-01 Lab or something else? Default chosen: ESP32-C3 Lab.
  3. Sequence numbers — every TX gets a <seq> prefix and waits for ECHO:<seq> before sending the next, OR fire-and-forget for v0.1? Default chosen: fire-and-forget. Sequencing infra in ble-scheduler.js is wired but disabled.
  4. Servo / motor mode — does the firmware ship in motor mode by default (servos won't respond on J4/J5 until MODE:servos), or detect at runtime? Default chosen: motor mode at boot. Servo lab issues MODE:servos on first SRV: command.
  5. GitHub repo — create github.com/abourdim/esp32c3-lab (public, gh-pages on master) on first push? Default chosen: yes, public.

§ 11How to proceed

Reply with: