ESP32-S3 NimBLE trial results, 2026-09-08
These are local hardware trial results for USA Cascade Full Companions on a Heltec V4.3 OLED and a Seeed XIAO ESP32-S3 with WIO SX1262. The V4.2 shares the profile but was not physically tested. Build and command instructions are in the trial guide.
The final source revision is 9dbab431, using NimBLE-Arduino 2.5.1 and the
existing Arduino-ESP32 2.0.17 toolchain. These optional profiles are outside the
ordinary release matrix. This trial does not change GitHub release assets or
the firmware picker.
Capacity
| Measurement | Heltec V4 | XIAO S3 WIO |
|---|---|---|
| Contacts / channels | 350 / 40 | 350 / 40 |
| PSRAM-backed offline queue | 512 | 256 |
| Application image, bytes | 1,774,600 | 1,510,536 |
| OTA application slot, bytes | 6,553,600 | 3,342,336 |
| Linked internal capacity before runtime allocation, bytes | 263,944 | 265,576 |
| Required runtime RAM budget, bytes | 173,056 | 148,480 |
| Headroom beyond that budget, bytes | 90,888 | 117,096 |
| Live free heap with Bluetooth and 350 test contacts, bytes | 142,644 | 147,444 |
| Minimum free heap during that contact test, bytes | 138,024 | 142,788 |
The live contact-test readings are from revision 7f1bd8b1. Revision
9dbab431 retains the same Bluetooth adapter, contact allocation and queue
sizes, and fixes the MQTT save stack use described below. Linked capacity is
not live free heap; the firmware memory reports label that distinction.
A matching V4 build using Bluedroid had 248,840 bytes of linked internal capacity. NimBLE recovers 15,104 bytes at link time. The matching Bluedroid application was 2,169,848 bytes. There was no matching physical Bluedroid heap measurement, so these figures do not establish a live heap saving.
Bluetooth hardware checks
Both boards passed the following cases with BlueZ on the Mercerwood Pi:
- PIN authentication, encryption, stored bonding, and bonded reconnection.
- MTU 179; accepted 176-byte writes and complete 148-byte contact notifications.
- Insert and synchronize all 350 synthetic contacts without missing or duplicate entries.
- Custom MAC address appears over the air in the entered byte order and persists across reboots.
- Saved random MAC persists; random-every-boot changes on each boot.
- Random-after-connect retains its address through an unauthenticated boot and rotates on the boot following an authenticated connection.
- Stealth first pairing, bonded-peer reconnection, and reconnection after reboot with a stable identity.
- Restore factory MAC and ordinary discovery.
- Three reconnect samples with no observed downward free-heap trend.
The complete MAC suite ran on 7f1bd8b1. Final-image connection and update
checks also verify the unchanged adapter after the MQTT stack fix. Synthetic
contacts were removed after testing; factory MAC mode and stealth off were
restored.
MQTT save stack overflow
The V4 test reproduced an actual reset while saving MQTT settings through
WebConfig. Serial output identified Stack canary watchpoint triggered
(loopTask). onConfigBatchEnd() kept a second 2.8 KB preference copy while
the loader and NVS code also used stack space. Free heap was not the limiting
resource.
The loader already updates its destination only after validation, so the callback now passes the live preference object directly. The actual Xtensa callback frame shrank from 2,928 bytes to 32 bytes. The regression test compiles the real callback with a 512-byte frame ceiling and demonstrates that the previous nested-copy implementation fails the check.
On the corrected 9dbab431 image, the 11-setting WebConfig batch completed
with all entries accepted and no reboot. The V4 connected to a local
Mosquitto broker, published MQTT traffic, and kept its authenticated Bluetooth
session alive through a 30-second load interval with continuous uptime and
zero reported error flags. Free heap measured 120,684 then 122,140 bytes;
the minimum was 118,332 bytes and the largest free block was 110,580 bytes.
Disabling the test MQTT slot also completed successfully without reboot.
WiFi updates and QR
Both boards completed real application-image uploads over WiFi with Bluetooth connected, followed by reboot and reconnection using the saved bond. Repeated HTTP page requests and Bluetooth status requests ran together before upload. The image hash was checked before transmission, and the running firmware version was checked after reboot.
The V4 WiFi setup page again contains a QR code. The default open setup SSID fits a 44 x 44 pixel code: 2 x 2 pixel modules and an exact one-pixel white border. The native test rendered the actual display/QRCode code and decoded the result with ZXing. SSID, portal address and the hold-to-stop hint fit beside it. Physical phone-camera scanning has not been verified.
Observed limits
One XIAO warm reboot left its USB endpoint unresponsive while Bluetooth still answered. The user's physical unplug/replug restored USB; the later full MAC suite completed its commanded reboots successfully. The cause of that one USB failure is unconfirmed.
The XIAO initially failed to associate with the saved WiFi network. Reapplying the SSID scheduled a fresh connection, after which it joined at -53 dBm and completed WiFi OTA. No antenna change was needed for that recovery.
This was functional hardware testing, not a long-duration soak. iOS/Android app pairing, phone camera scanning, multiple TLS MQTT brokers, and an end-to-end LoRa mOTA transfer were not qualified in this trial. The XIAO parent profile does not include direct MQTT; the V4 does.
Final application hashes
| Hardware | SHA-256 |
|---|---|
| V4 | e6a1522ced4ed38ddd00e7cdf8393857224adbb622888a86370c0f7a3ee28d88 |
| XIAO S3 WIO | ecf7afe6222e3897556a04e022515f9dcfcb825172dae9eaaa72eb92396dd004 |