Repository navigation
Archive Change Requests CR0 CR7
Snapshot of
docs/archive/changerequest/config-app-api.mdat commit707b4a0, preserved before the docs were compacted. History only: the current specs are in the repository.
Archived. All entries below are done (firmware 2.7). New firmware / API requests from the Config App are opened as GitHub issues with the label
firmware, not in this file.
Requested by: Config App (configApp/, branch feature/config-app) · Date: 2026-10-18
These are changes to the firmware and the configuration API (../config-api.md)
that the Config App would benefit from. They are ranked by priority. Each entry can be
implemented on its own. Version 1 of the app works without any of them. The app handles
a missing feature by checking info (field missing → old behaviour).
When you implement one: update config-api.md, the reference clients in
tools/ if affected, bump firmware in info, and mark the entry here as done with the commit.
| ID | Priority | Title | Status |
|---|---|---|---|
| CR-0 | Blocker | BLE responses are dropped when the client subscribes for notifications | done (firmware 2.1, branch feature/cr-0-ble-indicate-only) |
| CR-1 | High | Tell sets apart in the BLE advertisement | done (firmware 2.2, branch feature/cr-1-ble-set-name) |
| CR-2 | Medium | Program format revision in info
|
done (firmware 2.3, branch feature/cr-2-program-format) |
| CR-3 | Medium | Document engine details the simulator must copy | done (docs only, branch feature/cr-3-engine-semantics) |
| CR-4 | Medium |
validate with inline content (dry run) |
done (firmware 2.4, branch feature/cr-4-validate-inline) |
| CR-5 | Low | Program metadata in list
|
done (firmware 2.5, branch feature/cr-5-list-metadata) |
| CR-6 | Low | Optional PIN for write operations | done (firmware 2.6, branch feature/cr-6-write-pin) |
| CR-7 | Medium | Report the BLE MTU in info (documented, not implemented) |
done (firmware 2.7, branch feature/cr-7-mtu-in-info) |
Status: done (firmware 2.1). Option A: the Response characteristic offers INDICATE only.
config-api.md §2 updated. tools/reactpod_config.py needs no change (bleak subscribes for
indications when notify is not offered).
Added 2026-10-18 (P2 of the app).
Reason. The Response characteristic offers INDICATE | NOTIFY
(src/Roles/Central/PhoneConfig.cpp, createCharacteristic(CHAR_RESPONSE_UUID, …)), and
PhoneConfig::sendLine always calls indicate(). The Arduino BLE library drops an indication
when the client enabled only notifications in the CCCD (BLECharacteristic::notify, the
!p2902->getIndications() check, framework-arduinoespressif32 libraries/BLE/src/BLECharacteristic.cpp).
Clients pick notifications when a characteristic offers both:
-
Web Bluetooth (Chrome / Edge):
startNotifications()writes "notifications on" if the characteristic hasNOTIFY, indications only otherwise. There is no way to choose. - Android (and the Capacitor BLE plugin) and most bleak back ends do the same.
So the app (and likely tools/reactpod_config.py) connects and writes requests, but never
gets a response. The app cannot work around this.
Description. Pick one (option A is preferred):
-
A. Remove
PROPERTY_NOTIFYfrom the Response characteristic. Then every client must use indications, which keeps the flow controlsendLinerelies on. Update §2 of config-api.md ("Indicate, CCCD"). -
B. Keep both and send with whatever the client enabled: in
sendLine, read the BLE2902 descriptor;getIndications()→indicate(), elsegetNotifications()→notify(). Notifications have no confirmation, so pace them (e.g. wait for the BLE stack's congestion event or a few ms per packet) to avoid losing data.
Acceptance. In desktop Chrome, navigator.bluetooth → connect → startNotifications() on
Response → write {"op":"info"} to Command → the info response arrives. The same works with
tools/reactpod_config.py info. App acceptance test AT-B1 passes.
Status: done (firmware 2.2), including both optional parts:
- Name
ReactPod <last 4 hex digits of setId>. The central creates itssetIdwhen the menu starts, so the name is stable from the first boot. - Manufacturer data: company id
0xFFFF+setId(4 bytes, big endian). -
{"op":"setName","name":"Halle 2"}stores a set name (≤ 16 bytes) in NVS; it is advertised asReactPod Halle 2(the prefix stays sonamePrefix: "ReactPod"filters still match) and reported ininfo.setName. Per pod, not synced. - Reference clients:
reactpod_config.py --name,scan,set-name;reactpod_serial.py set-name.
Reason. Every central advertises the same name, "ReactPod Config"
(src/Roles/Central/PhoneConfig.cpp:46). Coaches often have several sets in one hall.
The Web Bluetooth chooser (and most native pickers) only shows the name, so the user cannot
tell which set they are about to change. Writing programs to the wrong set is the most
likely mistake a customer will make.
Description.
- Advertise the name as
ReactPod <last 4 hex digits of setId>, e.g.ReactPod 91c2. The name must stay ≤ 29 bytes so it fits into the advertisement. - Keep the service UUID
12345678-1234-1234-1234-1234567890abin the advertisement. The app filters by the service UUID, plus thenamePrefix: "ReactPod". - Optional: put the full
setId(4 bytes) into the manufacturer data, so the native app can show it without connecting. - Optional, nicer: a user-defined set name (≤ 16 characters) stored in NVS and set with a
new op
{"op":"setName","name":"Halle 2"}. If it is set, advertise that name instead. Report it ininfoas"setName".
Acceptance. Two centrals in the menu show up with different names in the Chrome
chooser. The suffix matches the setId in info.
Status: done (firmware 2.3). info.programFormat = BlockFactory::PROGRAM_FORMAT (1).
Changelog table in programs.md §6. Missing field = revision 1.
Reason. info.blockTypes lists the type names only. If a firmware update adds a field
or a port to an existing type (e.g. a new port on button), the app cannot tell whether the
connected pod supports it. Programs using the new field would then pass the app's
validation and fail on the pod, or the field would be ignored silently.
Description. Add "programFormat": <int> to the info response, starting at 1
(matches "version": 1 in program files). Increase it whenever a block type gets new
fields, ports or semantics. Keep a short changelog table in ../programs.md.
Acceptance. info contains programFormat. programs.md lists what each revision added.
Status: done: programs.md §7 "Engine semantics" answers every point
below with code references. Firmware unchanged (docs only). Notable details found: a
timeoutMs of 0 means no timeout for button but immediate timeout for remotePress /
remoteMultiPress; a press of exactly shortMs is long.
Reason. The app simulates programs in the browser. To behave like the pod it has to copy details that are only in the code today. If they change silently, the simulator and the pod drift apart.
Description. Add a section "Engine semantics" to ../programs.md that states:
- the step limit per loop pass (
BlockManager::MAX_STEPS_PER_PASS = 32) and what happens when it is reached (continues in the next pass, so an endless loop of instant blocks does not hang the pod); - which blocks finish immediately and which wait;
-
random/randomOtherranges and howrandomOtherbehaves whenvalue≤ 1; -
delaywithminMs/maxMs: inclusive bounds; -
button: exact meaning ofpressedvsshort/long, what happens when atimeoutfires while the button is held, which port is taken whenshortMsis reached while held; -
remoteMultiPress: how the avoid pods are drawn (central included, can all be avoid?), what happens withpods> connected peripherals; -
remotePresson a pod id that does not exist (→timeout, already documented) and on the central's own id; -
summarywhen the metric has no values (which values are stored).
Acceptance. Every point above is answered in programs.md, with a reference to the code.
Status: done (firmware 2.4). validate accepts data; BlockManager::loadProgram(path, text)
parses the text instead of the file, includes come from the store, nothing is written.
info.features = ["validateInline"]. Reference client: reactpod_config.py validate <path> --file <local>.
Reason. Today a program can only be validated by the firmware after it has been stored on the pod. The app validates locally first, but its validator is a copy of the rules. A dry run lets the app check a program against the real loader before anything changes on the pod, and show the pod's message in the editor.
Description. Extend validate:
→ {"op":"validate","path":"/prog/mygame.json","data":"{ ... }"}
← {"op":"validate","ok":true,"path":"/prog/mygame.json","blocks":14}- With
data, the program text is taken from the request instead of the file. Includes are still read from the store. Nothing is written, the generation does not change. -
datais limited bymaxRequest(12288 bytes). Larger programs are validated after the upload, as today. - Report support in
info, e.g."features":["validateInline"].
Acceptance. A dry run of an invalid program returns invalid_program with the block
name, and list shows no changes afterwards.
Status: done (firmware 2.5). list adds name, peripherals, menu for programs and
fragment: true for fragments. There was no cached manifest (ProgramStore::list reads every
file for its CRC), so the metadata is cached in ProgramStore by path + CRC and a file is only
parsed again after it changed. info.features contains listMeta.
Reason. To show the program overview (name, number of pods, menu pattern) the app
has to get every file. Over BLE with a small MTU this takes several seconds. The app
caches the files by CRC, so this only matters for the first connection to a set.
Description. In the list response add, for files that parse as a program with a
name: "name", "peripherals", "menu" (as in the file). Fragments (no name) get
"fragment": true. The firmware should read this from the cached manifest, not parse each file
on every list.
Acceptance. list on the default set returns names and peripheral counts that match the files.
Status: done (firmware 2.6). setPin (USB serial only), auth, error codes auth_required
/ auth_failed, info.pinRequired. USB serial never needs the PIN. A wrong PIN blocks auth
for 3 s. Reference clients: reactpod_config.py --pin, reactpod_serial.py set-pin.
See config-api.md §8.
Reason. config-api §8: anyone in BLE range can change programs while a central is in the menu. That is fine for now. When customers use the pods in public places (schools, clubs), some will ask for protection.
Description. An optional PIN (4–6 digits) stored in NVS. When it is set, put, delete
and setName (CR-1) need a prior {"op":"auth","pin":"1234"} on the same connection. Read
ops stay open. Set / clear the PIN over USB serial only, so a lost PIN can be reset by the owner.
info reports "pinRequired": true.
Acceptance. With a PIN set, put without auth returns a new error code auth_required.
Over USB serial the PIN can be cleared.
Status: done (firmware 2.7). info and status over BLE report mtu (the connection's
peerMtu, via ConfigApi::setMtuSource set by PhoneConfig). Absent over USB serial.
reactpod_config.py uses it for the write size.
Added 2026-10-18 (P2 of the app).
Reason. config-api.md §5 shows "mtu":247 in the info response, but ConfigApi::opInfo
does not send it. Web Bluetooth does not tell the page the negotiated MTU. Without it the app
has to write requests in 20-byte pieces (default MTU 23), which makes uploads slow: about 100
writes per 2 KB chunk.
Description. Add mtu to info and status: the current peerMtu of the BLE connection
(PhoneConfig.cpp), and leave it out (or null) on USB serial. A small getter on PhoneConfig
that ConfigApi can call (only the "ble" transport instance) is enough.
Acceptance. info over BLE from desktop Chrome reports the MTU Chrome negotiated (usually
517 or 247). Over USB serial the field is absent. The app then writes MTU − 3 bytes per write.
For users and coaches
Repository and workflow
Firmware
- Firmware Overview
- Firmware Architecture
- Firmware Link Protocol
- Firmware Config API
- Firmware Programs
- Firmware Testing
- Firmware Timing
- Firmware Host Syntax Check
Config App
Community platform (on hold)
Decisions (ADRs)
- Decisions
- ADR 0001 Repo Layout and Workflow
- ADR 0002 Versions and Firmware Delivery
- ADR 0003 Community Platform
- ADR 0004 Platform Architecture
- ADR 0005 Live Results over BLE
- ADR 0006 No OTA
- ADR 0007 Platform on Hold
Archive