Skip to content

Archive Change Requests CR0 CR7

Patrick Stähle edited this page Oct 30, 2026 · 1 revision

Snapshot of docs/archive/changerequest/config-app-api.md at commit 707b4a0, preserved before the docs were compacted. History only: the current specs are in the repository.

Change requests from the Config App

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)

CR-0 – BLE responses are dropped when the client subscribes for notifications (Blocker)

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 has NOTIFY, 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_NOTIFY from the Response characteristic. Then every client must use indications, which keeps the flow control sendLine relies 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(), else getNotifications() → 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.


CR-1 – Tell sets apart in the BLE advertisement (High)

Status: done (firmware 2.2), including both optional parts:

  • Name ReactPod <last 4 hex digits of setId>. The central creates its setId when 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 as ReactPod Halle 2 (the prefix stays so namePrefix: "ReactPod" filters still match) and reported in info.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-1234567890ab in the advertisement. The app filters by the service UUID, plus the namePrefix: "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 in info as "setName".

Acceptance. Two centrals in the menu show up with different names in the Chrome chooser. The suffix matches the setId in info.


CR-2 – Program format revision in info (Medium)

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.


CR-3 – Document engine details the simulator must copy (Medium, docs only)

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 / randomOther ranges and how randomOther behaves when value ≤ 1;
  • delay with minMs/maxMs: inclusive bounds;
  • button: exact meaning of pressed vs short/long, what happens when a timeout fires while the button is held, which port is taken when shortMs is reached while held;
  • remoteMultiPress: how the avoid pods are drawn (central included, can all be avoid?), what happens with pods > connected peripherals;
  • remotePress on a pod id that does not exist (→ timeout, already documented) and on the central's own id;
  • summary when the metric has no values (which values are stored).

Acceptance. Every point above is answered in programs.md, with a reference to the code.


CR-4 – validate with inline content (Medium)

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.
  • data is limited by maxRequest (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.


CR-5 – Program metadata in list (Low)

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.


CR-6 – Optional PIN for write operations (Low, later)

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.


CR-7 – Report the BLE MTU in info (Medium)

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.

Clone this wiki locally