Commit Graph
49 Commits
Author SHA1 Message Date
torlando-techandClaude Opus 4.6 4e1f379d94 Persist only contacts to flash, mark on send/receive
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>
2026-02-23 13:17:28 -05:00
torlando-techandClaude Opus 4.6 e343caf2d2 Stability: WDT yield, BLE mutex fixes, time-based desync recovery
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>
2026-02-23 12:30:30 -05:00
torlando-techandClaude Opus 4.6 3ca27f53f6 Task watchdog, BLE mutex fixes, NimBLE crash-safe recovery
Subscribe loopTask and BLE task to the ESP32 Task Watchdog (10s timeout)
to detect and recover from silent hangs. Per-step WDT feeds in the main
loop prevent false triggers from cumulative slow operations.

Fix BLE mutex starvation that blocked the main loop for 3-6s:
- Move processDiscoveredPeers() out of performMaintenance() so _mutex
  is not held during blocking NimBLE connect calls
- Use try_lock() in send_outgoing() to skip sends when BLE task has
  the mutex, rather than blocking (Reticulum retransmits)
- Switch BLE data writes to write-without-response (non-blocking)
- Add WDT feeds to all NimBLE blocking wait loops

Replace NimBLE soft-reset recovery with immediate reboot — deinit()
during sync failures caused CORRUPT HEAP panics. With atomic file
persistence, data survives reboots reliably.

Reduce loop task stack from 49KB to 16KB (measured peak ~6KB).
Add NimBLE PHY update null guard to patch_nimble.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:45:43 -05:00
torlando-techandClaude Opus 4.6 a499a2b30a Persistence reliability: NimBLE crash fix, atomic save, fast persist
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>
2026-02-23 01:44:38 -05:00
torlando-techandClaude Opus 4.6 43a7e1088f BLE P2P stability: fix ODR violation, shutdown safety, connection robustness
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>
2026-02-23 00:24:45 -05:00
torlando-techandClaude Opus 4.6 ac7b0ac1f7 Remove debug heartbeat and loop_count from BLE interface loop
Cleanup after BLE stability debugging - the Serial.printf heartbeat and
loop_count were temporary instrumentation no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:46:35 -05:00
torlando-techandClaude Opus 4.6 869963c33c BLE: use NimBLEClient for connections, fix service discovery and host reset
Root cause: connectNative() used raw ble_gap_connect() which bypasses
NimBLE's client management. The NimBLEClient created afterwards wasn't
associated with the connection handle, causing service discovery to fail
with "could not retrieve services". This led to a connect-disconnect loop
where no BLE peers could complete handshakes.

Fix: Replace raw ble_gap_connect() with NimBLEClient::connect() which
properly manages the GAP event handler, connection handle tracking, MTU
exchange, and service discovery. Connections now succeed with MTU 517
and identity handshakes complete.

Also fixed:
- Error recovery escalates to full stack reset (deinit/reinit) when
  NimBLE host fails to sync, instead of looping in a dead state
- Added recursion guard in enterErrorRecovery()
- Promoted key BLE logs (scan, connect, peer status) to INFO level
  for visibility during monitoring
- Added 10-second serial heartbeat with connection/peer/heap stats

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:34:28 -05:00
torlando-techandClaude Opus 4.6 769c9952bd BLE P2P stability: PSRAM zero-init, pool sizing, stuck-state recovery
Root cause: Bytes objects stored in PSRAM-allocated BLEInterface had
corrupted shared_ptr members from uninitialized memory, causing crashes
in processDiscoveredPeers(). Fixed by using heap_caps_calloc instead of
heap_caps_malloc for PSRAM placement-new allocation.

Additional fixes:
- Reduce pool sizes to fit memory budget (reassembler 134KB→17KB,
  fragmenters 8→4, handshakes 32→4, pending data 64→8)
- Store local MAC as BLEAddress struct instead of Bytes to avoid
  heap allocation in PSRAM-resident object
- Move setLocalMac after platform start (NimBLE needs to be running
  for valid random address), add lazy MAC init fallback in loop()
- Add stuck-state detector: resets GAP state machine if hardware
  is idle but state machine thinks it's busy
- Enhance getLocalAddress with 3 fallback methods (NimBLE API,
  ble_hs_id_copy_addr RANDOM, esp_read_mac efuse)
- Fix C++17 structured binding to C++11 compatibility
- Increase BLE task stack 8KB→12KB for string ops in debug logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:57:05 -05:00
torlando-techandClaude Opus 4.6 d58ac9573f Switch to Codec2 3200 (LBW profile) for better voice quality
- Change codec from 1600bps to 3200bps (2x bitrate, 20ms frames)
- Signal LBW profile (0x30) instead of VLBW (0x20) to Columba
- TX batch size 10 frames (10×160=1600 samples matches Columba LBW)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:28:50 -05:00
torlando-techandClaude Opus 4.6 90d5afa35b 15-tap half-band FIR anti-alias filter, reduce mic gain to 15dB
Replace crude 2-tap averaging with proper 15-tap half-band FIR filter
for 16kHz→8kHz decimation (~60dB stopband attenuation, Kaiser beta=6).
Exploits symmetry + half-band zeros for only 5 MACs per output sample.

Separate TDM deinterleave (CH0 extraction at 16kHz) from FIR decimation
for cleaner signal processing pipeline.

Reduce ES7210 mic gain from 8 (24dB) to 5 (15dB) to avoid ADC clipping;
AGC in the voice filter chain compensates for quieter input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:13:37 -05:00
torlando-techandClaude Opus 4.6 3655a8a9eb LXST audio fixes: split codecs, TDM stride-4, anti-alias, TX batching
- Split Codec2 into separate encode/decode instances to eliminate mutex
  contention between capture task (core 0) and main thread decode
- Fix TDM deinterleave: stride-4 (was stride-2) for [CH0,CH1] at 16kHz
  to produce 8kHz mono output matching Codec2's expected sample rate
- Add 2-tap anti-aliasing average before decimation to reduce >4kHz alias
- Add hard limiter at ±16000 to prevent ADC clipping artifacts
- TX batching: send exactly 8 Codec2 frames per packet (2560 decoded
  samples) to match Columba's PacketRingBuffer.frameSamples requirement
- Add capture diagnostics: sample rate, raw/downsampled peaks, hex dumps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:25:38 -05:00
torlando-techandClaude Opus 4.6 7215a2f6f9 Fix SPSC ring buffer violations in capture and playback paths
Both encoded ring buffer (capture) and PCM ring buffer (playback) are
SPSC (Single Producer Single Consumer) with lock-free atomics. The
overflow handlers were calling read() from the producer thread, racing
with the consumer thread on the read index. This caused frame
corruption, duplication, and skipping — resulting in distorted audio.

Fix: Drop new frames on overflow instead of evicting old ones. The
consumer (TX pump / playback task) will drain the buffer naturally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 02:14:13 -05:00
torlando-techandClaude Opus 4.6 6dbdd4854b LXST voice TX: link routing fix, frame batching, TX diagnostics
- 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>
2026-02-22 02:11:13 -05:00
torlando-techandClaude Opus 4.6 06743635eb Fix ES7210 mic capture: use LilyGO library with slave mode
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>
2026-02-22 01:42:47 -05:00
torlando-techandClaude Opus 4.6 f73fbb2568 Fix LXST voice call interop with Python LXST/Columba
- Fix thread safety: defer Reticulum link callbacks (packet, link_closed)
  to call_update() which runs under LVGL lock, preventing crashes from
  concurrent LVGL access across cores
- Fix outgoing call signal handling: store link reference and re-register
  packet/link_closed callbacks in on_call_link_established so signals
  are actually received
- Fix call answer screen freeze: update UI before blocking audio init
  (I2S/ES7210/Codec2 setup) so screen renders immediately
- Fix audio direction: use startPlayback() (speaker RX) instead of
  startCapture() (mic TX) so received audio is actually heard
- Add msgpack wire format for LXST signalling and audio frames
- Add LXST IN destination for receiving calls + announce support
- Add incoming call UI (Answer/Reject buttons) on CallScreen
- Add path request before outgoing call link establishment
- Add LXST announce handler registration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:00:07 -05:00
torlando-techandClaude Opus 4.6 c1af11d75e LXST audio hardware config: ES7210 mic pins, tone helpers, platformio deps
- Add ES7210 I2C address and I2S mic capture pin definitions
- Add ring/hangup tone helpers to Tone library
- Add lxst_audio library scaffold
- Add Codec2 dependency to platformio.ini

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:41:47 -05:00
torlando-techandClaude Opus 4.6 8f265da0bb LXST voice call UI and state machine
- Add CallScreen with ring/active/ended states and call controls
- Add call state machine to UIManager (link establish, identify, ring, answer)
- Add call button to ChatScreen header
- Add call initiate/hangup with Reticulum Link management
- Add StatusScreen call status display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:41:40 -05:00
torlando-techandClaude Opus 4.6 beb122606d Screen wake fix, memory monitor poll, crash breadcrumbs
- 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>
2026-02-19 16:41:32 -05:00
torlando-techandClaude Opus 4.6 de5002d347 Route NimBLE memory allocations to PSRAM
Enable CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL to move NimBLE's
internal buffer pools (~35-45KB) from internal heap to PSRAM.
Fixes heap exhaustion crash during BLE operations.

Before: free=17952 max_block=11252 (crash)
After:  free=97396 max_block=86004 at boot, stable ~38KB during operation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:26:44 -05:00
torlando-techandClaude Opus 4.6 ec181e18f8 BLE interop fixes: pools, keepalive tracking, data buffering
- 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>
2026-02-19 00:18:02 -05:00
torlando-techandClaude Opus 4.6 a50eafde41 Move Transport pools from BSS to PSRAM
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>
2026-02-18 21:18:55 -05:00
Astra b1a4f3582c Use after free fixes 2026-02-11 21:41:18 +09:00
Astra c30dba58e6 Display blackout debugging 2026-02-11 21:41:09 +09:00
Astra 66f50c91be LINKIDENTIFY inverted logic fix 2026-02-11 19:49:40 +09:00
Astra 9cecce2f0d Bump timer stack size 2026-02-11 19:49:24 +09:00
Astra 5f2c076e90 Fix SX1262 SPI Mutex
Start receive mode before releasing the mutex, otherwise it can interfere with the display since they're on the same bus
2026-02-11 19:28:56 +09:00
Astra 9740419cde Proper MTU sizes
Fixes being unable to talk over DIRECT links on LoRa
2026-02-11 19:27:51 +09:00
Astra 512cfb27ec Don't crash when no wifi network is configured 2026-02-11 14:37:39 +09:00
Astra a5a7b79e7d Don't crash on garbled announce packets 2026-02-11 13:29:09 +09:00
Torlando 74227f962b Add LICENSE file 2026-02-07 11:49:21 -05:00
torlando-techandClaude Opus 4.6 4aad90932e chore: update microReticulum submodule
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>
2026-02-07 11:12:49 -05:00
torlando-techandClaude Opus 4.6 1c5b714284 fix: increase loop task stack to 48KB for large DIRECT messages
32KB was insufficient for the deep call chain when receiving large
messages over links (transport → link → resource → LXMF unpack →
crypto → msgpack). Crashed with StoreProhibited on 935-byte messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 11:12:44 -05:00
torlando-techandClaude Opus 4.6 6ffafb9242 fix: auto-start TCP interface when WiFi connects after boot
TCP interface was never created if WiFi wasn't connected at boot time.
The settings-changed handler tried to restart it but the objects didn't
exist. Extract TCP creation into start_tcp_interface() helper and call
it from boot, settings changes, and a WiFi state-change detector in
the main loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 00:52:54 -05:00
torlando-techandClaude Opus 4.6 d0528591c0 fix: increase loop task stack to 32KB to prevent stack overflow
The 16KB stack was insufficient for deep call chains during message
delivery proof handling (transport → crypto → proof callback → app
callback → SPIFFS I/O → msgpack unpack), causing crashes every few
minutes. Also adds the stack size flag to tdeck-bluedroid env which
was missing it entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 00:41:19 -05:00
torlando-techandClaude Opus 4.6 dd361be825 fix(flasher): add esptool.js to repo for gh-pages deployment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.1.0
2026-02-06 23:31:45 -05:00
torlando-techandClaude Opus 4.6 80baf98d52 feat: auto-inject firmware version from git tags at build time
Add version.py PlatformIO pre-build script that reads version from
`git describe --tags --always` and defines FIRMWARE_VERSION as a build
flag. Local builds without tags fall back to "dev". Also rename
FIRMWARE_NAME from "microReticulum" to "Pyxis".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 23:26:09 -05:00
torlando-techandClaude Opus 4.6 e2a62e6701 fix(flasher): clarify full install is required for first-time installs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 23:00:22 -05:00
torlando-techandClaude Opus 4.6 1b2705d8fe fix(flasher): use esploader.hardReset() for reliable post-flash reset
Replace manual DTR toggle with esploader's built-in hardReset() which
uses RTS (EN pin) to properly reset the ESP32-S3 after flashing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:58:47 -05:00
torlando-techandClaude Opus 4.6 826c69695c fix(flasher): disable tracing and fix flash params to match rnode-flasher
Transport tracing and missing enableTracing flag caused timing issues
with ESP32-S3 USB-Serial/JTAG during bootloader sync. Also fixes
flashMode/flashFreq format, routes esptool output to on-page log,
and adds firmware binaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:52:49 -05:00
torlando-techandClaude Opus 4.6 8c6a6d2ee5 fix(flasher): update footer links to pyxis repo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 21:27:25 -05:00
Torlando dae8f33a16 Fix typo in README regarding propagation node sync 2026-02-06 20:21:52 -05:00
Torlando ac32d43366 Document crash issue in README
Added a note about potential crash during normal use.
2026-02-06 20:21:31 -05:00
torlando-techandClaude Opus 4.6 3dbb67f320 fix(ci): add tag_name for workflow_dispatch and make releases draft
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:11:05 -05:00
Torlando c7dd7d30ed Add explanation for 'Pyxis' in README
Added a section explaining the meaning of 'Pyxis'.
2026-02-06 20:04:10 -05:00
Torlando 69edf390a9 Update README with modified fork details
Added a note about the modified fork of microReticulum.
2026-02-06 19:59:20 -05:00
Torlando 6111ad49c1 Add new feature to README for message notification 2026-02-06 19:57:56 -05:00
Torlando ee15c6c6db Add additional features to README 2026-02-06 19:57:28 -05:00
Torlando cce6e3a685 Create README.md for Pyxis project
Add initial README with project description and features
2026-02-06 19:57:03 -05:00
torlando-techandClaude Opus 4.6 ac6ceca9f8 Initial commit: standalone Pyxis T-Deck firmware
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>
2026-02-06 19:48:33 -05:00