From e1f8a99c0e9ff4c1ef1dba521ccf8abcaf99968b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 19:24:34 +0000 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20cut=20v4.1.0=20=E2=80=94=20version?= =?UTF-8?q?=20stamp,=20CHANGELOG=20cut,=20channel-accurate=20release=20not?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FIRMWARE_VERSION 4.0.0 -> 4.1.0 (matches the v4.1.0 tag to come). MINOR is right: new settings and device behaviour, nothing removed and no format break — track files, the DOVEX layout, log filenames and the BLE command protocol are byte-for-byte what 4.0.0 wrote. CHANGELOG: [Unreleased] had accreted the same shape as last cut — two separate "### Changed" headings and a trailing "### Added" after "### Fixed" from successive merges. Consolidated into one Added/Changed/Fixed set under [4.1.0] - 2026-08-22. All 20 entries and every body line moved VERBATIM (verified by diffing the sorted line multiset before and after); only the five entries below gained a channel marker. The marker is the substantive change here. Most of this release's headline work — the whole NeoPixel subsystem — is behind BIRDSEYE_ENABLE_NEOPIXEL, which is off in the published images, so a reader of these notes would otherwise go looking for LEDs that a release build never lights. Each affected entry is now marked *(beta channel)*, and the section intro says plainly which half of the release is live for every user. Two plan-0007 behaviours are NOT gated and were being described as if they were: the tach page's corrected *OVER REV* header (display_pages.ino reads settingOverrevLimit outside any #if) and the pace page's STOPPED state (raceEngineStopped(), same) both ship active — those entries now say *(every build)* on that half and *(beta channel)* on the LED half. Link refs: [Unreleased] was still comparing from v3.1.0, stale since the 4.0.0 cut, and [4.0.0] never got a ref at all. Both fixed, [4.1.0] added. DovesLapTimer pins are deliberately untouched. compile-sketch.yml warns that promoting the channel means bumping them, but the library's BETA branch is source-identical to the v4.3.0 tag master already pins (the only difference between the two refs is five CI workflow files), so there is nothing to bump. Verified: 507 host test cases / 325,581 assertions pass. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U6kd8xwnUbXyd2Ce6m8ydX --- BirdsEye/project.h | 4 +-- CHANGELOG.md | 75 ++++++++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/BirdsEye/project.h b/BirdsEye/project.h index 9d8ec19..907646e 100644 --- a/BirdsEye/project.h +++ b/BirdsEye/project.h @@ -31,10 +31,10 @@ #ifdef FIRMWARE_VERSION_OVERRIDE #define FIRMWARE_VERSION _BE_TOSTRING(FIRMWARE_VERSION_OVERRIDE) #else - // The 4.0.0 release cut (matches the v4.0.0 tag). The webapp still keys + // The 4.1.0 release cut (matches the v4.1.0 tag). The webapp still keys // the track JSON budget off this — 8 KB at or above 3.2.0 — so never // stamp a build below that line again. - #define FIRMWARE_VERSION "4.0.0" + #define FIRMWARE_VERSION "4.1.0" #endif /////////////////////////////////////////// diff --git a/CHANGELOG.md b/CHANGELOG.md index 4544d7a..5f5b407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,30 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2 ## [Unreleased] -Slated to release as **4.1.0** (minor — new NeoPixel subsystem on the -beta channel, plus the fixes below). +Nothing yet. + +## [4.1.0] - 2026-08-22 + +MINOR — new settings and device behaviour, all backwards compatible. Track +files, the DOVEX log format, the log filenames and the BLE command protocol +are byte-for-byte unchanged from 4.0.0, so 4.0.0 logs, tracks and companion +apps keep working. + +**Which of this is live in the release firmware.** The NeoPixel strip +(subsystem 16) and the SensorEgg EGT POC (subsystem 14) sit behind the +`BIRDSEYE_ENABLE_NEOPIXEL` / `BIRDSEYE_ENABLE_SENSOREGG` build flags, and +those flags are **off** in the published `BirdsEye-sense` / +`BirdsEye-nonsense` images — a flag-on build performs a one-way +NFC-pads-to-GPIO conversion on hardware that mostly has no LEDs wired, so it +is not something to push to the whole fleet. Entries below marked +*(beta channel)* therefore ship as code in this release but are only active +in a beta build. Everything else is live for every user: the Bluetooth +download speed-up, the RPM filter rework, the `tach_filter` and pickup-health +diagnostics, the tach page's corrected `*OVER REV*` header, the pace page's +`STOPPED` state, the four new Back/Cancel menu rows, and the transfer-mode +exit fixes. The new LED settings keys are written to `/SETTINGS.json` on +every build (so a card moved into a beta unit is already configured); only a +flag-on build reads them. ### Added - **NeoPixel LED strip subsystem** (beta channel only, @@ -41,12 +63,14 @@ beta channel, plus the fixes below). every filename stay exactly as they were, and timezone presentation remains the viewing app's job. - **Overrev alert** (plan 0007): new `overrev_limit` setting (default 0 - = disabled) — past it the whole LED chain flashes red until RPM falls - back below the normal `rev_limit`. The rev limit warns the engine is - at its ceiling; the overrev limit says it's broken. -- **Temp1 alert threshold setting** (`temp1_alert_c`, default 650 °C): - the right status LED is now a tri-state — flashing red at/above the - limit, off when good, solid blue when there is no probe signal. + = disabled) — past it the whole LED chain flashes red *(beta channel)* + and the tach page's `*OVER REV*` header trips *(every build)*, both + until RPM falls back below the normal `rev_limit`. The rev limit warns + the engine is at its ceiling; the overrev limit says it's broken. +- **Temp1 alert threshold setting** *(beta channel)* (`temp1_alert_c`, + default 650 °C): the right status LED is now a tri-state — flashing red + at/above the limit, off when good, solid blue when there is no probe + signal. - **`tach_filter` setting** (default `smooth`, plan 0009): picks the RPM estimator, so the tach can be A/B'd against a live engine at the track instead of argued about from a plotted log. `smooth` is the new filter @@ -60,9 +84,14 @@ beta channel, plus the fixes below). thrown away this power-cycle. A count that climbs with RPM is ignition ringing or missed sparks reaching the ISR, i.e. the pickup rather than the filter. -- **GPS-search pip**: in race mode without a full GPS lock the strip - shows a green pixel bouncing end-to-end instead of the RPM scale, so - a not-yet-timing session is visibly "searching". +- **GPS-search pip** *(beta channel)*: in race mode without a full GPS + lock the strip shows a green pixel bouncing end-to-end instead of the + RPM scale, so a not-yet-timing session is visibly "searching". +- **The browser-sim harness can fake a GPS fix.** A "GPS fix" toggle (plus + an mph field) streams a deterministic synthetic 25 Hz fix parked on the + bundled OKC track's start line, so fix-gated flows — most usefully the + on-device course creator, including its 3 s point-averaging hold — can be + exercised in the simulator without loading a log file. ### Changed - **Settings file and JSON document buffers raised 512 -> 1024 bytes** @@ -100,16 +129,15 @@ beta channel, plus the fixes below). a flat ~20 RPM (worst case under 300) from 1500 to 14 000 RPM. The cost is ~90 ms more lag on a 5500 RPM/s pull. Set `tach_filter` to `legacy` to get the old behaviour back. -- **Engine dies mid-session** (tach-proven sessions): the LED bar goes - dark and the pace page shows `STOPPED` instead of a still-counting - pace — status LEDs (temp alert) stay live. Clears on restart. +- **Engine dies mid-session** (tach-proven sessions): the pace page shows + `STOPPED` instead of a still-counting pace *(every build)*, and the LED + bar goes dark while the status LEDs (temp alert) stay live + *(beta channel)*. Clears on restart. - **Tach page `*OVER REV*` header** now means actual overrev: it shows only when `overrev_limit` is enabled and RPM reaches it (was a hardcoded 9999 RPM). The `rev_limit` warning stays on the LED only. -- **Temp status LED flashes red** (was orange), matching the rev - flasher's alert language. - -### Changed +- **Temp status LED flashes red** *(beta channel)* (was orange), matching + the rev flasher's alert language. - **Bluetooth downloads are faster, and now say why when they are not** (plan 0008). A 3.3 MB session downloading at 28.8 KB/s on an iPad prompted a look at the whole transfer path. Three things were capping @@ -179,13 +207,6 @@ beta channel, plus the fixes below). (16 columns after the `Temp1`/`Junction1`/`Temp2` additions) injected nothing. It now accepts 13+ and reads the stable first 13. -### Added -- **The browser-sim harness can fake a GPS fix.** A "GPS fix" toggle (plus - an mph field) streams a deterministic synthetic 25 Hz fix parked on the - bundled OKC track's start line, so fix-gated flows — most usefully the - on-device course creator, including its 3 s point-averaging hold — can be - exercised in the simulator without loading a log file. - ## [4.0.0] - 2026-08-10 ### Added @@ -1419,7 +1440,9 @@ Initial tagged release. Core capabilities: - 8+ OLED display pages, Bluetooth LE file download / settings / track sync, and a low-power sleep mode. -[Unreleased]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v3.1.0...HEAD +[Unreleased]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v4.1.0...HEAD +[4.1.0]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v4.0.0...v4.1.0 +[4.0.0]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v3.1.0...v4.0.0 [3.1.0]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v3.0.2...v3.1.0 [3.0.2]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v3.0.1...v3.0.2 [3.0.1]: https://github.com/TheAngryRaven/DovesDataLogger/compare/v3.0.0...v3.0.1 From 938e45dd02f1736b643a5b117d4b76c80ffa1cde Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 21:58:17 +0000 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20ship=20the=20NeoPixel=20strip=20in?= =?UTF-8?q?=20every=20build=20=E2=80=94=20BIRDSEYE=5FENABLE=5FNEOPIXEL=20d?= =?UTF-8?q?efaults=20to=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LED strip stops being a beta-only build and becomes a core feature in 4.1.0: a stock logger drives a strip the moment someone wires one, with no special firmware. The flag's #ifndef default in project.h goes 0 -> 1, which is the whole mechanism — every workflow already installs the Adafruit NeoPixel library, release.yml passes no feature flags at all, and compile-sketch.yml's non-BETA arm passes an empty FEATURE_FLAGS, so all three channels pick the new default up on their own. beta.yml's explicit -DBIRDSEYE_ENABLE_NEOPIXEL=1 is now redundant but harmless, and left alone. WHAT THIS COSTS, stated plainly because no later firmware can undo it: the subsystem's first boot programs UICR->NFCPINS to convert the two NFC pads to GPIO and self-resets once so the latch takes effect. That is a ONE-WAY change — reversing it needs a full chip erase and a bootloader reflash over USB — and with the flag on by default it is charged to EVERY device that installs 4.1.0, wired for LEDs or not, since the firmware cannot tell the difference. The trade was made deliberately: this hardware never uses NFC, the pads are otherwise idle, and gating a headline feature behind a separate build had kept it out of everyone's hands. Users who want the pads for something else must not install 4.1.0, so the CHANGELOG leads its 4.1.0 section with an upgrade note saying exactly that, including the extra reboot to expect. No other translation unit changes: grep confirms `#if BIRDSEYE_ENABLE_NEOPIXEL` appears nowhere outside neopixel.ino, so nothing else compiles differently and the simulator (which excludes that file) is untouched — 6/6 sim ctest still green with the flag flipped, goldens and both lap oracles included. Docs follow the decision rather than trailing it: the CHANGELOG's channel note is replaced by the upgrade warning and the per-entry "(beta channel)" markers come off the LED entries (they are not beta any more); CLAUDE.md subsystem 16, its flags list, constants table, File Map intro and workflows row; the ARCHITECTURE LED bullet; CONTRIBUTING's flag table (now noting this is the one flag defaulting to 1); and compile-sketch.yml's channel comment, where the difference between the two arms is now just SensorEgg and the library ref. Two in-code comments still calling the strip beta-only are corrected too. Verified: 507 host test cases / 325,581 assertions and 6/6 sim ctest. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01U6kd8xwnUbXyd2Ce6m8ydX --- .github/workflows/compile-sketch.yml | 5 +- ARCHITECTURE.md | 6 ++- BirdsEye/BirdsEye.ino | 6 ++- BirdsEye/project.h | 36 +++++++++----- BirdsEye/settings.ino | 6 +-- CHANGELOG.md | 72 +++++++++++++++------------- CLAUDE.md | 38 +++++++++------ CONTRIBUTING.md | 2 +- 8 files changed, 102 insertions(+), 69 deletions(-) diff --git a/.github/workflows/compile-sketch.yml b/.github/workflows/compile-sketch.yml index dbd9e92..5e07000 100644 --- a/.github/workflows/compile-sketch.yml +++ b/.github/workflows/compile-sketch.yml @@ -37,7 +37,10 @@ jobs: # SensorEgg POC and the NeoPixel strip exactly as beta.yml does, so # the flag-on build is compile-checked on the PR rather than first # failing on the publish workflow. Everything else builds the - # master/release defaults (off). + # project.h defaults — which since 4.1.0 means the NeoPixel strip is + # ON (it is a core feature now; the flag defaults to 1) and only the + # SensorEgg POC is off. So the difference between the two arms is + # now just SensorEgg and the DovesLapTimer ref. FEATURE_FLAGS: ${{ (github.base_ref == 'BETA' || github.head_ref == 'BETA' || github.ref_name == 'BETA') && '-DBIRDSEYE_ENABLE_SENSOREGG=1 -DBIRDSEYE_ENABLE_NEOPIXEL=1' || '' }} # Build both XIAO nRF52840 variants. The Sense board has the onboard # LSM6DS3 IMU; the plain board does not (accelerometer logging degrades diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 05f16b3..ccdc70c 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -142,8 +142,10 @@ to the matching `*_LOOP()`. `led_brightness` by day, `led_brightness_night` after dark (see *Local time* below). The strip's 5 V boost converter has its EN pin driven low in sleep, so System OFF really - powers the LEDs down. Gated on `BIRDSEYE_ENABLE_NEOPIXEL`: on in - beta, off (fully compiled out, no UICR write) in master/release. + powers the LEDs down. `BIRDSEYE_ENABLE_NEOPIXEL` is on in **every** + channel as of 4.1.0, which is what makes the strip a core feature — at + the price of a one-way, fleet-wide UICR NFC→GPIO conversion on the + first boot after updating. - **Course creator** (`course_creator` + `track_json` pure units, glued into the menu/pages/SD modules) — authors a track course on the device by walking to each cone and holding for a 3 s GPS average. Autocross diff --git a/BirdsEye/BirdsEye.ino b/BirdsEye/BirdsEye.ino index 3f13b6f..cc6bfec 100644 --- a/BirdsEye/BirdsEye.ino +++ b/BirdsEye/BirdsEye.ino @@ -1078,8 +1078,10 @@ void setup() { // beta channel): the one-time UICR NFC->GPIO write needs direct NVMC // access, which is illegal once the SoftDevice is up. Also before // wdtSetup() so the one-time self-reset can't race the watchdog. Needs - // SETTINGS_SETUP (led_brightness) — a no-op unless - // BIRDSEYE_ENABLE_NEOPIXEL is set (beta channel only). + // SETTINGS_SETUP (led_brightness). Since 4.1.0 this runs on EVERY + // channel — BIRDSEYE_ENABLE_NEOPIXEL defaults to 1 — so the first boot + // of any 4.1.0+ image is the one that spends the NFC pads and resets + // once. See project.h. NEOPIXEL_SETUP(); // Camera auto-record: load the persisted Insta360 serial + init the FSM diff --git a/BirdsEye/project.h b/BirdsEye/project.h index 907646e..3a82384 100644 --- a/BirdsEye/project.h +++ b/BirdsEye/project.h @@ -84,21 +84,31 @@ // ---- NeoPixel strip (11 px: 2 status + 9-px pace/RPM strip) ---- // -// 0 (default — master and release): the whole subsystem is compiled out. -// The module's entry points become no-ops and, critically, the firmware -// NEVER writes UICR->NFCPINS and never drives pins 30/31 (P0.09/P0.10, -// the NFC pads) — a flag-off build leaves the pads exactly as it found -// them. +// 1 (default — master, beta and release all ship this as of 4.1.0): the +// strip is a CORE feature, present in every image so a logger works the +// moment someone wires LEDs to it. On first boot NEOPIXEL_SETUP() +// converts the NFC pads to GPIO by programming UICR->NFCPINS and +// self-resets once so the pin latch takes effect. After that: pin 30 = +// boost converter EN, pin 31 = WS2812 data. See plan 0006 and neopixel.h. // -// 1 (the beta channel passes -DBIRDSEYE_ENABLE_NEOPIXEL=1): on first -// boot NEOPIXEL_SETUP() converts the NFC pads to GPIO by programming -// UICR->NFCPINS (a ONE-WAY change — undoing it needs a full chip erase, -// i.e. a bootloader reflash; accepted, NFC is never used on this -// hardware) and self-resets once so the pin latch takes effect. After -// that: pin 30 = boost converter EN, pin 31 = WS2812 data. See plan -// 0006 and neopixel.h. +// KNOW WHAT THIS COSTS, because it is charged to every unit in the field, +// not just the ones with LEDs on them. The UICR write is ONE-WAY — undoing +// it needs a full chip erase, i.e. a bootloader reflash over USB — so the +// first boot after updating to 4.1.0 permanently spends the NFC pads and +// reboots itself once, on every device, wired for LEDs or not. That was the +// deliberate 4.1.0 decision (NFC is not used on this hardware and the pads +// are otherwise idle); it is recorded here rather than in a commit message +// because nothing about a later build can undo it. +// +// 0 (no shipped channel sets this; -DBIRDSEYE_ENABLE_NEOPIXEL=0 forces it): +// the subsystem is compiled out — no Adafruit_NeoPixel dependency, and on a +// board that has NOT already been converted the firmware never writes UICR +// and never drives pins 30/31. On one that HAS (it ran a flag-on build +// before), the stubs still hold the boost EN pin low, because a floating EN +// leaves the 5 V rail up through System OFF — see the #else block in +// neopixel.ino. #ifndef BIRDSEYE_ENABLE_NEOPIXEL - #define BIRDSEYE_ENABLE_NEOPIXEL 0 + #define BIRDSEYE_ENABLE_NEOPIXEL 1 #endif /////////////////////////////////////////// diff --git a/BirdsEye/settings.ino b/BirdsEye/settings.ino index 876378a..02ec442 100644 --- a/BirdsEye/settings.ino +++ b/BirdsEye/settings.ino @@ -139,9 +139,9 @@ static void ensureDefaultSettings() { // starts the rotation at the speed page — end users never see the // diagnostic counters; "show" restores them for development/tuning. { "debug_pages", "hide" }, - // NeoPixel strip (plan 0006). Written on every channel so - // SETTINGS.json has a uniform shape (SensorEgg DOVEX-column - // precedent); only a BIRDSEYE_ENABLE_NEOPIXEL build reads them. + // NeoPixel strip (plan 0006). Read on every channel since 4.1.0 — + // BIRDSEYE_ENABLE_NEOPIXEL defaults to 1, so these are live + // settings on a stock logger, not beta-only bookkeeping. { "led_brightness", "64" }, // global cap 0-255; 0 = LEDs disabled { "rev_limit", "15000" }, // true RPM: LED scale ceiling + rev flasher + OVER REV header // Plan 0007: the PROBLEM limit (0 = disabled) — whole LED chain diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5b407..6c8a712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,25 +21,36 @@ files, the DOVEX log format, the log filenames and the BLE command protocol are byte-for-byte unchanged from 4.0.0, so 4.0.0 logs, tracks and companion apps keep working. -**Which of this is live in the release firmware.** The NeoPixel strip -(subsystem 16) and the SensorEgg EGT POC (subsystem 14) sit behind the -`BIRDSEYE_ENABLE_NEOPIXEL` / `BIRDSEYE_ENABLE_SENSOREGG` build flags, and -those flags are **off** in the published `BirdsEye-sense` / -`BirdsEye-nonsense` images — a flag-on build performs a one-way -NFC-pads-to-GPIO conversion on hardware that mostly has no LEDs wired, so it -is not something to push to the whole fleet. Entries below marked -*(beta channel)* therefore ship as code in this release but are only active -in a beta build. Everything else is live for every user: the Bluetooth -download speed-up, the RPM filter rework, the `tach_filter` and pickup-health -diagnostics, the tach page's corrected `*OVER REV*` header, the pace page's -`STOPPED` state, the four new Back/Cancel menu rows, and the transfer-mode -exit fixes. The new LED settings keys are written to `/SETTINGS.json` on -every build (so a card moved into a beta unit is already configured); only a -flag-on build reads them. +> ### Read this before updating +> +> **This release permanently converts the two NFC pads to GPIO, on every +> device, and reboots once while doing it.** +> +> The LED strip stops being a beta-only experiment in 4.1.0 and ships in the +> normal firmware, so a logger lights up the moment someone wires a strip to +> it — no special build. Making those two pads usable as GPIO means writing +> the chip's UICR, and **that write cannot be undone by any later firmware**; +> reversing it needs a full chip erase and a bootloader reflash over USB. +> Your logger does the write the first time it boots after updating, then +> resets itself once so the change takes effect. That single extra reboot +> during the update is expected — nothing is wrong. +> +> This happens whether or not you have LEDs attached, because the firmware +> cannot know. The trade was made deliberately: this hardware does not use +> NFC for anything, the pads are otherwise idle, and requiring a separate +> build to use a headline feature had kept it out of everyone's hands. If you +> have some future use for the NFC pads on your device, **do not install +> 4.1.0.** +> +> With no strip wired, nothing else changes — the pins simply sit there. + +The SensorEgg wireless-EGT proof of concept remains beta-only +(`BIRDSEYE_ENABLE_SENSOREGG`, off in the published images), but it gained +nothing this release — so every entry below is live for every user. ### Added -- **NeoPixel LED strip subsystem** (beta channel only, - `BIRDSEYE_ENABLE_NEOPIXEL`, plan 0006): 11 WS2812 pixels on the NFC +- **NeoPixel LED strip subsystem** (plan 0006) — **now in every build**, + see the upgrade note above: 11 WS2812 pixels on the NFC pads converted to GPIO — 2 status indicators + a 9-px strip with a centerline. A global brightness cap (`led_brightness` setting, 0 disables the LEDs entirely) that no LED can ever exceed; a pace pip @@ -52,8 +63,7 @@ flag-on build reads them. sleep truly powers the LEDs off. **First boot of a flag-on build performs a one-way NFC-pads-to-GPIO conversion (UICR write) and resets once.** -- **LED day/night brightness + a device timezone** (beta channel, plan - 0010): new `utc_offset_min` setting (minutes east of UTC, default 0) +- **LED day/night brightness + a device timezone** (plan 0010): new `utc_offset_min` setting (minutes east of UTC, default 0) gives the device a local wall clock, and the LED strip swaps to `led_brightness_night` (default 16) between `led_night_start_hour` (19) and `led_day_start_hour` (7) — local hours, so 7am is the @@ -63,12 +73,12 @@ flag-on build reads them. every filename stay exactly as they were, and timezone presentation remains the viewing app's job. - **Overrev alert** (plan 0007): new `overrev_limit` setting (default 0 - = disabled) — past it the whole LED chain flashes red *(beta channel)* - and the tach page's `*OVER REV*` header trips *(every build)*, both - until RPM falls back below the normal `rev_limit`. The rev limit warns - the engine is at its ceiling; the overrev limit says it's broken. -- **Temp1 alert threshold setting** *(beta channel)* (`temp1_alert_c`, - default 650 °C): the right status LED is now a tri-state — flashing red + = disabled) — past it the whole LED chain flashes red and the tach + page's `*OVER REV*` header trips, both until RPM falls back below the + normal `rev_limit`. The rev limit warns the engine is at its ceiling; + the overrev limit says it's broken. +- **Temp1 alert threshold setting** (`temp1_alert_c`, default 650 °C): + the right status LED is now a tri-state — flashing red at/above the limit, off when good, solid blue when there is no probe signal. - **`tach_filter` setting** (default `smooth`, plan 0009): picks the RPM @@ -84,8 +94,7 @@ flag-on build reads them. thrown away this power-cycle. A count that climbs with RPM is ignition ringing or missed sparks reaching the ISR, i.e. the pickup rather than the filter. -- **GPS-search pip** *(beta channel)*: in race mode without a full GPS - lock the strip shows a green pixel bouncing end-to-end instead of the +- **GPS-search pip**: in race mode without a full GPS lock the strip shows a green pixel bouncing end-to-end instead of the RPM scale, so a not-yet-timing session is visibly "searching". - **The browser-sim harness can fake a GPS fix.** A "GPS fix" toggle (plus an mph field) streams a deterministic synthetic 25 Hz fix parked on the @@ -130,14 +139,13 @@ flag-on build reads them. is ~90 ms more lag on a 5500 RPM/s pull. Set `tach_filter` to `legacy` to get the old behaviour back. - **Engine dies mid-session** (tach-proven sessions): the pace page shows - `STOPPED` instead of a still-counting pace *(every build)*, and the LED - bar goes dark while the status LEDs (temp alert) stay live - *(beta channel)*. Clears on restart. + `STOPPED` instead of a still-counting pace, and the LED bar goes dark + while the status LEDs (temp alert) stay live. Clears on restart. - **Tach page `*OVER REV*` header** now means actual overrev: it shows only when `overrev_limit` is enabled and RPM reaches it (was a hardcoded 9999 RPM). The `rev_limit` warning stays on the LED only. -- **Temp status LED flashes red** *(beta channel)* (was orange), matching - the rev flasher's alert language. +- **Temp status LED flashes red** (was orange), matching the rev + flasher's alert language. - **Bluetooth downloads are faster, and now say why when they are not** (plan 0008). A 3.3 MB session downloading at 28.8 KB/s on an iPad prompted a look at the whole transfer path. Three things were capping diff --git a/CLAUDE.md b/CLAUDE.md index b070ef7..4588b86 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,7 +67,7 @@ Core capabilities: DovesSensorEgg thermocouple pod's advertising broadcasts (`PW-ADV` v1 and v2), logs `Temp1`/`Junction1`/`Temp2` DOVEX columns + Temp1/Temp2 race pages (subsystem 14) -- **NeoPixel strip (beta)**: 11 WS2812 pixels on the NFC pads converted +- **NeoPixel strip**: 11 WS2812 pixels on the NFC pads converted to GPIO — 2 status alert LEDs + a 9-px pace-pip / RPM-scale strip with a global brightness cap, boot animation, and a purple session-best sector celebration (subsystem 16) @@ -176,7 +176,7 @@ handoff spec. | Path | Contents | |---|---| -| `.github/workflows/` | CI: compile-sketch (+ flash-size gate), arduino-lint, unit-tests, clang-tidy, coverage, sim-build (native sim TU + 60 s boot soak + determinism + goldens + lap oracles + two-session carryover, plus a wasm job: emsdk 3.1.61 build + node smoke + `birdseye-sim-wasm` artifact), release (dual-board build + GitHub Release + prod OTA manifest to `gh-pages`), beta (dual-board build on `BETA`-branch push → latest-only `beta/` OTA channel on `gh-pages`, no Release). Per-channel build config: `BETA` builds track DovesLapTimer's `BETA` branch and pass `-DBIRDSEYE_ENABLE_SENSOREGG=1`; master/release pin `v4.3.0` and build the all-flags-off defaults | +| `.github/workflows/` | CI: compile-sketch (+ flash-size gate), arduino-lint, unit-tests, clang-tidy, coverage, sim-build (native sim TU + 60 s boot soak + determinism + goldens + lap oracles + two-session carryover, plus a wasm job: emsdk 3.1.61 build + node smoke + `birdseye-sim-wasm` artifact), release (dual-board build + GitHub Release + prod OTA manifest to `gh-pages`), beta (dual-board build on `BETA`-branch push → latest-only `beta/` OTA channel on `gh-pages`, no Release). Per-channel build config: `BETA` builds track DovesLapTimer's `BETA` branch and pass `-DBIRDSEYE_ENABLE_SENSOREGG=1`; master/release pin `v4.3.0` and build the `project.h` defaults — which since 4.1.0 means NeoPixel ON, SensorEgg off | | `tests/` | Host doctest harness (CMake) for the pure-logic units | | `docs/plans/` | Numbered design records (`NNNN-slug.md`, see its README) — the rationale behind each chunk of work; plan-executing commits cite the number. Same convention as DovesDataViewer | | `CHANGELOG.md` | Keep-a-Changelog history; release workflow ties to version tags | @@ -1312,12 +1312,17 @@ hardware needs no power switch. Wake = chip reset = fresh `setup()`. ### 16. NeoPixel Strip (`neopixel.{h,ino}`, `led_frame/led_modes/led_animations/sector_purple.{h,cpp}`) -- **BUILD FLAG — `BIRDSEYE_ENABLE_NEOPIXEL` (`project.h`)**: `0` - (master/release default) compiles the whole subsystem down to no-op - `NEOPIXEL_*` entry points — no UICR write, no pin driving, no - Adafruit NeoPixel dependency in the image. `1` (beta channel, passed - by `beta.yml` + BETA-targeted `compile-sketch.yml` runs) is - everything below. +- **BUILD FLAG — `BIRDSEYE_ENABLE_NEOPIXEL` (`project.h`)**: `1` on + **every** channel as of 4.1.0 — master, beta and release — so the strip + is a core feature rather than a special build. Everything below is in + every image. The cost is charged fleet-wide and cannot be taken back: + the first boot after updating performs the ONE-WAY UICR NFC→GPIO + conversion and self-resets once, on every device, LEDs wired or not. + `0` (no shipped channel sets it) compiles the subsystem out — no + Adafruit NeoPixel dependency, and no UICR write on a board that has not + already been converted; on one that HAS, the `#else` stubs still hold + boost EN low, because a floating EN leaves the 5 V rail up through + System OFF. - **Hardware**: 11 WS2812 pixels fed by an Adafruit 5 V boost converter. Pixels 0 and 10 are status indicators; pixels 1–9 are the strip with pixel 5 the centerline. Pin 30 (P0.09/NFC1) drives the boost EN @@ -1699,7 +1704,7 @@ the one loaded). Sector lines stay optional — zero, one, or two. | SensorEgg scanner self-heal | 30 s no packet → stop+start kick | `sensoregg_protocol.h` | | SensorEgg RSSI floor | −90 dBm | `sensoregg_protocol.h` | | SensorEgg pairing MAC | `SENSOREGG_MAC` (all-zeros = any egg) | `sensoregg.h` | -| NeoPixel strip flag | `BIRDSEYE_ENABLE_NEOPIXEL`, default 0; 1 on the beta channel | `project.h` | +| NeoPixel strip flag | `BIRDSEYE_ENABLE_NEOPIXEL`, default **1** on every channel since 4.1.0 | `project.h` | | NeoPixel pins | 30 = boost EN, 31 = data (NFC pads, post-UICR) | `neopixel.h` | | NeoPixel layout | 11 px: status 0 + strip 1–9 (center px 5) + status 10 | `led_frame.h` | | LED frame rate | 30 Hz (`NPX_FRAME_INTERVAL_MS` 33) | `neopixel.ino` | @@ -1781,8 +1786,9 @@ This device operates in ignition-noise environments. Three layers of defense: `compiler.cpp.extra_flags` property — a second `--build-property` for one key replaces the first). Local setup: CONTRIBUTING.md "Local build flags". -- **Feature flags** (`project.h`, all default `0`, all tested with `#if` - so an explicit `-DFLAG=0` wins): +- **Feature flags** (`project.h`, tested with `#if` so an explicit + `-DFLAG=0` wins; `BIRDSEYE_ENABLE_NEOPIXEL` defaults to `1`, the rest + to `0`): - `BIRDSEYE_ENABLE_ONBOARD_CHARGING` — off in **every** channel. See subsystem 10: HICHG hold + the USB charging UX. The hardware now has an external charging circuit. @@ -1790,10 +1796,12 @@ This device operates in ignition-noise environments. Three layers of defense: (`beta.yml`, plus `compile-sketch.yml` for PRs targeting `BETA` so the flag-on build is compile-checked before it reaches the publish workflow). See subsystem 14. - - `BIRDSEYE_ENABLE_NEOPIXEL` — off in master/release, **on in beta** - (same two workflows as SENSOREGG). First flag-on boot performs the - ONE-WAY UICR NFC→GPIO conversion and self-resets once. See - subsystem 16. + - `BIRDSEYE_ENABLE_NEOPIXEL` — **on everywhere since 4.1.0** (the + `project.h` default; no workflow needs to pass it). The first boot of + any 4.1.0+ image performs the ONE-WAY UICR NFC→GPIO conversion and + self-resets once, on every device. This is the one flag whose default + is 1 — see subsystem 16 and the upgrade note at the top of + CHANGELOG.md's 4.1.0 section. When adding a flag: give it a `#ifndef` default in `project.h`, decide its per-channel value in the workflows, and document it here + in CONTRIBUTING.md's flag table. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 374b285..2bacb00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,7 +80,7 @@ so an explicit `-DFLAG=0` still turns the feature off. |---|---|---|---| | `BIRDSEYE_ENABLE_ONBOARD_CHARGING` | `0` | nothing — off in every channel | Holds the BQ25100 HICHG pin high for ~100 mA fast charge and runs the USB charging UX (VBUS wake shortcuts to the charge screen; the main menu drops into the charging loop after `USB_MENU_CHARGE_IDLE_MS`). Off, the firmware leaves HICHG alone and an external charging circuit owns the battery. The VBUS park at shutdown happens either way — see ARCHITECTURE.md. | | `BIRDSEYE_ENABLE_SENSOREGG` | `0` | `beta.yml`, and `compile-sketch.yml` for PRs targeting `BETA` | Compiles in the wireless-EGT POC: passive BLE scanner, Temp1 race page, and BLE core up at boot. Off, the accessors return NaN, so `Temp1`/`Junction1` still log as `nan` and the log format is unchanged. | -| `BIRDSEYE_ENABLE_NEOPIXEL` | `0` | `beta.yml`, and `compile-sketch.yml` for PRs targeting `BETA` | Compiles in the NeoPixel strip subsystem (plan 0006): on first boot it programs `UICR->NFCPINS` to convert the NFC pads to GPIO (**one-way** — undoing it needs a full chip erase) and self-resets once, then drives pin 30 as the 5 V boost EN and pin 31 as WS2812 data. Off, the firmware never touches UICR or those pins. Needs the `Adafruit NeoPixel` library. | +| `BIRDSEYE_ENABLE_NEOPIXEL` | **`1`** | every channel (the `project.h` default — no workflow passes it) | Compiles in the NeoPixel strip subsystem (plan 0006): on first boot it programs `UICR->NFCPINS` to convert the NFC pads to GPIO (**one-way** — undoing it needs a full chip erase) and self-resets once, then drives pin 30 as the 5 V boost EN and pin 31 as WS2812 data. **The only flag defaulting to 1**, since 4.1.0 — that conversion is therefore charged to every device in the field, LEDs wired or not. Forcing `=0` compiles it out; on a board already converted the stubs still hold boost EN low. Needs the `Adafruit NeoPixel` library. | ### arduino-cli The exact invocation CI uses is in