# Mesh America configurator — provider catalog ZephCore firmware is published to the [Mesh America Device Configurator](https://apps.meshamerica.com) as a third-party provider catalog. Users pick a board in the app and flash ZephCore directly from the browser. ## What to send Mesh America ``` Provider name: ZephCore Catalog URL: https://raw.githubusercontent.com/liquidraver/ZephCore/firmware-dist/catalog.json ``` (Send this once, after the first `master` build has run and created the `firmware-dist` branch. Validate first at .) ## How it works `gen_provider_catalog.py` turns a release's firmware assets into `catalog.json` (the provider-spec schema: `version -> {notes, files:[{type,name,url}]}` with absolute HTTPS URLs). The `build firmware` workflow runs it in the `publish-catalog` job and force-pushes the firmware + catalog to the `firmware-dist` orphan branch on every `master` build. **Hosting / CORS.** The configurator runs in the browser and `fetch()`es both the catalog and each firmware binary, so every response needs `Access-Control-Allow-Origin: *`. GitHub **Release assets do not** send it (they redirect to `release-assets.githubusercontent.com`, which omits the header), so we cannot serve firmware from the normal release. Instead: | Artifact | Host | Why | |----------|------|-----| | `catalog.json` | `raw.githubusercontent.com/.../firmware-dist/catalog.json` | CORS `*`, always fresh (raw doesn't cache branch refs) | | firmware binaries | `cdn.jsdelivr.net/gh/liquidraver/ZephCore@firmware-dist/` | CORS `*`, CDN-backed; filenames carry the git hash so they're effectively immutable | The normal timestamped GitHub Release (with all the same binaries) is still created for manual downloads and OTA — the configurator pipeline is additive. ## Device mapping Boards whose `name` matches an official MeshCore device **fold** into that device (our firmware shows as extra options under the existing tile). Boards MeshCore doesn't have become **new** ZephCore-badged tiles. The mapping table lives in `BOARDS` in `gen_provider_catalog.py`; adjust it there when boards are added or renamed. Names must match MeshCore's canonical `config.json` (`meshcore-dev/flasher.meshcore.io`) **exactly** to fold — the validator flags mismatches. Notes on the mapping: - **ESP32 offers both `flash-wipe` and `flash-update`.** `flash-wipe` = the `-merged.bin` (MCUboot + app) written at `0x0`. `flash-update` = the `-update.bin` (MCUboot-signed app) written at `0x10000`. As of 2026-07-09 ZephCore's ESP32 app slot is at `0x10000` (see `boards/esp32//partitions.overlay`), matching Mesh America's fixed flash-update offset — so app-only updates that keep settings now work. `build.sh` ships `-update.bin` for both ESP32 companion and repeater roles. **Classic-ESP32 (T-Beam)** uses simple boot (no MCUboot), produces no `-update.bin`, and is naturally flash-wipe-only. - **Companion firmware** is one image serving BLE + USB; it's listed under both `companionBle` and `companionUsb` (same file). Native-Linux companions use a custom `companionTcp` role. - **Heltec T114** lists screen and "No screen" variants (subTitle) under one tile. - **Native-Linux** boards (femtofox, rak6421) are `noflash` download-only tiles. - **nRF54L15** boards (ME25LS02) are `noflash` too, for a different reason: the SoC has no USB peripheral, so no bootloader exists and nothing the configurator can drive is possible. They publish a `.hex` as a plain download, flashed over SWD, and the companion is listed under `companionBle` only — there is no USB transport to offer. - **MG24 and STM32WL** are `noflash` download-only for the same reason as the nRF54L15 — no USB device peripheral, so no bootloader and no browser-flashable path. Both publish a `.hex` flashed over SWD. **XIAO MG24** has BLE (Silabs controller blob) so its companion is `companionBle`. **LoRa-E5 mini** has neither BLE nor USB: its companion speaks MeshCore serial framing on USART1, which reaches the host as an ordinary serial port through the board's USB-UART bridge, so it is listed under `companionUsb`. Neither board exists in MeshCore's catalog at all, so both are ZephCore-only tiles. - **Thumbnails:** folded devices inherit MeshCore's image automatically (device-level fields come from the official entry on merge), so only the new tiles set a `tooltip`. Those reuse the configurator's own image set via jsDelivr (`IMG_BASE` in the script); boards without dedicated art fall back to a neutral LoRa icon. - To ship art of our own, drop the file in the repo's `img/` dir and set `own_img` to its filename. `build.sh` copies `img/*` into `firmware/` alongside the firmware, and the generator resolves `own_img` against `--url-base` — so it lands on the same `firmware-dist` branch and CDN as everything else, with no host hardcoded anywhere. - **Copy vendor product shots in, don't hot-link them.** A vendor CDN path is theirs to reshuffle, and a tile whose picture 404s is worse than one that never had a picture. `img/meshtracker_x1.jpg` (Seeed's SenseCAP MeshTracker X1 photo) is the first of these. - **Erase / "wipe settings" (nRF52) uses ZephCore's own formatter.** MeshCore's official erase targets a different flash layout and only *partially* wipes a ZephCore node (observed on a Wio Tracker L1: nuked `channels`, left identity/prefs/contacts, LittleFS half-corrupted). Since spec §4a we override it: each nRF52 firmware option carries an `erase` pointing at ZephCore's formatter. The formatter is **SoftDevice-specific** (v6 and v7 have different partition maps), so `SOFTDEVICE` in the generator maps every nRF52 board to `SoftDevice_v6_formatter.zip` or `SoftDevice_v7_formatter.zip`; `build.sh` publishes both from `formatter/` under stable, un-hashed names so the erase URLs never change. The `.zip` drives the automated DFU erase flow; the `.uf2` is published too as the manual drag-and-drop fallback. - `erase` goes on the **firmware option**, never the device object — a device-level `erase` is an official-only field that providers get silently ignored + warned for (§4a). - ESP32 has no `erase` (the field is nRF52-only); `flash-wipe` already reinstalls cleanly. - Users can still factory-reset on-device without the flasher: the `erase` command over the USB serial CLI, the app's factory reset, or a reflash (ZephCore auto-formats on first boot when it detects an incompatible/blank FS). All format `/lfs` + `/ext` + the NVS bond partition. ## Local test ``` python gen_provider_catalog.py --firmware-dir firmware \ --url-base "https://cdn.jsdelivr.net/gh/liquidraver/ZephCore@firmware-dist" \ --version v0.0.0 --out catalog.json ``` It validates the output against the spec's hard requirements and prints a fold/new summary; a non-zero exit means a schema error.