451 lines
32 KiB
Markdown
451 lines
32 KiB
Markdown
# Electrical tests
|
||
|
||
These procedures verify the Phase 7A OLED and buttons, MAX3243 breakout, UART1 data path, hardware flow control, and session broker. They are manual tests: the firmware never starts one automatically.
|
||
|
||
> **Safety:** With power removed, install only the wiring required by the selected test. DE-9 pins 3 (`TX`), 4 (`DTR`), and 7 (`RTS`) are driven outputs. Never connect one of these outputs to another driven output. Keep temporary Dupont wiring short and secure. Power the OLED only from 3.3 V because module-mounted I²C pull-ups may connect SDA and SCL to the OLED `VCC` rail.
|
||
|
||
## Phase 7 OLED and button bring-up
|
||
|
||
Use the exact OLED and button connections in [Hardware wiring](wiring.md). Display diagnostics initially operate I²C at 100 kHz and probe the standard 7-bit `0x3c` and `0x3d` addresses. The connected test module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78` and `0x79`. A missing or unresponsive display is nonfatal: diagnostics should report it without disrupting UART0 or the serial services.
|
||
|
||
### 1. Power-off wiring checks
|
||
|
||
Disconnect both USB connectors and every other power source before checking or changing wiring.
|
||
|
||
1. Confirm OLED `VCC` goes only to `3V3`, OLED `GND` goes to `GND`, SDA goes to GPIO11, and SCL goes to GPIO12.
|
||
2. Check for an unintended short between `3V3` and `GND`, and verify ground continuity between the OLED and ESP32 board.
|
||
3. Determine whether the OLED module has SDA/SCL pull-ups and verify that any module-mounted or external pull-ups terminate at 3.3 V, never 5 V. Add suitable external pull-ups to `3V3` only if the module does not provide them; account for parallel resistance if more than one set is fitted.
|
||
4. Confirm each button is wired between its input and `GND`: previous/back GPIO10, select/confirm GPIO13, and next GPIO14. With a meter, each button should be open when released and near zero ohms to `GND` when pressed.
|
||
5. Check that no button shorts two GPIOs together and that SDA and SCL are not swapped or shorted.
|
||
|
||
### 2. Powered idle checks and address probe
|
||
|
||
Apply power, but do not initialize the OLED yet.
|
||
|
||
1. Measure OLED `VCC` relative to `GND`; it should be approximately 3.3 V.
|
||
2. Measure idle SDA on GPIO11 and idle SCL on GPIO12. Both should be near 3.3 V. Power down immediately if either bus line rises toward 5 V; correct the OLED supply or pull-up wiring before continuing.
|
||
3. Run `debug display status` and record the diagnostic state.
|
||
4. Run `debug display probe`. Confirm that it tests only 7-bit `0x3c` and `0x3d` at the initial 100 kHz bus rate. The tested module should acknowledge at `0x3c` (8-bit `0x78` write / `0x79` read).
|
||
|
||
If the expected address does not respond, treat the result as a nonfatal hardware finding. On this dedicated local-UI bus, `debug display scan --force` may identify an unexpected address before further investigation. Otherwise leave the serial core running, power down, and recheck 3.3 V power, common ground, SDA/SCL order, solder joints, and pull-ups. Do not scan a bus shared with unrelated I²C devices.
|
||
|
||
### 3. Initialization and display patterns
|
||
|
||
Initialize the address observed during the scan. The tested module uses 7-bit `0x3c`, equivalently 8-bit `0x78` (write) and `0x79` (read):
|
||
|
||
```text
|
||
debug display init 0x3c
|
||
```
|
||
|
||
Then run:
|
||
|
||
```text
|
||
debug display pattern clear
|
||
debug display pattern fill
|
||
debug display pattern checker
|
||
debug display pattern grid
|
||
debug display pattern corners
|
||
```
|
||
|
||
Confirm that clear and fill affect the full 128×64 area, checker and grid have regular spacing without shifted or wrapped columns, and all four corner markers are visible in the correct locations. Display diagnostics pause the periodic status UI for 30 seconds so the selected pattern remains observable. Record any unexpected mirroring, rotation, clipping, or column offset for correction.
|
||
|
||
### 4. Rendered status/content layout
|
||
|
||
Run the Phase 7B panel-layout pattern:
|
||
|
||
```text
|
||
debug display pattern layout
|
||
```
|
||
|
||
Confirm that the status text is entirely in the yellow 128×16 panel (rows 0–15), content text is entirely in the blue 128×48 panel (rows 16–63), and the physical black divider between them remains visible. Record any text crossing the divider, clipping, incorrect panel color, or divider obscuration.
|
||
|
||
### 5. Row 15/16 color-boundary test
|
||
|
||
Clear the display, illuminate row 15, and record its physical color and position:
|
||
|
||
```text
|
||
debug display pattern clear
|
||
debug display row 15
|
||
```
|
||
|
||
Repeat for row 16:
|
||
|
||
```text
|
||
debug display pattern clear
|
||
debug display row 16
|
||
```
|
||
|
||
**Verified result:** row 15 is the last yellow addressable row and row 16 is the first blue addressable row. The two colored areas are separated by a narrow physical black divider, so later UI rendering must treat the 128×16 yellow and 128×48 blue regions as separate panels rather than one visually continuous canvas. Also test another endpoint row if needed with `debug display row <0..63>` to confirm row addressing and orientation.
|
||
|
||
### 6. Contrast, inversion, and display-off checks
|
||
|
||
With a visible pattern loaded, exercise the bounded contrast range and confirm that brightness changes without bus errors. On the validated module, the useful visible brightness range begins at `1` and extends through `255`:
|
||
|
||
```text
|
||
debug display contrast 0
|
||
debug display contrast 64
|
||
debug display contrast 128
|
||
debug display contrast 255
|
||
```
|
||
|
||
Then verify inversion toggles all displayed pixels and can be restored:
|
||
|
||
```text
|
||
debug display invert on
|
||
debug display invert off
|
||
```
|
||
|
||
Finally run `debug display off` and confirm the panel turns off cleanly. Use `debug display status`, then `debug display init 0x3c` (or equivalently `0x78` or `0x79`) before further display tests.
|
||
|
||
### 7. Button checks
|
||
|
||
With all buttons released, run `debug buttons status`. Confirm previous/back GPIO10, select/confirm GPIO13, and next GPIO14 report released/high due to their internal pull-ups; each should report pressed/low while held to `GND`.
|
||
|
||
Run `debug buttons test` for the default 10-second interval. During the test, press and release each button separately with a deliberate short press, then repeat with a sustained long press. Confirm that the correct button and short/long classification are reported exactly once per intended action.
|
||
|
||
Repeat with an explicit duration, for example:
|
||
|
||
```text
|
||
debug buttons test 30
|
||
```
|
||
|
||
Use the longer run to check:
|
||
|
||
- **Debounce:** press with normal switch bounce and make several deliberately quick taps; one physical press must not produce a burst of duplicate press/release or short/long events.
|
||
- **Long press:** hold each button long enough for the diagnostic to classify it as long, then release it; it must not also create an unintended short-press action.
|
||
- **Stuck button:** hold one button before starting the test and keep it held. The input must remain identified as pressed/stuck without blocking checks of the other buttons, and the bounded diagnostic must still exit after the selected duration.
|
||
- **Recovery:** release the held button and confirm `debug buttons status` returns to released/high without a reboot.
|
||
|
||
`debug buttons test [seconds]` accepts 1 through 30 seconds and defaults to 10 seconds when omitted. Record unexpected event duplication, missed transitions, incorrect GPIO mapping, false long presses, or a test that fails to terminate.
|
||
|
||
### 8. Read-only status UI (Phase 7C)
|
||
|
||
After boot, the OLED starts on the **OVERVIEW** page. A short previous/back press on GPIO10 and a short next press on GPIO14 must wrap through these read-only pages:
|
||
|
||
1. **OVERVIEW** — serial, broker, USB, HTTPS/WebSocket, SSH, and Wi-Fi summary.
|
||
2. **RS232 MODEM** — framing, modem inputs, byte counters, queue depth, drops, and faults.
|
||
3. **BROKER** — connected clients, current writer marker, pending output, and drop/event counters.
|
||
4. **NETWORK SERVICES** — Wi-Fi state/RSSI/IP/AP state and HTTPS, WebSocket, and SSH service state.
|
||
|
||
Confirm that each page remains entirely within the blue content panel. The yellow panel must retain the same fixed icon positions while pages change: serial, segmented Wi-Fi strength, USB, WebSocket, SSH, broker clients, and alert. Active icons are solid with a value below; inactive icon slots remain completely blank and omit their value. Confirm that page text uses readable upper/lowercase, separates items and values with colons, and displays the active station SSID or AP SSID with its channel on the Overview and Network pages.
|
||
|
||
The UI refreshes at 4 Hz maximum and is read-only: select (GPIO13) must not change a service, acquire/release a writer, inject serial data, or expose passwords, credentials, tickets, or key material. With UART0, USB CDC, WebSocket, and SSH active, navigate pages and confirm that the display continues to update without disrupting serial traffic or UART0 recovery.
|
||
|
||
For the OLED-aging policy, leave all three buttons untouched and verify:
|
||
|
||
1. At five minutes, display contrast drops to `1` while the contents remain present.
|
||
2. At ten minutes, the OLED switches off.
|
||
3. The first debounced press of any button wakes the OLED at contrast `127` without changing the current page.
|
||
4. A subsequent previous/next press navigates normally and restarts the inactivity timers.
|
||
|
||
`debug display status` may verify an individual transition in a separate timing run. Every display diagnostic counts as activity and holds normal UI rendering for 30 seconds, so do not invoke it between the five- and ten-minute observations of one continuous run. Record a missing, stale, clipped, or implausible status value or incorrect dim/off/wake transition before Phase 7C is marked complete.
|
||
|
||
### 9. Local controls (Phase 7D)
|
||
|
||
From a status page, use a short Select press to open **Controls**. Previous/next selects an item; Select activates it. The menu includes serial start/stop, Wi-Fi start/stop/reconnect/next-profile, HTTPS start/stop, SSH start/stop, writer revocation, display off, and reboot. `WiFi:next profile` requests the enabled station profile after the active one in priority order and wraps safely; it must never show or change credentials, Wi-Fi profile configuration, serial framing, TLS/SSH keys, or I²C scan controls.
|
||
|
||
Stopping a service, Wi-Fi reconnect, Wi-Fi next-profile, writer revocation, and reboot open a confirmation page. Verify that:
|
||
|
||
1. Previous/back cancels the confirmation without changing the selected service or writer.
|
||
2. A short Select press does not execute the action.
|
||
3. Only one continuous two-second Select hold executes the action once; releasing it does not repeat the action.
|
||
4. Any simultaneous button chord is ignored and cannot confirm an action.
|
||
5. An untouched confirmation expires to the menu after 30 seconds.
|
||
6. A dimmed/off OLED consumes the first button press for wake only; its later release and hold must not navigate or confirm an action.
|
||
|
||
For Wi-Fi lifecycle calls, confirm the immediate result says `Requested`, then use the status pages to observe the asynchronous state change. For `WiFi:next profile`, configure at least two enabled station profiles, cancel once with Previous/back, then hold Select for two seconds; verify the current station disconnects, the next enabled profile is attempted, and the Overview/Network `WiFi:<SSID> Ch:<channel>` value (or `wifi status`) changes. Repeat until the selection wraps to the first profile. HTTPS and SSH starts must fail cleanly when neither station nor AP networking is available. Writer revocation must only release the current writer—never assign a replacement—and the UI must never appear as a broker client. For reboot, observe `Restarting...`, then verify all normal boot services and UART0 recovery return.
|
||
|
||
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 boot animation, persistence, and fault recovery
|
||
|
||
On each normal boot with an initialized OLED, first verify the bounded five-second identity animation: the full `ESP32 SERIAL SWISS ARMY KNIFE` name scrolls across the yellow panel; the blue panel shows the compact right-oriented monochrome logo with an upright `>_` prompt and serial/USB cable ends entering from the left. Wi-Fi activity and the cursor blink at the 4 Hz animation cadence. A missing OLED must skip the animation without preventing normal UART0 recovery.
|
||
|
||
Then 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.
|
||
|
||
## Phase 8A role-based user database and UART0 administration
|
||
|
||
Keep UART0 physically attached throughout these tests. Phase 8A deliberately does not change HTTPS or SSH authentication: existing network sessions still use the legacy shared credential, while the new database and public keys are prepared for the Phase 8B cutover. Do not include generated or entered passwords in test logs.
|
||
|
||
### 1. First-boot migration and persistence
|
||
|
||
Upgrade a device that already has working HTTPS/SSH credentials and no `user_db` blob. After boot:
|
||
|
||
1. Run `user status` and `user show admin`.
|
||
2. Confirm exactly one `admin`-named account exists with role `user`, `bootstrapped=no`, no displayed password/salt/verifier, and no public keys.
|
||
3. Confirm the pre-upgrade credential still authenticates to HTTPS and SSH and that serial terminal behavior is unchanged.
|
||
4. Reboot and confirm `user status` reports the same account ID and authentication generation.
|
||
5. Confirm routine boot logs, `status`, `web status`, and `ssh status` contain no user password, verifier, salt, or raw public-key blob.
|
||
|
||
A transient failure reading the legacy web credential must not create or commit an empty user database. If a valid `user_db` blob already exists, it must still load without the legacy credential being readable.
|
||
|
||
### 2. Administrator bootstrap
|
||
|
||
Run `user bootstrap`, verify that typed password characters are not echoed, enter a 12–64 character printable password twice, and confirm `user show admin` now reports role `admin` and a higher authentication generation. Reboot and confirm `bootstrapped=yes` and one administrator persist. On a separate resettable test image, exercise `user bootstrap --generate`; record the generated password securely for the test, verify it is displayed once, then clear the terminal scrollback after use.
|
||
|
||
During Phase 8A, verify the newly bootstrapped password does **not** replace the legacy HTTPS/SSH login yet and the existing network credential continues to work. This separation is intentional until Phase 8B. A second `user bootstrap` must be rejected without changing the account.
|
||
|
||
### 3. Account CRUD, capacity, and invariants
|
||
|
||
Use generated test passwords where practical:
|
||
|
||
```text
|
||
user add operator user --generate
|
||
user add maint admin --generate
|
||
user list
|
||
user show operator
|
||
user role operator admin --force
|
||
user role operator user --force
|
||
user password operator --generate
|
||
```
|
||
|
||
Confirm unique valid usernames are accepted; uppercase, empty, oversized, or punctuation-invalid usernames are rejected. Confirm duplicate users are rejected. Fill all eight slots and verify a ninth account is rejected without altering existing records; delete a non-final account and confirm its slot can be reused with a new account ID.
|
||
|
||
With two administrators, demote or delete one and confirm the operation persists. Then attempt both deletion and demotion of the final administrator and confirm both are rejected. Before bootstrap on a separate migration run, verify `user delete admin --force` is rejected so legacy synchronization remains recoverable. `Ctrl-C` during either hidden password prompt must cancel without changing the account or leaving stale input in the next console command.
|
||
|
||
### 4. SSH public-key import and fingerprints
|
||
|
||
Generate disposable Ed25519 and ECDSA P-256 OpenSSH keys on a trusted host. For each supported type, run `user key add <username>`, paste the single `.pub` line, and confirm `user show <username>` reports the type, stable slot index, and expected `SHA256:` fingerprint without displaying the Base64 blob or comment.
|
||
|
||
Verify all of the following fail without changing the account generation or key list:
|
||
|
||
- Unsupported RSA, certificate, or unknown key types.
|
||
- Truncated/invalid Base64, mismatched outer and embedded types, trailing blob data, malformed Ed25519 lengths, and an invalid/off-curve ECDSA point.
|
||
- Adding the same key again to the same account or assigning it to a different account.
|
||
- Adding a fourth key to an account that already has three.
|
||
- Deleting an empty/out-of-range slot.
|
||
|
||
Then exercise `user key delete <username> <index> --force` and `user key clear <username> --force`, reboot, and confirm the exact remaining fingerprints persist. Phase 8A stores these keys but does not yet accept SSH key login.
|
||
|
||
### 5. Legacy credential rotation boundary and reboot reconciliation
|
||
|
||
Before administrator bootstrap on a resettable test image:
|
||
|
||
1. Run `web credentials rotate --force`.
|
||
2. Confirm the console reports that the pre-bootstrap migrated verifier was synchronized.
|
||
3. Verify the new legacy credential works for HTTPS and SSH, reboot, and confirm `user show admin` retains the synchronized authentication generation.
|
||
4. Where NVS fault injection or controlled power interruption is available, interrupt after the web credential commits but before the user blob commits. On the next boot, confirm reconciliation updates the migrated verifier rather than committing an empty database or losing network recovery.
|
||
|
||
After bootstrap, rotate the legacy credential again. Confirm the console explicitly says it remains separate from user passwords, existing network sessions are revoked, the new legacy credential works, and the bootstrapped administrator generation/password are not silently replaced. Apply the same checks to `web reset --force`, additionally confirming HTTPS certificate recovery/restart behavior remains intact.
|
||
|
||
### 6. Explicit database recovery and storage failures
|
||
|
||
Using a disposable device or test NVS image, inject a wrong-size/version or malformed `user_db` blob and reboot. Confirm normal database initialization fails without erasing shared NVS, but the `user` command is still registered. `user status` must report the database unavailable. Run:
|
||
|
||
```text
|
||
user recover --force
|
||
user status
|
||
user bootstrap --generate
|
||
```
|
||
|
||
Confirm recovery explicitly replaces only `user_db`, imports the current legacy credential as role `user`, and leaves serial, Wi-Fi, HTTPS identity, SSH host key, and legacy network login intact. Confirm recovery is rejected while a valid database is initialized. Simulate NVS write/commit exhaustion or failure during create, password, role, key, and recovery operations; after reboot, each operation must expose either the complete old blob or complete new blob, never a partially live mutation.
|
||
|
||
Because NVS is not yet encrypted, this test validates logical verifier storage rather than resistance to physical extraction. PBKDF2 verifiers remain susceptible to offline guessing, and append-oriented NVS may retain historical plaintext legacy credentials until a full partition/flash erase.
|
||
|
||
### 7. Phase 8A regression
|
||
|
||
With the maximum supported mix of USB CDC, WebSocket, and SSH serial clients active, repeat user list/show and several UART0 mutations. Confirm password prompts remain physical-UART-only, no user operation acquires a broker client or writer lease, existing Phase 8A network authentication and revocation behavior remains functional, and serial/broker counters show no unexplained loss. Record `memory`, `web counters`, and `ssh counters` before and after the run.
|
||
|
||
## Phase 8B role-aware HTTPS and SSH authentication
|
||
|
||
Complete the Phase 8A bootstrap first and retain two disposable accounts, one `user` and one `admin`. Phase 8B gives both roles the existing serial/status interface; the administrative SSH shell is not expected until Phase 8C.
|
||
|
||
### 1. HTTPS passwords and fail-closed behavior
|
||
|
||
For both roles, authenticate to `/`, `/api/status`, and the browser terminal with the account password. Confirm the full 16-character username and a 64-character password work, while absent, malformed, oversized, wrong-user, and wrong-password Basic headers fail without exposing which component was wrong. After bootstrap, confirm the value from `web credentials show` no longer authenticates unless it independently equals a current user password.
|
||
|
||
On a disposable malformed-database image, confirm HTTPS returns authentication-service unavailable and never falls back to the legacy credential. Recover through physical UART0 only. Verify `web status` reports user-database authentication and active WebSocket sessions show account, user role, and password method without secrets.
|
||
|
||
### 2. SSH password and public-key login
|
||
|
||
For both `user` and `admin`, verify password SSH login reaches the same broker-backed serial stream as before. Confirm `ssh sessions` shows the account, user role, password method, and writer/observer role. An administrator must not receive UART0 commands yet.
|
||
|
||
Import one Ed25519 key and one ECDSA P-256 key through `user key add`, then verify each matching private key authenticates without a password and `ssh sessions` reports `public-key`. Confirm an unregistered key, wrong username, unsupported RSA key, certificate key, and three failed completed attempts are rejected. Normal client probe-then-sign behavior must consume one completed attempt, not two; unsigned probes alone must never create an authenticated session or broker client.
|
||
|
||
### 3. WebSocket ticket binding
|
||
|
||
Authenticate separately as two users and request tickets. Confirm each ticket is accepted exactly once, expires after 30 seconds, and cannot be reused or altered. Change one account after ticket issuance but before WebSocket connection; its ticket must fail closed without creating a broker client, while the other account's ticket still works. Repeat with delete followed by recreation of the same username and confirm the old ticket remains invalid.
|
||
|
||
### 4. Targeted session revocation
|
||
|
||
Connect simultaneous WebSocket and SSH sessions for two different users and place one affected session in writer state. One mutation at a time, exercise password change, role change, SSH-key add/delete/clear, and account deletion. Required results:
|
||
|
||
1. Outstanding tickets and active sessions for the changed username are revoked promptly and any writer lease is released.
|
||
2. The stale session cannot inject additional serial bytes or reacquire writer ownership.
|
||
3. The unrelated user's WebSocket/SSH sessions remain connected and retain their expected broker roles.
|
||
4. New authentication succeeds only with the updated password/key/role state.
|
||
5. Deleting and recreating the same username does not revive old tickets or sessions.
|
||
|
||
Run `web status`, `ssh sessions`, `web counters`, `ssh counters`, and `broker clients` around each mutation. Also rotate `web credentials rotate --force` after administrator bootstrap and confirm it changes only recovery material: role-based HTTPS/SSH sessions remain connected and user passwords are unchanged. `web reset --force` restarts HTTPS because the TLS identity changes, but must not revoke unrelated SSH sessions.
|
||
|
||
### 5. Concurrency regression
|
||
|
||
With USB CDC, two role-based network users, one WebSocket terminal, one SSH terminal, and UART1 traffic active, alternate writer ownership and mutate one account. Confirm binary transparency, observer isolation, bounded authentication/handshake behavior, UART0 responsiveness, and no unexpected disconnect of the unaffected user. Record memory, broker, web, SSH, and serial counters before and after. Repeat after reboot to verify passwords, roles, keys, and authentication methods persist.
|
||
|
||
## Configuration A: data and handshake pairs
|
||
|
||
Connect the following pairs:
|
||
|
||
| Driven output | Receiver input | Expected ESP32 logic |
|
||
|---|---|---|
|
||
| DE-9 pin 3, `TX` | DE-9 pin 2, `RX` | `RX == TX` |
|
||
| DE-9 pin 4, `DTR` | DE-9 pin 6, `DSR` | `DSR == DTR` |
|
||
| DE-9 pin 7, `RTS` | DE-9 pin 8, `CTS` | `CTS == RTS` |
|
||
|
||
```text
|
||
DE-9 pin 3 TX ─────> pin 2 RX
|
||
DE-9 pin 4 DTR ─────> pin 6 DSR
|
||
DE-9 pin 7 RTS ─────> pin 8 CTS
|
||
```
|
||
|
||
Run `debug loopback-a`. The test cycles all TX/DTR/RTS states and verifies the three receivers and `VLD`. Driver and receiver inversions cancel, so the resulting ESP32 logic levels must match.
|
||
|
||
This configuration also supports `debug valid-test`, `debug uart-loopback <baud> [format] [bytes]`, and `debug uart-suite`; the UART commands need only the pin 3-to-2 connection.
|
||
|
||
## Configuration B: remaining receivers
|
||
|
||
Remove every configuration A jumper, then connect:
|
||
|
||
| Driven output | Receiver input | Expected ESP32 logic |
|
||
|---|---|---|
|
||
| DE-9 pin 3, `TX` | DE-9 pin 1, `DCD` | `DCD == TX` |
|
||
| DE-9 pin 4, `DTR` | DE-9 pin 9, `RI` | `RI == DTR` |
|
||
| DE-9 pin 7, `RTS` | DE-9 pin 2, `RX` | `RX == RTS` |
|
||
|
||
```text
|
||
DE-9 pin 3 TX ─────> pin 1 DCD
|
||
DE-9 pin 4 DTR ─────> pin 9 RI
|
||
DE-9 pin 7 RTS ─────> pin 2 RX
|
||
```
|
||
|
||
Run `debug loopback-b`. Configurations A and B together exercise all three drivers and five receivers.
|
||
|
||
## CTS transmit gating
|
||
|
||
Disconnect all external DE-9 equipment, power down, and remove previous jumpers. Install only:
|
||
|
||
| Driven output | Receiver input | Purpose |
|
||
|---|---|---|
|
||
| DE-9 pin 3, `TX` | DE-9 pin 2, `RX` | Return UART1 data for comparison |
|
||
| DE-9 pin 4, `DTR` | DE-9 pin 8, `CTS` | Present inactive and active CTS states |
|
||
|
||
```text
|
||
DE-9 pin 3 TX ─────> pin 2 RX
|
||
DE-9 pin 4 DTR ─────> pin 8 CTS
|
||
```
|
||
|
||
Run `debug cts-flow-test`. At 115200 baud with hardware CTS enabled, the firmware first queues 512 bytes while CTS is inactive and confirms that no data is sent for 250 ms. It then activates CTS and verifies automatic, exact transmission and reception of all bytes.
|
||
|
||
## RTS receive backpressure
|
||
|
||
Disconnect all external DE-9 equipment, power down, and remove previous jumpers. Install only:
|
||
|
||
| Driven output | Receiver input | Purpose |
|
||
|---|---|---|
|
||
| DE-9 pin 4, `DTR` | DE-9 pin 2, `RX` | Carry UART2-generated data to UART1 RX |
|
||
| DE-9 pin 7, `RTS` | DE-9 pin 1, `DCD` | Return UART1 RTS to UART2 CTS |
|
||
|
||
```text
|
||
UART2 TX / DE-9 pin 4 DTR ─────> pin 2 RX / UART1 RX
|
||
UART1 RTS / DE-9 pin 7 RTS ─────> pin 1 DCD / UART2 CTS
|
||
```
|
||
|
||
Run `debug rts-flow-test`. UART2 queues 4096 bytes while UART1 is initially unread. Once UART1 reaches its receive threshold, it deasserts RTS and stops UART2 through CTS. Draining UART1 must reassert RTS and let UART2 finish. `UART_BUFFER_FULL` is expected; FIFO overflows, framing/parity errors, breaks, or any data mismatch are failures.
|
||
|
||
## Voltage and polarity checks
|
||
|
||
With no DE-9 loopback jumpers installed, measure pins 3, 4, and 7 relative to DE-9 pin 5 (`GND`):
|
||
|
||
```text
|
||
debug drivers 0 0 0
|
||
```
|
||
|
||
Each output should be at a positive RS-232 voltage. Then run:
|
||
|
||
```text
|
||
debug drivers 1 1 1
|
||
```
|
||
|
||
Each output should be negative. Exact voltages depend on supply, load, meter, and charge-pump behavior; polarity is the important result.
|
||
|
||
## Recommended order
|
||
|
||
1. With no DE-9 jumpers, run `status`.
|
||
2. Measure output polarity with `debug drivers 0 0 0` and `debug drivers 1 1 1`.
|
||
3. Power down, install configuration A, power up, and run `debug loopback-a`.
|
||
4. Run `debug valid-test` with configuration A still installed.
|
||
5. Keep only pin 3-to-2 and run `debug uart-loopback 9600 8N1 256`, then `debug uart-suite`.
|
||
6. Power down, install configuration B, power up, and run `debug loopback-b`.
|
||
7. Power down, install CTS wiring, power up, and run `debug cts-flow-test`.
|
||
8. Power down, install RTS wiring, power up, and run `debug rts-flow-test`.
|
||
|
||
RTS and CTS remain GPIO signals during static and basic UART tests. Only the flow-control tests assign them to UART peripherals. The firmware shuts down the MAX3243 while changing GPIO-matrix routing and restores outputs to static logic 1 after each test.
|
||
|
||
## Session-broker loopback
|
||
|
||
This test verifies broker fan-out and writer ownership independently of the diagnostics. Disconnect external peers, power down, remove previous jumpers, and connect only DE-9 pin 3 (`TX`) to pin 2 (`RX`). Power up and run:
|
||
|
||
```text
|
||
serial start
|
||
broker connect writer
|
||
broker connect observer
|
||
broker clients
|
||
```
|
||
|
||
Use the client IDs printed by the device:
|
||
|
||
```text
|
||
broker request-writer <writer-id>
|
||
broker send-hex <writer-id> 0055aaff1b5b33316d
|
||
broker read <writer-id> 64
|
||
broker read <observer-id> 64
|
||
```
|
||
|
||
Both clients should receive `0055aaff1b5b33316d`. A read may return zero if UART1 has not returned the bytes yet; repeat it shortly afterward.
|
||
|
||
Verify writer exclusion and administrative reassignment:
|
||
|
||
```text
|
||
broker request-writer <observer-id>
|
||
broker send-hex <observer-id> dead
|
||
broker force-writer <observer-id>
|
||
broker send-hex <observer-id> 112233
|
||
broker read <writer-id> 64
|
||
broker read <observer-id> 64
|
||
broker events <writer-id>
|
||
broker events <observer-id>
|
||
```
|
||
|
||
The competing request and its initial send should fail. After reassignment, both clients should receive `112233`; events should show denial, revocation, and grant. Clean up:
|
||
|
||
```text
|
||
broker disconnect <observer-id>
|
||
broker disconnect <writer-id>
|
||
serial stop
|
||
```
|
||
|
||
Remove the loopback jumper with power off before connecting an external serial device.
|