Add persistent OLED UI settings and recovery

This commit is contained in:
2026-08-29 20:18:08 +02:00
parent 16c0c02389
commit 276559536b
14 changed files with 538 additions and 38 deletions
+12
View File
@@ -10,6 +10,18 @@ Use these commands from the UART0 `serial-tool>` administration console. Run `he
| `reboot` | Drain console output briefly and restart the ESP32. |
| `status` | Show quick MAX3243 signal state. |
## Local display
| Command | Description |
|---|---|
| `display status` | Show the runtime aging settings and OLED service state. |
| `display set dim-seconds <0..86400>` | Set the RAM inactivity delay before contrast drops to `1`; `0` disables dimming. |
| `display set off-seconds <0..86400>` | Set the RAM inactivity delay before the OLED switches off; `0` disables automatic off. |
| `display save` / `display load` | Save the working aging settings to NVS or load them. |
| `display defaults` / `display reset` | Apply 300/600-second defaults in RAM, or apply and persist them. |
When both transitions are enabled, `off-seconds` must be greater than `dim-seconds`. Applying settings counts as local UI activity. A missing OLED remains nonfatal; after reconnecting it safely, one new button press requests a bounded reprobe and is consumed without navigating.
## Serial service
| Command | Description |
+27
View File
@@ -157,6 +157,33 @@ For Wi-Fi lifecycle calls, confirm the immediate result says `Requested`, then u
Run these checks with UART0 available. Repeat appropriate stop/revoke cases with USB CDC, WebSocket, and SSH clients connected; verify the intended session/service is interrupted, unrelated recovery paths remain responsive, and no action injects serial data.
### 10. Phase 7E persistence and fault recovery
First shorten the delays for a bounded aging-policy test:
```text
display status
display set dim-seconds 5
display set off-seconds 10
display save
reboot
```
After reboot, `display status` must report the stored 5/10-second values. Verify dim at five seconds, off at ten seconds, and one consumed wake press. Run `display load`, then test `display defaults` without saving and confirm 300/600 seconds are restored only in RAM. Run `display reset`, reboot, and confirm the defaults persisted. Verify invalid combinations such as dim `10` with off `5` are rejected without changing the working values. Set either timeout to `0`, save/reboot, and confirm that transition is disabled; finish with `display reset`.
Exercise the following fault matrix while keeping UART0 available:
| Fault/stress | Procedure | Required result |
|---|---|---|
| OLED absent at boot | Power down, remove the OLED, then boot. | UART0, serial, USB, Wi-Fi, HTTPS/WebSocket, and SSH startup remain independent; no reset or probe loop occurs. |
| Reattach | Power down before reconnecting loose wiring, boot, and press one button. If using a connector explicitly suitable for live removal, avoid shorts and reconnect before pressing. | One bounded reprobe initializes the panel; the wake press does not navigate or execute an action. |
| NACK/timeout | With safe test wiring, interrupt the display during refresh or hold one bus line low briefly, then release it. | One bounded frame fails and marks the OLED unavailable; no watchdog reset, repeated log flood, serial loss, or broker ownership change occurs. A later new press can recover after the bus is healthy. |
| Stuck button | Hold each button continuously for at least ten seconds, then operate each of the other buttons. | The held input is quarantined, the other controls remain usable, and no action repeats. Release and debounce the held input; it must rearm without rebooting. |
| Real chord | Press two healthy buttons together before either is quarantined. | The chord is ignored and cannot confirm an action. |
| Repeated actions | Re-enter Controls and repeat start/stop, reconnect, next-profile, writer-revoke, and display-off requests during transitions. | Each confirmation hold executes once; stale state produces an error rather than the opposite action; queue saturation, if reached, reports an error and leaves the Wi-Fi alert/counter visible. |
For the final concurrency regression, connect USB CDC, one WebSocket terminal, and one SSH terminal while UART1 carries sustained bidirectional traffic. Navigate the UI, run the persistence checks, exercise service stop/start and writer revocation, and inject the display/stuck-button faults above. Confirm UART0 remains responsive, the UI never appears as a broker client or injects serial bytes, writer ownership is always the expected client or none, and serial/broker drop counters do not increase unexpectedly. Record `memory`, transport counters, `wifi counters`, and `debug display status` before and after the run.
## Configuration A: data and handshake pairs
Connect the following pairs:
+11 -9
View File
@@ -37,7 +37,7 @@ These constraints apply across all phases:
| 5A | Authenticated HTTPS administration foundation | **Complete** |
| 5B | Offline xterm.js WebSocket serial terminal | **Complete** |
| 6 | Authenticated SSH serial transport | **Complete** |
| 7 | Local display and button interface | **In progress (7D)** |
| 7 | Local display and button interface | **In progress (7E validation)** |
| 8 | Security and production hardening | **Planned** |
| 9 | Authenticated, rollback-capable OTA | **Planned** |
| 10 | BLE serial transport and provisioning evaluation | **Planned** |
@@ -184,7 +184,7 @@ The order below is the current plan. Phase 7 is in progress; later phases remain
Add a standalone local status/control interface without making it a dependency of the serial core. The planning baseline uses a 128×64 dual-color monochrome I²C OLED sold with an SSD1315 controller. Phase 7A confirmed SSD1306-compatible operation, 7-bit I²C address `0x3c`, orientation, column mapping, contrast/inversion behavior, button inputs, and the physical color geometry on the selected hardware.
Phase 7A diagnostics and target-hardware electrical validation are complete. Phases 7B and 7C are complete; Phase 7 overall remains in progress (7D). Phase 7D is implemented and awaiting validation; Phase 7E is not complete.
Phase 7A diagnostics and target-hardware electrical validation are complete. Phases 7B, 7C, and 7D are complete. Phase 7E is implemented and awaiting target-hardware fault and persistence validation.
#### Hardware baseline
@@ -220,17 +220,19 @@ The persistent yellow strip uses fixed-position serial, Wi-Fi-strength, USB, Web
- To reduce OLED aging, inactivity dims contrast to `1` after five minutes and switches the panel off after ten minutes. The first debounced button press restores contrast `127` and wakes without navigating.
- A missing/unresponsive display is nonfatal; the task remains read-only and never acquires serial writer ownership.
- Target-hardware validation passed for the four pages, fixed active-only status icons, lowercase layout, diagnostics hold, dim/off/wake behavior, all available concurrent client interfaces, UART0 recovery, and serial traffic from 9600 through 230400 baud.
4. **Phase 7D — Local controls — Implemented; validation pending**
4. **Phase 7D — Local controls — Complete**
- A shallow local `Controls` menu directly invokes public serial-service, Wi-Fi, HTTPS, SSH, broker, display, and restart APIs; it never parses or injects CLI text.
- The menu provides serial start/stop, Wi-Fi start/stop/reconnect/next-profile, HTTPS start/stop, SSH start/stop, emergency current-writer revocation, display off, and reboot. `WiFi:next profile` queues a rotation to the enabled profile after the currently active one in priority order, wrapping safely; profile configuration and credentials remain unavailable to the UI. The menu deliberately excludes serial configuration editing, Wi-Fi profile/AP changes, host-key/certificate changes, I²C scans, and assignment of the writer to another client.
- A visible confirmation screen and one continuous two-second Select hold are required for service stops, Wi-Fi reconnect, Wi-Fi next-profile, writer revocation, and reboot. The confirmation times out after 30 seconds; previous/back cancels. A wake press is consumed and cannot navigate or confirm an action.
- Wi-Fi lifecycle results report a queued request rather than falsely claiming an asynchronous transition has completed. HTTPS/SSH start requests require a connected station or active AP. The current writer is only ever force-released to no writer; the UI is not a broker client and cannot acquire or assign a writer lease.
- Pending target-hardware validation: menu navigation/timeouts, each enabled action and error result, confirmation cancellation/chord rejection, wake-press suppression, and preservation of UART0 plus transport recovery.
5. **Phase 7E — Reliability, persistence, and documentation — Planned**
- Make the implemented contrast dim/blank timeouts configurable and persistent without making the display necessary for recovery.
- Validate display removal, I²C NACK/timeouts, stuck buttons, queue saturation, and repeated actions.
- Re-run concurrent USB CDC, WebSocket, and SSH traffic while the UI refreshes and confirm UART0 remains responsive.
- Update wiring, electrical-test, command-reference, and recovery documentation.
- Target-hardware validation passed for menu navigation and timeout behavior, controls and asynchronous results, two-second confirmations and cancellation, Wi-Fi next-profile progression/wrap, chord rejection, wake-press suppression, and preservation of UART0 plus transport recovery.
5. **Phase 7E — Reliability, persistence, and documentation — Implemented; validation pending**
- A versioned `local_ui` NVS configuration makes the dim and display-off inactivity delays independently configurable from 0 through 86400 seconds. Zero disables a transition; defaults remain 300/600 seconds. UART0 `display` commands provide status, RAM edits, save/load, defaults, and atomic reset behavior.
- A button held for ten seconds is quarantined until its debounced release, after which it rearms. Quarantined inputs no longer keep a chord latched or block the other controls. A confirmation hold can execute at most once.
- Dim/off wake and missing-display recovery now occur only on a new debounced press edge. A held or stuck input cannot continuously probe an absent OLED, flood logs, or indefinitely refresh the inactivity timer; the wake gesture remains consumed.
- Dirty framebuffer commits have a 500 ms scheduling budget in addition to each bounded 250 ms I²C transaction. A failed frame marks the panel unavailable, while serial, broker, network, USB, and UART0 services remain independent. Safely reconnecting the OLED and pressing a button requests one bounded reprobe.
- Wi-Fi manager queue drops now activate the persistent yellow alert slot and remain observable through Wi-Fi counters. Local action errors continue to be reported directly without blocking queue insertion.
- Pending target-hardware validation: configuration persistence/disable semantics, absent and reattached display behavior, I²C NACK/timeout handling, each stuck button and rearm path, repeated actions/queue-error indication, and concurrent USB CDC, WebSocket, SSH, serial, and UART0 operation during faults.
Completion requires electrical validation on the selected module, correct operation with all current transports, bounded display/button failure behavior, safe action confirmation, no unexplained serial or broker loss, and documented UART0 recovery.
+2 -2
View File
@@ -127,13 +127,13 @@ GPIO13 ───── select/confirm button ──── GND
GPIO14 ───── next button ────────────── GND
```
> **OLED voltage warning:** Power OLED `VCC` from `3V3`, not 5 V. Many OLED modules connect their SDA/SCL pull-up resistors directly to `VCC`; powering such a module from 5 V could expose the ESP32-S3 GPIOs to unsafe I²C levels.
> **OLED voltage warning:** Power OLED `VCC` from `3V3`, not 5 V. Many OLED modules connect their SDA/SCL pull-up resistors directly to `VCC`; powering such a module from 5 V could expose the ESP32-S3 GPIOs to unsafe I²C levels. Power down before attaching or removing loose OLED wiring. Only perform live-removal fault tests with a connector designed to avoid shorts and unintended pin sequencing.
Before applying power, verify whether the module already includes SDA and SCL pull-ups and where they terminate. Any module-mounted or external I²C pull-ups must go to 3.3 V. If pull-ups are absent, add suitable external pull-ups from SDA and SCL to `3V3`; if they are present, account for their parallel resistance before adding more. The buttons normally need no external pull-ups because firmware enables the ESP32 internal pull-ups.
## Electrical verification
See [Electrical tests](electrical_tests.md) for Phase 7A OLED/button bring-up, safe loopback wiring, polarity checks, UART flow-control verification, and session-broker loopback testing.
See [Electrical tests](electrical_tests.md) for Phase 7 OLED/button bring-up, persistent aging settings, optional-display and stuck-button fault recovery, safe loopback wiring, polarity checks, UART flow-control verification, and session-broker/concurrent-transport testing.
## Future hardware profiles