release 3.1.0: raise the OTA image cap 320 KiB → 408 KiB - #124
Merged
Conversation
Ports the flash-split rebalance from the beta channel (PR #122, merged and flashed clean on beta hardware) and cuts it as 3.1.0. The app and the OTA staging region share one 820 KiB stretch ([0x27000, 0xF4000) = 839,680 B) and BOTH must be able to hold the image: the incoming one is staged up top, then copied down over the app. So the largest installable image is half the span. The split was lopsided -- 320 KiB staging against 500 KiB of app region -- which capped OTA at 320 KiB while leaving ~180 KiB of app region no legal image could reach. Staging base moves 0xA4000 -> 0x8E000 for an even, page-aligned split: staging [0x8E000, 0xF4000) = 417,792 B = 408 KiB (FW_MAX_IMAGE_SIZE) app [0x27000, 0x8E000) = 421,888 B = 412 KiB (>= the cap) Two constants, plus static_asserts for page alignment and app-region fit. No change to the apply sequence, the FW* protocol, the CRC, or the web client (which never enforced a cap of its own -- it relies on FWERR:SIZE). MINOR rather than PATCH: the device now accepts OTA images it previously rejected with FWERR:SIZE, which is new backwards-compatible behavior. Upgrading to 3.1.0 is safe from any earlier build -- staging is chosen at apply time from the INSTALLED firmware's constants, so a 3.0.x device stages at the old 0xA4000 and installs this normally, and the new app region ends below that old base so the two can never collide. Devices still on 3.0.x keep their own 320 KiB limit until they take this release; CI now warns when a build crosses that line so a fleet split shows up before release instead of in the field. Scope note: only the OTA gate block is taken from the beta workflow. The channel-selection rewrite and -DDOVES_DISABLE_DEBUG stay behind -- the latter is a DovesLapTimer BETA-branch feature and master pins v4.2.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HnTP6BdA9xjLR5hSWE9frb
Coverage — host-testable units📂 Overall coverage
📄 File coverage
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports the flash-split rebalance from beta (#122 — merged and flashed clean on hardware) and cuts it as 3.1.0.
The change
App and staging share one 820 KiB stretch and both must hold the image, so the largest installable image is half the span. The old split was 320 KiB staging against 500 KiB app — capping OTA at 320 KiB while ~180 KiB of app region sat where no legal image could reach it. Two constants, plus
static_asserts for page alignment and app-region fit. Nothing about the apply sequence, theFW*protocol, or the CRC moves.Why MINOR and not PATCH
The device now accepts OTA images it would previously have rejected with
FWERR:SIZE— new device behavior, backwards compatible. Per the semver policy in the changelog header that's MINOR, so3.0.2→3.1.0.Upgrade safety
Safe from any earlier build. Staging is chosen at apply time from the installed firmware's constants, so a 3.0.x device stages at the old
0xA4000and installs 3.1.0 normally. The new app region also ends below that old staging base, so a new-layout image can never collide with an old device's staging area.Devices still on 3.0.x keep their own 320 KiB limit until they take this release. A future build over 320 KiB can't be sent to one over the air — it would answer
FWERR:SIZE— and would need USBFWDFU→ UF2 instead. CI now warns when a build crosses that line so a fleet split shows up before release rather than in the field.Scope note
Only the OTA gate block is taken from beta's
compile-sketch.yml. Deliberately left behind:head_ref == 'BETA') — only affects BETA-targeted PRs, master builds identically either way-DDOVES_DISABLE_DEBUG— a DovesLapTimer BETA-branch feature; master pinsv4.2.0, which doesn't have it, so the flag would be inertBoth come across naturally when BETA merges to master. Also note
docs/plans/doesn't exist on master yet, so the "plan 0004" reference in the gate's error message resolves against the BETA branch for now — kept verbatim rather than reworded so the two workflow files don't diverge further.Changes
BirdsEye/firmware_ota.ino— the two constants, rewritten memory-map comment, twostatic_asserts.github/workflows/compile-sketch.yml—OTA_IMAGE_MAX_BYTES327680 → 417792, new non-fatalOTA_LEGACY_MAX_BYTESfleet-split warningBirdsEye/project.h—FIRMWARE_VERSION3.0.2→3.1.0CHANGELOG.md—[3.1.0]release section (the crossing-pattern entry from perf: generate the crossing animation instead of storing 2 KB of bitmaps (backport of #119) #123 rolls into it) + compare linksCLAUDE.md— constants tableTest plan
static_asserts negative-tested — a 412 KiB cap trips the app-region-fit assert, 407 KiB trips the page-alignment onefirmware_ota.inodelta vs master confirmed to be only the rebalance (nothing else rode along from beta)v3.1.0to triggerrelease.yml(publishes per-board.hex/.uf2/.zip+ the prod OTA manifest)Generated by Claude Code