Skip to content

refactor(#265): E4 band 4 -- the screens leave src.ino; hardware verification before merge - #280

Merged
Strycher merged 4 commits into
mainfrom
fc/212-e4-screens
Sep 8, 2026
Merged

Strycher merged 4 commits into
mainfrom
fc/212-e4-screens

Conversation

@Strycher

@Strycher Strycher commented Sep 7, 2026

Copy link
Copy Markdown
Owner

E4 band 4, task #265, Epic #212. Opened autonomously under the owner's standing grant of 2026-09-07 — and deliberately not merged under it: #265's acceptance requires full hardware verification, every screen and every peripheral, before merge. Nothing in E4 has been flashed. This PR waits for that.

What moved — three proven commits on one branch

commit unit(s) functions
1 lvgl_port — the five LVGL callbacks, initFCTheme, the display/indev objects, the touch statistics 6
2 oled_screens — updateOLED and the five drawOLED* 6
3 screen_compass, screen_env, screen_telemetry, screen_geocache, screen_settings, navigation — every widget of every screen, the navigate functions, fcHeaderCreate, the list picker, updateSDIndicators, updateDisplay 58

Commit 3 is six units in one step because the screens and the navigation reference each other through headers; any one alone would have reached back into src.ino. initLVGL stays as the composition root. A fourth commit is the residue clean-up and the record.

#265's acceptance, measured

  • src/src.ino: 750 lines — setup, loop, initLVGL, initSD, scanI2C, handleButtons and its two helpers; 6 file-scope variables.
  • python scripts/reference-graph.py: 7 of 8 functions touch no shared state of the file, the eighth only its own three; cut set 0, largest cluster 1.
  • python scripts/symbol-inventory.py: 8 functions, 6 variables — the residue only.
  • Hardware verification: yours. Every screen and every peripheral, per E4-6 (band 4): move the consumers and screens last #265, then merge.

Calls made under the grant — please review

  1. Band 4 as one branch and one open PR rather than five merged children, because of the merge gate above.
  2. initLVGL stays in src.ino (it builds every screen; composition root); lvglTickCb lost static because it registers it.
  3. web.h gained using fs::FS; — TFT_eSPI defines FS_NO_GLOBALS, and src.ino's include order had hidden that WebServer.h depends on the alias.
  4. Six statics the graph could not see (write-only headers, const-folded counts, an inlined helper, the settings menu buttons) were moved by hand and are marked in their units.
  5. The LVGL log callback's LV_USE_LOG guard was restored by hand; the harness carries functions, not the preprocessor lines around them.

Proof

scripts/verify_extraction.py PASS on each commit; full figures in docs/god-object-inventory.md §15:

unit 1  592 -> 592 symbols, 27 relocated, 13 linkage changes declared, 5 deltas net +2      Flash -20
unit 2  592 -> 592, 6 relocated, 1 delta, 6 moved checked                                   Flash +52
unit 3  592 -> 593 (one compiler clone, named), 213 relocated, 12 linkage changes declared,
        6 deltas net +104, 61 moved functions checked by reference set                      Flash +88
Flash 1,856,690 -> 1,856,810; RAM 103,248 unchanged. Both toolchains build. 58 tests pass.

E4 in total

src/src.ino 8,742 → 750 lines into 25 translation units and 28 headers; Flash +848 bytes (0.05 %) with every byte traced; RAM +16. Every move proven behaviour-neutral by symbol set and, from band 3c, by every moved function's reference set. The two harness faults the dry runs caught this band are in §15.

src/lvgl_port.{h,cpp}: the five LVGL callbacks (tick, flush, log, touch read,
encoder read), initFCTheme and its seven named styles (private: only the
theme touches them), the display/indev/group objects and draw-buffer pointers
(published: initLVGL, which stays in src.ino as the composition root, wires
them), the touch statistics (published: the diagnostics page and loop read
them). lvglTickCb loses static because initLVGL registers it -- the harness
gained --publish-functions for exactly that. The button pins join
fc_config.h (the encoder callback reads them).

Verified by scripts/verify_extraction.py, PASS: 592 -> 592 symbols, 27
relocated, 13 linkage changes all declared, 5 size deltas net +2 all layout,
5 moved functions checked by reference set, unexplained none. Flash
1,856,690 -> 1,856,670 (-20), RAM unchanged. src.ino 4,387 -> 4,209.
src/oled_screens.{h,cpp}: updateOLED and the five drawOLED* functions. No
state of their own: they render the published domain state on the oled
object through eleven headers.

Harness: the existing-prototype matcher now requires type words at column 0
and nothing after the semicolon -- the dry run for the next unit showed it
matching `case SCREEN_COMPASS: updateCompassData(); ...; break;` inside
updateDisplay, which it would have deleted as a redundant prototype. Caught
before anything was written. Pointer-to-pointer variable types are accepted.

Verified by scripts/verify_extraction.py, PASS: 592 -> 592 symbols, 6
relocated, no linkage change, 1 size delta (-1), 6 moved functions checked
by reference set, unexplained none. .flash.text +60 (six calls now cross-TU:
literal pools and relaxation), .flash.rodata -8, RAM unchanged. Flash
1,856,670 -> 1,856,722. src.ino 4,209 -> 3,993.
…tion leave src.ino

Six units in one commit, because the screens and the navigation reference
each other through their headers (a screen registers screenGestureCb and
creates its header with fcHeaderCreate; navigation loads the screens): any
one of them alone would have reached back into src.ino.

  src/screen_compass.{h,cpp}    builder, rose draw callback, data update
  src/screen_env.{h,cpp}        builder, data update
  src/screen_telemetry.{h,cpp}  builder, row helpers, data update
  src/screen_geocache.{h,cpp}   builder, nav draw callback, data update, the
                                nine filter callbacks, the button handler
  src/screen_settings.{h,cpp}   builder, data update, 21 callbacks, the
                                settings scroll helper, logLvglHeap
  src/navigation.{h,cpp}        navigate*, the gesture and gear callbacks,
                                fcHeaderCreate, the list picker,
                                updateSDIndicators, updateDisplay, mainScreens

Each screen publishes its screen object (navigation's mainScreens table and
initLVGL load them) and its header (updateSDIndicators). Six statics the graph
could not see -- two write-only headers, two const-folded counts, an inlined
colour helper, the settings menu buttons -- moved by hand and are so marked.
web.h restores the fs::FS alias WebServer.h relies on: TFT_eSPI defines
FS_NO_GLOBALS, which src.ino's include order used to hide.

Harness: static functions get forward declarations at the top of their unit
and lose their file-scope prototypes in src.ino; the existing-prototype
matcher no longer matches a `case X: fn(); ...` line. Verifier: compiler
clones of library inlines compare as the original and are tolerated.

Verified by scripts/verify_extraction.py, PASS: 592 -> 593 symbols (a
constprop clone of lv_obj_set_style_pad_all in the smaller telemetry TU),
213 relocated, 12 linkage changes all declared, 6 size deltas net +104 all
attributed, 61 moved functions checked by reference set (27 references now
cross-TU, every other kept), unexplained none. .flash.text +184 (inputs +182:
.text +110, literals +72), .flash.rodata -96, RAM unchanged. Flash
1,856,722 -> 1,856,810. src.ino 3,993 -> 804.
… the record

src/src.ino: the stranded comments and empty banners left by twelve bands of
removals are gone; what remains is setup, loop, initLVGL, initSD, scanI2C
and the three button handlers, 750 lines. src/lvgl_port.cpp: the LV_USE_LOG
guard around lvglLogCb restored (src.ino compiled it conditionally; the
harness carries a function, not the preprocessor lines around it).

docs/god-object-inventory.md section 15: the band-4 record, the #265
acceptance measured on the residue (8 functions, 6 variables, cut set 0,
largest cluster 1), the two harness faults the dry runs caught, and the E4
totals: src.ino 8,742 -> 750 lines into 25 translation units and 28 headers,
Flash +848 bytes traced, RAM +16, every move proven behaviour-neutral.

Not flashed. The PR stays open for the hardware verification #265 requires.
@Strycher

Strycher commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Hardware verification evidence for band 4 arrived through PR #288, whose branch is this PR's head plus the UM FeatherS3D env and two fixes (#284 TFT_eSPI SPI port, #285 watchdog). On um_feather at 2026-09-08 02:16 EDT every unit that band 4 extracted was exercised from the boot log: lvgl_port, the five screens, navigation, oled_screens; TFT drawing, touch found and armed, OLED up, and the loop at 44 Hz with the owner confirming the TFT sleep timeout. Nothing in band 4 misbehaved; the two defects found were pre-existing PlatformIO-build defects that no image had met before, fixed in #288. Merge order: this PR first, then #288 retargets to main.

@Strycher
Strycher merged commit 25c8bb8 into main Sep 8, 2026
4 checks passed
Strycher added a commit that referenced this pull request Sep 8, 2026
A second [env:um_feathers3] for the UM FeatherS3[D] the bench was rewired
to on 2026-09-05 (#282). Shared settings move to an [env] base section;
each env carries board, memory, partitions, flash and its three TFT
control-line flags (UM: CS 17, DC 18, RST 14). src/fc_config.h selects the
other seven pins on the platform's board macro (UM: SD_CS 3, FRAM_CS 12,
TFT_BL 5, CTP_INT 6, BUTTON_A/B/C 1/38/33) and errors on an unknown board.
On the UM board setup() drives LDO2 (GPIO 39) HIGH before I2C, since it
feeds the vertical STEMMA QT connector. partitions/default_16MB.csv is a
byte-identical copy of the core's table.

Both envs build (feather_s3 1,856,814 B flash / 103,248 B RAM; um_feathers3
1,797,018 B / 74,032 B). The feather_s3 image keeps the same size and
defined-symbol set as the #280 build. pytest scripts/: 58 passed.
Strycher added a commit that referenced this pull request Sep 8, 2026
A second [env:um_feathers3] for the UM FeatherS3[D] the bench was rewired
to on 2026-09-05 (#282). Shared settings move to an [env] base section;
each env carries board, memory, partitions, flash and its three TFT
control-line flags (UM: CS 17, DC 18, RST 14). src/fc_config.h selects the
other seven pins on the platform's board macro (UM: SD_CS 3, FRAM_CS 12,
TFT_BL 5, CTP_INT 6, BUTTON_A/B/C 1/38/33) and errors on an unknown board.
On the UM board setup() drives LDO2 (GPIO 39) HIGH before I2C, since it
feeds the vertical STEMMA QT connector. partitions/default_16MB.csv is a
byte-identical copy of the core's table.

Both envs build (feather_s3 1,856,814 B flash / 103,248 B RAM; um_feathers3
1,797,018 B / 74,032 B). The feather_s3 image keeps the same size and
defined-symbol set as the #280 build. pytest scripts/: 58 passed.
@Strycher
Strycher deleted the fc/212-e4-screens branch September 8, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant