Skip to content

fix(#290, #291): a tap wakes a sleeping TFT; GPS parser survives a short RMC sentence - #293

Merged
Strycher merged 6 commits into
mainfrom
fc/290-touch-wake
Sep 8, 2026
Merged

Strycher merged 6 commits into
mainfrom
fc/290-touch-wake

Conversation

@Strycher

@Strycher Strycher commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Closes #290, closes #291.

Two firmware defects found on the UM FeatherS3D bench on 2026-09-08, both pre-existing on main and independent of the board, plus the display module's official pinout and the FeatherS3D power-path facts in the docs.

#290 — a tap wakes a sleeping TFT

loop() parks lv_timer_handler() while the TFT sleeps, and LVGL's indev callback was the only reader of the FT6336U, so once asleep only the three wing buttons could wake the panel. Hidden until now because TFT sleep defaults to "always on"; the owner enabled it.

  • touch.cpp/touch.h: touchPollForWake() — the CTP_INT falling-edge ISR sets a flag (no pin read, nothing outside IRAM), consumed on the next loop pass; a 100 ms ctp.touched() poll is the fallback for a bench without CTP_INT wired. The waking tap is swallowed until the finger lifts (touchWakeSwallow, called from the LVGL read callback), capped at 2 s so a chip stuck reporting "touched" cannot disable input.
  • src.ino: while tftSleeping, touchPollForWake() → wakeAllDisplays(), the same path the buttons use. Logs [SLEEP] Woken by touch.

Hardware evidence (um_feather): cf3f8fd (poll only) woke after 90 s, ~4 min and an overnight idle, but needed several taps; ea4a99b/e3818d3 (edge-driven) woke on the running image, logged. The post-review edge-mode change (FALLING instead of CHANGE+read) is built but not yet flashed.

#291 — GPS parser crash on a short RMC sentence

parseNMEA declared an uninitialised fields[], nmeaParse filled only nFields slots, and the RMC branch read fields[3..7] after a nFields >= 3 gate. A sentence cut short by a UART hiccup with status A read stale stack pointers and faulted at strlen(fields[7]): LoadProhibited, EXCVADDR 0, decoded with addr2line against a rebuild of the crashed commit (parseNMEA gps.cpp:138 ← readGPS ← loop). Three identical panics in one night, 1.5–3.5 h apart.

  • gps.cpp/gps.h: every unfilled slot points at an empty string; fields[] is now const char* so a write through it cannot compile.

Hardware evidence: e3818d3 has run since 12:07 EDT with no GPS panic (the old image failed within 1.5–3.5 h every time).

Docs (#290 branch)

  • docs/um-bench-wiring.md and docs/HARDWARE.md: the display module's official 14-pin order from the LCDWIKI MSP3526 manual (header J2 and the 14P 0.5 mm FPC P2 carry the same signals in the same order, verified from the schematic), CTP_RST → hole 14, the manual's 5 V recommendation, and the FeatherS3D power paths from its schematic (5V pin = VBUS via a diode, no boost on board).

Gemini adversarial review (gemini-2.5-pro, docs/llm-consultations/2026-09-08-touch-wake-gps-parse-*.log)

# Finding Action
1 digitalRead() inside the IRAM ISR flagged as non-IRAM The claim is wrong for core 3.3.8 (__digitalRead is ARDUINO_ISR_ATTR), but it has a log_w path, so the read is gone anyway: the interrupt is now FALLING and the ISR only sets the flag. Fixed.
2 Non-atomic check-then-clear of the flag can lose a second edge A lost second edge changes nothing: the first edge is already waking the panel. Justified, documented in code.
3 Swallow-until-release never clears if the chip reports touch forever 2 s cap on the swallow. Fixed.
4 (char*) cast off the const empty string is a latent write hazard fields[] is const char* end to end; no cast. Fixed.
5 talker[2] read without a length check isRMC is only true when strstr(talker, "RMC") found the substring, so talker has at least three characters on that path. Justified.

Builds

um_feathers3 and feather_s3 both green on the tip; the Adafruit env is unchanged in behaviour. pytest scripts/ untouched by this branch.

For Ben

  • Flash the tip before merge if you want the post-review edge path verified on hardware; the board currently runs e3818d3.
  • Merge is yours.

🤖 Generated with Claude Code

loop() parks lv_timer_handler() while the TFT sleeps, and the LVGL indev
callback was the only reader of the touch chip, so once asleep only the wing
buttons could wake the panel. While asleep the touch unit now polls the chip
every 100 ms (touchPollForWake) and loop() calls wakeAllDisplays() on a
touch, the same path the buttons use. The touch that woke the panel is
swallowed until the finger lifts so it does not also click what is under it.

Docs: CTP_RST goes to hole 14 with the panel's RESET (owner, 2026-09-08).
On the bench the 100 ms poll needed several taps: a short tap fell between
two reads. The FT6336U pulls CTP_INT low for the whole touch, so the ISR
that #260 left empty now sets a flag on the falling edge and the sleep path
wakes on it; the poll stays as the fallback for a bench without CTP_INT
wired. LVGL's read path clears the flag while awake, so a touch made before
sleep cannot wake the panel the moment it sleeps.
… garbage

nmeaParse fills only nFields slots of parseNMEA's uninitialised fields[];
the RMC branch is entered at nFields >= 3 and reads fields[3..7] unchecked.
A sentence cut short by a UART hiccup with status 'A' therefore read stale
stack pointers and, once every few hours, faulted at strlen(fields[7])
(LoadProhibited, twice on um_feather 2026-09-08). Every unfilled slot now
points at an empty string, so a missing field reads as empty, which is
what the existing checks already mean by it.
… manual; FPC order; VCC note

Header J2 and FPC P2 carry the same 14 signals in the same order (manual
schematics 4.6/4.7). A 14P 0.5 mm FFC-to-DIP adapter is therefore a valid
latching-cable option. Manual recommends 5 V on VCC; the bench runs on 3V3.
…EA fields (#291)

From the Gemini gate on this branch: the wake ISR no longer reads the pin
(FALLING edge, flag only); the swallow-until-release of a waking tap is
capped at 2 s so a chip stuck reporting touch cannot disable input; the
NMEA field array is const char* so the empty-string padding needs no cast
and a write through it cannot compile. Two findings justified in the PR.
@Strycher

Strycher commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Agent: EmberDelta (session f0e32f9f)

@Strycher
Strycher merged commit 2ab9280 into main Sep 8, 2026
5 checks passed
@Strycher
Strycher deleted the fc/290-touch-wake branch September 8, 2026 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant