Propagation sync (microReticulum submodule):
- Fix msgpack interop: send nil (not 0) for per_transfer_limit so
Python server doesn't reject all messages as exceeding "0 KB limit"
- Fix Resource response routing: extract request_id from packed data
when not present in Resource advertisement, route to pending request
callback instead of generic concluded handler
- Fix Link::request() to manually build packed arrays, avoiding
Bytes::to_msgpack() BIN-wrapping that breaks protocol interop
UI enhancements:
- PropagationNodesScreen: manual node entry via 32-char hex hash in
search field, with paste support and radio button selection
- StatusScreen: display stamp cost from propagation node
- UIManager: NVS persistence for selected propagation node, proactive
path request on node selection, sync state machine with timeout
handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only destinations that have exchanged messages are written to SPIFFS.
UIManager marks destinations as persistent on send_message() and
on_message_received(). Reduces persist time from 40-50s to <1s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduces crash rate from every 60-85s to 1 reboot per 6+ minutes.
Zero WDT triggers in 10-minute stability test.
BLE mutex fixes (BLEInterface.cpp):
- Release _mutex before blocking GATT ops in onConnected() and
onServicesDiscovered() — prevents 5-30s main-loop stalls during
service discovery, notification subscribe, identity exchange
- Non-blocking try_lock() for peerCount(), getConnectedPeerSummaries(),
get_stats() — returns empty/default if BLE task holds mutex
- Write-without-response in initiateHandshake()
WDT and persistence (main.cpp, sdkconfig.defaults, microReticulum):
- 30s WDT timeout (up from 10s) for SPIFFS flash I/O headroom
- Register Identity::set_persist_yield_callback() to feed WDT every
5 entries during save_known_destinations() (70+ entries = 30-50s)
- WDT feeds between reticulum and identity persist calls
BLE host desync recovery (NimBLEPlatform):
- Time-based desync tracking instead of aggressive counter-based reboot
- 60s tolerance without connections, 5 minutes with active connections
(data still flows over existing BLE mesh links)
- Remove immediate recoverBLEStack() from 574 handler and
enterErrorRecovery() — let startScan() manage reboot decision
- Increase CONNECTION_COOLDOWN from 3s to 10s to reduce 574 risk
- Increase SCAN_FAIL_RECOVERY_THRESHOLD from 5 to 10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NimBLE crash fix:
- Patch ble_hs.c assert(0) in BLE_HS_SYNC_STATE_BRINGUP timer handler
via pre-build script (patch_nimble.py). The assert fires when a timer
callback races with host re-sync — harmless, but kills the ESP32 and
corrupts any file writes in progress.
Persistence fixes (in microReticulum submodule):
- Atomic save: write to temp file then rename, protecting existing data
- Fast persist: 5s after dirty flag instead of waiting 60s interval
- Corrupt file recovery: delete invalid files, recover from temp files
- INFO-level logging for load/save visibility
Other:
- Wrap LXMF announce in try/catch for crash safety
- Call Identity::should_persist_data() from main loop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix systemic One Definition Rule violation where BLEInterface.h included
headers from deps/microReticulum/src/BLE/ while .cpp files compiled
against local lib/ble_interface/ versions, causing struct layout mismatches
(PeerInfo field shifting corrupted conn_handle/mtu) and class layout
mismatches (BLEPeerManager member differences caused LoadProhibited crash).
Key fixes:
- Include local BLE headers instead of deps versions in BLEInterface.h
- Sync PeerInfo keepalive tracking fields and BLETypes constants with deps
- Shutdown re-entrancy guard and proper client cleanup via deinit(true)
- Host sync checks before scan, advertise, and connect operations
- Avoid deadlock by deferring _on_connected from NimBLE host task
- Duplicate identity detection, stale handle cross-check in keepalives
- Bounds validation on conn_handle in setPeerHandle/promoteToIdentityKeyed
- Periodic persist_data() call for display name persistence across reboots
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix link packet routing in microReticulum to only transmit on the
link's attached interface (was flooding all interfaces including LoRa)
- Batch up to 8 Codec2 frames per Reticulum packet to reduce per-packet
encryption/transport overhead (~3 packets/sec instead of 25)
- Add TX diagnostics: hex dump of first 2 packets, per-interval stats
with link status, idle/drop warnings
- Audio now reaches remote end (TX=325 frames in 12s test call)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace custom ES7210 register-level driver with the verbatim LilyGO
T-Deck Plus ES7210 library. The custom driver was writing MODE_CONFIG
and clock doubler registers (master mode path) which broke the ES7210's
clock chain, causing all-zero PCM output. The LilyGO library in slave
mode leaves those registers at power-on defaults, which is correct since
the ESP32 I2S master provides MCLK/BCLK/LRCK.
Also includes LXST voice call protocol improvements:
- LXST IN destination for receiving calls
- Announce handler for tracking voice-capable peers
- Path request before outgoing calls
- Throttled SPIFFS saves in microReticulum (dirty flag)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix slow screen wake: track screen_off_time and wake on any activity
newer than when screen turned off (was requiring activity <1s ago)
- Add MEMORY_MONITOR_POLL() to main loop for deferred logging
- Add NVS crash breadcrumb system for LXST call debugging
- Update microReticulum submodule (memory stability fixes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace std::map and std::vector with fixed-size pools in
BLEInterface (fragmenters, pending handshakes, pending data)
- Track keepalive failures and disconnect after 3 consecutive
- Force-disconnect zombie peers detected by BLEPeerManager
- Add periodic advertising refresh (every 60s) to combat silent stops
- Buffer incoming data when identity not yet mapped instead of dropping
- Subtract ATT_OVERHEAD from MTU in NimBLEPlatform connection setup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update microReticulum submodule: static pool arrays (~20 pools) moved
from BSS segment to PSRAM via heap_caps_aligned_alloc at startup.
Before: boot heap ~116KB, steady-state max_block 7.6KB, constant skipped announces
After: boot heap ~161KB, steady-state max_block 65KB, zero skipped announces
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Includes:
- Link operator== / operator!= for correct pool lookups
- activate_link null guard and copy-before-remove
- Known destinations pool increased to 2048 slots
- Proactive culling at 90% threshold
- Low-memory guard for resource retries on ESP32
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split T-Deck firmware from microReticulum examples/lxmf_tdeck/ into its
own repo. microReticulum is consumed as a git submodule dependency pinned
to feat/t-deck. All include paths updated from relative symlinks to bare
includes resolved via library build flags.
Both tdeck (NimBLE) and tdeck-bluedroid environments compile successfully.
Licensed under AGPLv3.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>