Record five-board WiFi UDP bridge hardware qualification

This commit is contained in:
mikecarper committed 2026-10-22 17:03:03 -07:00
1 parent f4ade738df
commit a1b2d68115
4 files changed
+3012

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ Completed validation reports, experiments, and test guides are collected here. O
## Radio and RF
- [Five-board eight-peer WiFi UDP bridge hardware qualification, 2026-10-22](udp_bridge_hardware_2026-10-22.md)
- [Eight-peer WiFi UDP bridge: V3/V4/R8 Full build and host validation, 2026-10-22](udp_bridge_2026-10-22.md)
- [RS-232 bridge diagnostics: host/build checks and existing RAK Full RAM failure, 2026-10-22](rs232_bridge_diagnostics_2026-10-22.md)
- [ESP-NOW bridge diagnostics: V4 and V4 R8 TFT Full build/host validation, 2026-10-22](espnow_bridge_diagnostics_2026-10-22.md)
@@ -0,0 +1,401 @@
{
"schema_version": 1,
"scope": "Opaque receipt hashes for preserved attempts and final evidence; separate archives retain earlier failures.",
"cases": [
{
"case_id": "baseline-matrix01",
"status": "failed",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "2ff6405ba18eada054e76c4ad9b0af4c6fa924e3c2026d9d9de7eb69fdd8b0fa",
"bytes": 17318
}
},
{
"case_id": "rf02",
"status": "failed",
"counts": {
"result_rows": 0,
"passing_rows": 0,
"positive_rows": 0,
"negative_rows": 0
},
"source_commit": null,
"receipt": {
"sha256": "e58e68ccd6c54c7df96a1d434bc9d21090ee3f632f1f96d26324b0dc63987a17",
"bytes": 585
}
},
{
"case_id": "rf03",
"status": "failed",
"counts": {
"result_rows": 0,
"passing_rows": 0,
"positive_rows": 0,
"negative_rows": 0
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "7635a3f7b1ad1fefec7f59b15bbb083183b3d6b4ae1a19287fd05bac0e2c2d57",
"bytes": 1491
}
},
{
"case_id": "recovery03-01",
"status": "failed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "572a37a98b4245919c971af3ce17748fda7da2d5fd4ca7a93bd59094d719b0c3",
"bytes": 2468
}
},
{
"case_id": "recovery03-02",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "f9d54ad2fc642f268e7dac1cdc22d62ec2bcc47633b8e819b65655e0e333c9b5",
"bytes": 2370
}
},
{
"case_id": "rf-setup01",
"status": "setup_complete",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "47ed75dc7c0bb9572ec37ccb0895ac3ef8baa0dedd082e979230264cc0d5616f",
"bytes": 8271
}
},
{
"case_id": "rf-setup03",
"status": "setup_complete",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "9748e5954eb9f6bc4f7a7bbb7e30f1d56846acaf99ff81c692037706fbabd46a",
"bytes": 8269
}
},
{
"case_id": "rf-setup04",
"status": "setup_failed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "7ca7128885ca05ceda7281f9e23748642c7e6cc51aaa7985e78e59c48c9df975",
"bytes": 8354
}
},
{
"case_id": "witness-setup02",
"status": "passed",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "e61d0ecacd3a5551761beddaa9d81fe79209a4834f4d8e7842bcd0193623ba7e",
"bytes": 1029
}
},
{
"case_id": "witness-setup03",
"status": "passed",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "360e935fbb76930e865ba7b124710a2e029117af77ecb443e62fc5080171f372",
"bytes": 1029
}
},
{
"case_id": "witness-setup04",
"status": "passed",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "5c2171f9c96f3baa54a0733c4496620dfd6f25459c410facd48fb606f64178ef",
"bytes": 1029
}
},
{
"case_id": "rf04",
"status": "failed",
"counts": {
"result_rows": 1,
"passing_rows": 0,
"positive_rows": 0,
"negative_rows": 1
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "1d629b65ff275307e5f1c251851757935ac8b0897959d8706fed289889d0b3d2",
"bytes": 1409
}
},
{
"case_id": "rxps-restore01",
"status": "failed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "976f0b1be6bf29817bc23ce6cd75824d1a94e18357e18c6386f8d35a45335f43",
"bytes": 2016
}
},
{
"case_id": "rxps-restore02",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "f65355ca9b72cc703d344a2e657e7d6c66acb3313742b61227ee68edf0b580b3",
"bytes": 1933
}
},
{
"case_id": "rxps-restore03",
"status": "failed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "976f0b1be6bf29817bc23ce6cd75824d1a94e18357e18c6386f8d35a45335f43",
"bytes": 2016
}
},
{
"case_id": "rf05",
"status": "failed",
"counts": {
"result_rows": 12,
"passing_rows": 12,
"positive_rows": 10,
"negative_rows": 2
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "31b98ce4bd26825e371fd5fd386113692d0d55e92da34a8f7bb3926265916afe",
"bytes": 5595
}
},
{
"case_id": "rf06",
"status": "failed",
"counts": {
"result_rows": 0,
"passing_rows": 0,
"positive_rows": 0,
"negative_rows": 0
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "81ac160be7164cab08f235fd51e54962b9e5df6b69395d9b1570de3db2a37d7a",
"bytes": 1299
}
},
{
"case_id": "rf07",
"status": "passed",
"counts": {
"result_rows": 18,
"passing_rows": 18,
"positive_rows": 15,
"negative_rows": 3
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "c6dd421a9cf375136ac8034bcd58b2640fa7d7c4fd3c8cbf2d49d1f84012a165",
"bytes": 8046
}
},
{
"case_id": "fixture04-setup",
"status": "setup_complete",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "8c060e7eaf29cd2f2d3cb36ef8deee8f8fef2c5931e478b7583a24b4087f7dd3",
"bytes": 511
}
},
{
"case_id": "fixture05-setup",
"status": "setup_complete",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "9ac8ba8b90dc7f2fa6761148baa01e04863f62a8a44f02d22e0e7a42d23fbc6f",
"bytes": 548
}
},
{
"case_id": "final-matrix02",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "0fefdc8c90537fd59b472cb6125026033e0b1209a2edb6f14c4c881244d65f55",
"bytes": 18062
}
},
{
"case_id": "qualified-software-reboot02",
"status": "passed",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "d3d2920b041263231dfccec10dfa5b28e76aab558e94116c3cdb28852a2625ef",
"bytes": 8072
}
},
{
"case_id": "combined-rf05-07",
"status": "passed",
"counts": {
"result_rows": 30,
"passing_rows": 30,
"positive_rows": 25,
"negative_rows": 5
},
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "60cf4f52f582591209926ded9784df05d32b9c06449586a28648ed084975053f",
"bytes": 15031
}
},
{
"case_id": "final-cleanup07",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "40faf96bc7c93011e4225e8e6a7f1427c78e37114fa3c67215131ea20db9bbb1",
"bytes": 4606
}
},
{
"case_id": "independent-raw-rf-audit",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "3f198199c88432259004b3adac498ea0a013de6b19ea883e87b66ca5be0d7ce0",
"bytes": 2105
}
},
{
"case_id": "independent-cleanup-audit",
"status": "passed",
"counts": null,
"source_commit": "36d9735e5539656a31b648eff32cae6b99576500",
"receipt": {
"sha256": "3903dfa3538d9426fd227f78ed83d1b8f3fc2a0ca4ead064e58f0af28a65c6a2",
"bytes": 9372
}
},
{
"case_id": "baseline-matrix01-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "c29951baf0963b38f2689c41dbc85257b68c5713403e2f02faf1b92d2c3c8b69",
"bytes": 3891200
}
},
{
"case_id": "blocked-rf02-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "398878df341f8aba7ea8ffefdb469a69382f2454b4a5a3bd55270aacf0f8405d",
"bytes": 20480
}
},
{
"case_id": "host-fault-rf03-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "2cb4a09c0bc835080f90a0943ae1a370e9e1a1eca3ed7fa65d431414e45842fb",
"bytes": 133120
}
},
{
"case_id": "rf04-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "cb90750f060c5fb017b50bdeece5c4086f9f8e411ae0f680d9356b64a63f56e3",
"bytes": 624640
}
},
{
"case_id": "rxps-restore01-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "e3e00af6b01009d34b860b632016960cb1e2c26c7b67455884e44375ffa875b9",
"bytes": 389120
}
},
{
"case_id": "rf05-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "538911603ff25457c1bd67aaa2b901f9fcf64ebd77b5b520e7134943c5b3ab6e",
"bytes": 2662400
}
},
{
"case_id": "rf06-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "c3351b1af36040e1bbd3f4894b093c336194e6a6dd261d41dfa11f78573d8823",
"bytes": 399360
}
},
{
"case_id": "fixture05-setup-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "3229b11c0dc0e3187da021466d6a7a9fad544a59def0383b16f274a3a50f1cb1",
"bytes": 7741440
}
},
{
"case_id": "original-fixture-terminal-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "7b18b360255462b29faac6ab25c2413c04e6929c9a2cdb3c9e5b43df4db0977a",
"bytes": 5918720
}
},
{
"case_id": "final-evidence-archive",
"status": "preserved_archive",
"counts": null,
"source_commit": null,
"receipt": {
"sha256": "22adb7f49b6c1650248f63acf9f6f9a70971e5c7b3c3e17400c4b28803eb4206",
"bytes": 18503680
}
}
]
}
@@ -0,0 +1,164 @@
<!-- meshcore-hosted-doc-link:start -->
<p class="meshcore-hosted-doc-link"><a href="https://mikecarper.github.io/MeshCore/test-results/udp_bridge_hardware_2026-10-22/">View this page on MeshCore Docs</a>.</p>
<!-- meshcore-hosted-doc-link:end -->
# Five-board WiFi UDP bridge hardware qualification, 22 October 2026
**PASS for the tested Full Repeater configuration.** Five physical ESP32 boards
passed the directed UDP matrix, rejection checks, reconnects, software-reboot
persistence and physical LoRa forwarding through eight configured UDP peers.
All final images use
[source `36d9735e5`](https://github.com/mikecarper/MeshCore/commit/36d9735e5539656a31b648eff32cae6b99576500).
Independent raw-packet and cleanup audits passed before consolidation of the
[machine-readable results](udp_bridge_hardware_2026-10-22.safe.json).
Physical coverage combines two disjoint scopes: twelve accepted passing cases
from failed run05 and eighteen from passing run07. Run05 remains failed; the
first twelve cases were not repeated. Failed run06 sent no data and remains
preserved separately.
## Coverage and results
| Check | Completed result |
| --- | --- |
| Canonical Full builds and installation | Five boards; capability and memory gates passed; no feature reductions |
| Directed UDP probes | 300/300 across 60 directions, five probes each |
| Security rejections | 20/20: replay, bad authentication, unknown endpoint and malformed frame on each board |
| Recovery | WiFi reconnect and saved UDP-off choice passed on all five |
| Qualified software reboots | Five restarts, retained configuration and 20/20 fresh probes |
| Physical LoRa delivery | 25/25 exact application receipts, five per ingress board |
| UDP-off negative controls | 5/5; none crossed the frequency gap |
| Independent copy audit | 100 physical-peer copies and 100 Pi-endpoint copies; zero UDP transit |
| Final cleanup | Five boards quiet, original RXPS restored and owned fixture stopped |
The matrix covered 20 board-to-board, 20 board-to-Pi and 20 Pi-to-board
directions. Initial station readiness was observed on the first read-only poll
for every final image. Each board had 23 continuity samples spanning at least
1,043 seconds, with advancing uptime and no new
latched error bits. The reboot qualification used deliberate software restarts,
not cold power cycles. Identity, exact firmware/model, shared secret, UDP port
and all eight peers persisted; eight authenticated sessions returned.
## Boards and builds
| Board | Selected Full environment | Modeled internal-memory margin |
| --- | --- | ---: |
| Heltec V3 | `Heltec_v3_repeater_observer_mqtt` | 352 bytes |
| Heltec V4.3 OLED | `heltec_v4_repeater_observer_mqtt` | 4,720 bytes |
| LilyGo T-Deck | `LilyGo_TDeck_repeater` | 53,632 bytes |
| Seeed XIAO ESP32-S3 WIO | `Xiao_S3_WIO_repeater_observer_mqtt` | 9,960 bytes |
| SenseCAP Indicator LoRa N8R8 | `SenseCapIndicator-LoRa_repeater` | 1,904 bytes |
The shared V4 target detects the V4.3 FEM at runtime. The Indicator tested here
has 8 MiB flash and OPI PSRAM. Its separate 16 MiB/QSPI N16R2 recipe is not
interchangeable and was not physically qualified. Existing display support
was retained in the repeater recipes. Build receipts bind source, recipe and
application/merged-image hashes, verify both OTA slot capacities, and report
empty feature-reduction lists. Slot checks do not qualify an OTA installation.
## Physical method and operating conditions
Each ESP32 had four other physical ESP32 peers plus four controlled Pi UDP
endpoints: eight configured destinations, not eight physical peer radios.
All joined an existing WiFi network as stations. MQTT, ESP-NOW and RS232 bridges
were disabled. UDP used the existing bridge shared secret without another
password; authentication does not encrypt the traffic.
Physical trials used **917 MHz ingress and 918 MHz egress, SF7, 62.5 kHz
bandwidth, CR5, preamble 32 and TX 2 dBm**. Radio2 and LoRa RX powersaving were
**off during packet trials**. Each ingress received one UDP-off control and
five fresh positive frames. T096 and Tag served as the physical source/sink
witnesses. Their different frequencies prevented a direct LoRa delivery from
substituting for bridge forwarding. The Pi did not inject mesh data, and each
accepted trial had exactly one physical send with no application retry.
The independent audit checked source counters, exact application bytes and
sequence, actual Pi event membership, four matching Pi copies per positive
frame, all four physical-peer copy observations, and exactly one designated
LoRa egress. Physical-peer observations include explicitly accounted duplicate
receipts. No receiving UDP peer re-exported the frame to UDP. Before each role,
the harness waited at least 65 seconds after radio activation and proved at
least two seconds of stable RF/UDP counters.
Historical watchdog flags and cumulative receive errors were retained in the
raw baseline. V3 had four receive errors recorded during setup; no measured
packet added one. A previously latched bit 8 was admitted only with explicit live
RX history, matching diagnostic/physical error counters, soft-only recovery
and an empty queue. This does not attribute every historical flag to idle
maintenance. Every measured packet required unchanged latched flags and
soft/hard recovery counters, **zero new physical receive errors**, and **zero
new witness receive errors**. There was no blanket exemption for bit 8.
## Runtime memory and final state
These measured internal-memory values are bytes. The boot minimum is the
firmware low-water counter read at cleanup, not a minimum across every earlier
boot. Modeled build margins above are separate capacity estimates.
| Board | Before cleanup free | Reported boot minimum | After cleanup free |
| --- | ---: | ---: | ---: |
| V3 | 74,400 | 42,652 | 114,800 |
| V4.3 | 81,164 | 76,028 | 106,528 |
| T-Deck | 151,584 | 145,812 | 176,996 |
| XIAO ESP32-S3 WIO | 136,464 | 131,304 | 161,844 |
| Indicator N8R8 | 106,644 | 101,672 | 132,036 |
All final core and memory queues were zero. V3 had no PSRAM; the other boards
reported it. Cleanup restored all five original RXPS-on profiles, disabled
UDP/repeat/logging, cleared all eight test peers, selected bridge source TX and
left WebConfig inactive. Identity, saved radio, WiFi/secret and CPU power policy
were preserved. No CPU-clock setter was issued; actual CPU-frequency readback
is not claimed. Cleanup performed no flash, erase, reboot, stats clear or
intentional data send. The final owned fixture stopped once, following the
separately receipted stop of its predecessor.
## Fix and preserved failures
The original V4.3 recovery failure exposed UDP/WebConfig station ownership:
an unused automatic recovery AP prevented the saved station connection from
returning. `36d9735e` fixes that handoff while preserving explicit setup/OTA
ownership and saved UDP-off intent. Twelve native ASan/UBSan tests and
independent review preceded the five final builds; the final physical matrix
then passed the formerly failing recovery sequence. The UDP protocol did not
change.
Earlier failures were retained rather than replaced by later passes. The
[history hash index](udp_bridge_hardware_2026-10-22.history.safe.json) records
actual receipt outcomes and separate preserved archives.
| Preserved attempt | Outcome and disposition |
| --- | --- |
| Baseline matrix01 | V4.3 recovery failed; XIAO recovery was not run. Final `36d9735e` matrix02 passed all five. |
| Initial Indicator join | One WiFi off/on joined with the same saved credentials; separate root cause remains unproven. |
| RF02 / RF03 | Zero data trials: busy witness setter, then Pi USB/Ethernet host fault. Recovery03-01 readback adapter failed; corrected recovery03-02 passed all seven devices. This was not measured RF packet loss. |
| RF04 | One accepted negative request; automatic OTA bootstrap traffic changed the strict negative-control counters. A bounded bootstrap/quiet wait was added. |
| RXPS restore01/03; setup04 | V4.3 restoration rejected; setup04 separately failed on a startup queue. Restore02 and final cleanup restored all five original profiles. Ordinary V4.3 RXPS is supported. |
| RF05 | Twelve strict passes, then preflight stopped on a latched watchdog flag before V3 data. Overall failed status retained. |
| RF06 | Zero data; historical-baseline gate incorrectly required current last-RX age and absolute zero cumulative errors. Run07 retained baselines and enforced zero new errors per packet. |
| Fixture and offline adapters | Original fixture ended during the host fault; two explicit RF fixture replacements preserved identical configuration. Exact getter/model adapters were corrected without firmware changes or repeating accepted packets. |
Full CI is separate evidence. Test-only fixture repairs were pushed in
`8da70b84d` and `52a36c89d`; focused tests passed. At report preparation,
[CI run 39869220511](https://github.com/mikecarper/MeshCore/actions/runs/39869220511)
had twelve successful non-main jobs while its main test was still running;
full CI success is not asserted here.
## Evidence and limits
The consolidated result binds all 32 opaque private-file hashes, both independent
audits and the disjoint campaign receipts. Its SHA-256 is
`f6a384cc496f8c1a457ddd10df581925ca0a444ebad51fec65155fed3e68a4f3`.
The final private archive SHA-256 is
`22adb7f49b6c1650248f63acf9f6f9a70971e5c7b3c3e17400c4b28803eb4206`.
That tar covers final evidence; earlier failures remain in separate preserved
archives identified by the history index. Private contents are not published.
The later noise-floor telemetry change in `f4ade738d` was not installed on
these radios and is outside this hardware result.
This qualifies Full Repeater UDP forwarding under the stated radio/power
conditions. It does not qualify other firmware types, every power/radio
profile, eight physical peers, AP-mode UDP, arbitrary router/VLAN policies or
client-isolation bypass. Simultaneous bridge backends, BLE, physical firmware
OTA application, cold starts, maximum load and long-duration soak remain
outside this campaign.
File diff suppressed because it is too large. Load diff