Guide for Claude Code agents working in this repo.
A fork of the MeshCore flasher web UI, tailored into a guided setup page for a specific two-device kit: an ultrasonic water-level sensor (Rook v4 as sender + Heltec V3 as receiver) that bridges LoRa DMs to Bayou over WiFi. Note: the sender is Rook v4 hardware, but its firmware is versioned separately — the current build is tagged v3-ultrasonic (files named rook_sender_ultrasonic_v3.uf2). Don't conflate the hardware rev with the firmware version. The upstream project is a generic multi-device flasher; this fork narrows it to one hardware pair, adds an explainer / TOC / help-menu sections, and hosts the firmware bins locally.
Deploy target: a static blog page. Users open it in Chrome/Edge, flash the Heltec V3 directly over Web Serial, drag the Rook UF2 onto the NICENANO drive, then use the in-page serial console to configure WiFi + pair the devices.
Vue 3 SPA, single index.html + flasher.js + simple-sensor.json. All page copy — section titles, intros, help output, common-settings bullets — lives in simple-sensor.json. The HTML template is a thin renderer that pulls from that JSON via v-html / {{ }}. To edit user-visible text, edit the JSON, not the HTML.
index.html— template. Sections rendered in order: header logos + title → TOC → §1 Overview → §2 Flashing → §3 Connecting via Advert → §4 Configuration.flasher.js— Vue setup. Loads?config=<name>.json(defaultsimple-sensor), fetches SVGs and inlines them (see below), extendscommandReferencefromconfig.commands.simple-sensor.json— the source of truth for all copy and device definitions. Devices, firmware file references, section text, TOC labels, help output — all here.firmware/simple-sensor/*.{bin,uf2}— bundled snapshots. Rename them with a version suffix (e.g._v3,_v4) each release and updatesimple-sensor.jsonto match.img/sensor-system.svg+img/sensor-system-mobile.svg— hero graphic. Both inline PNG references toimg/*.png; must be inlined to render (see next).css/flasher.css— layout, sections, mobile breakpoints (@media (max-width: 640px)and380px).
Inline SVG. When an SVG is loaded via <img src=...>, browsers block internal external references (<image href=...>) for security. So flasher.js fetches the SVG file as text and injects it via v-html. The desktop and mobile SVGs both use this path — see explainerSvgInline / explainerSvgMobileInline.
Mobile-friendly SVG. Two separate SVG files (horizontal for desktop, vertical stack for mobile), toggled by .svg-desktop / .svg-mobile CSS at 640px.
Beer.css nav gotcha. Beer.css styles <nav> as an inline-flex button row. Wrap content lists (e.g. the TOC) in <div>, not <nav>, or the inner <ol> collapses invisibly.
Config JSON conventions. Section keys: explainer, flashingSection, contactExchange, configuration (with intro + commonSettings + helpText). Per-device: firmware[].version['<version-tag>'].files[] with type in download/flash-wipe/flash-update.
Subpath portability. All asset paths are relative (no leading /) so the site works when hosted at any subpath (e.g. edgecollective.io/micro-config/). The SPA URL routing is fragment-based (#/rook-v4/...) rather than pathname-based, so navigation state changes don't push the browser out of the deployment subpath and no server rewrites are needed. When adding new asset paths anywhere (HTML, JSON, JS, SVG), keep them relative.
python3 -m http.server 8765
# open http://localhost:8765/
http.server binds to all interfaces (0.0.0.0) by default, so a phone on the same WiFi can reach http://<LAN-IP>:8765 — find the IP with hostname -I. Add --bind 127.0.0.1 only if you want to restrict access to localhost. For cellular / real testing, use cloudflared tunnel --url http://localhost:8765.
The serial console + web flash use the Web Serial API. It requires HTTPS (or localhost) and works only in Chrome/Edge on desktop — Firefox, Safari, and all mobile browsers can view the page but cannot open the console. Once Chrome grabs a serial port, it holds it exclusively — that's the recurring source of "cannot open /dev/ttyUSB0" errors during firmware development.
- Build new bins in the parent
MeshCore-simple-sensorrepo. - Copy into
firmware/simple-sensor/with a bumped version suffix (_v3→_v4etc.). - Update the file
nameandtitlefields insimple-sensor.jsonunder the appropriatefirmware[].version[].files[]. - Also bump the
versionkey itself if you want to communicate the change to the user.
- Upstream
config.json— kept alongsidesimple-sensor.jsonso?config=configstill works for the original flasher use case. lib/,flasher.jscore flashing logic (flashDevice,Dfu,ESPLoaderusage) — inherited from upstream. Only patch when necessary.