Commit Graph

28 Commits

Author SHA1 Message Date
Torlando 70b8df052d Merge pull request #12 from torlando-tech/feature/splash-screen
Add boot splash screen with Pyxis constellation logo
2026-03-04 18:38:43 -05:00
torlando-tech ff00c1d783 Clean up splash preprocessor structure and add include warning
Consolidate #ifndef/#ifdef into single #ifdef/#else/#endif block.
Add warning comment to generated header about static linkage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:19:49 -05:00
torlando-tech bae59ff424 Fix unused BG_COLOR warning and make show_splash() private
Move BG_COLOR inline into #ifndef block to avoid unused variable
when HAS_SPLASH_IMAGE is defined. Make show_splash() private since
it's only called internally from init_hardware_only().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:48:38 -05:00
torlando-tech 5b2a1ab53e Skip redundant fill_screen when full-screen splash image is available
Saves one full 320x240 SPI screen write before the splash renders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:09:49 -05:00
torlando-tech 2a1b98f8f1 Fix _initialized never set in Display::init()
Prevents double PSRAM allocation and LVGL driver re-registration
if init() were called more than once.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:39:49 -05:00
Torlando 2f61b80567 Update lib/tdeck_ui/Hardware/TDeck/Display.cpp
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-04 14:20:44 -05:00
torlando-tech a4a1aacdd8 Show boot splash within 1s of power-on instead of after 20s+ init
Move Display::init_hardware_only() and POWER_EN to right after serial
banner, before GPS/WiFi/SD/Reticulum init. Add 150ms delay after
POWER_EN HIGH so ST7789V power rail stabilizes before SPI commands
(without this, SWRESET is sent to an unpowered chip and silently lost).

Splash now visible for entire boot period (~18s) until LVGL takes over.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:12:25 -05:00
torlando-tech 0608af6d38 Unify all SPI peripherals on global FSPI to fix pin conflicts
Display and LoRa were creating separate SPIClass(HSPI) instances which
claimed GPIO pins via the matrix, preventing SD card (on FSPI) from
accessing MISO after Display init. Now all three peripherals use the
global SPI (FSPI) instance, eliminating GPIO routing conflicts.

- Display: use &SPI instead of new SPIClass(HSPI)
- SX1262Interface: use &SPI instead of new SPIClass(HSPI)
- SDAccess: enable format_if_empty for unformatted cards

Verified on device: SD (128GB SDHC), display, and LoRa all coexist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:41:35 -05:00
torlando-tech b4afa6d3f7 Fix SD card SPI init: use FSPI before Display claims HSPI
SD card was unresponsive (MISO stuck 0xFF) because Display's HSPI
peripheral had already claimed the GPIO pins via the matrix, preventing
FSPI from routing MISO. Fix by initializing SD card BEFORE Display,
using the global SPI (FSPI) instance — matching LilyGo's reference code.

- Move SD card init before display init in boot sequence
- Use global SPI (FSPI) instead of Display's SPIClass(HSPI)
- Lower SPI frequency to 800kHz matching LilyGo example
- Drive all CS lines (display, LoRa, SD) high before SD init
- Add MISO=38 to Display's SPI.begin for post-init bus sharing
- Add Display::get_spi() accessor for future shared use

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:29:47 -05:00
torlando-tech d03f0b308f Add shared SPI bus mutex for SD card, display, and LoRa coexistence
The T-Deck Plus shares HSPI across the display (CS=12), LoRa (CS=9),
and SD card (CS=39). Previously SD logging was disabled because
SD.begin() reconfigured the SPI bus and blanked the display.

This introduces a FreeRTOS mutex created in main.cpp and injected into
Display, SX1262Interface, and a new SDAccess class so all three
peripherals serialize their SPI transactions safely.

- Add SDAccess class wrapping SD.begin() and file ops with mutex
- Add set_spi_mutex() to Display and SX1262Interface
- Wrap Display flush, fill, draw, and power ops in mutex
- Refactor SDLogger to use SDAccess mutex instead of owning SD.begin()
- Wire up mutex creation and injection order in setup()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 00:19:10 -05:00
Torlando d9411fb4bb Merge pull request #7 from torlando-tech/ble-stability-audit
BLE stability: fix desync crash loops and scan recovery
2026-03-03 23:39:38 -05:00
torlando-tech 4f22776971 Add tone as explicit dependency of tdeck_ui
UIManager.cpp includes Tone.h, so tdeck_ui should declare this
dependency rather than relying on implicit global discovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:05:42 -05:00
davidcranor 827ff2eb42 Fix cross-platform build: replace ${PROJECT_DIR} with relative paths
platformio.ini:
- Replace -I${PROJECT_DIR}/lib, -I${PROJECT_DIR}/deps/... with relative
  paths (-Ilib, -Ideps/...) in both tdeck-bluedroid and tdeck environments;
  ${PROJECT_DIR} is mangled on Windows inside build_flags, causing include
  paths to resolve inside the PlatformIO builder directory instead of the
  project root
- Remove hardcoded -I.pio/libdeps/tdeck/TinyGPSPlus/src and
  -I.pio/libdeps/tdeck/NimBLE-Arduino/src; these paths reference generated
  cache, break on fresh clones, and are redundant with lib_ldf_mode = deep+
- Fix OTA upload_command: replace python3 with $PYTHONEXE so it resolves
  to PlatformIO's bundled Python on Windows, macOS, and Linux

src/main.cpp, lib/tdeck_ui/UI/LXMF/UIManager.cpp:
- Change #include "tone/Tone.h" to #include "Tone.h"; PlatformIO
  automatically adds -Ilib/tone for local libraries, making the
  subdirectory prefix unnecessary and broken when -Ilib is not effective

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:19:32 -05:00
torlando-tech 8d23c03e3b Fix conversation list showing hashes instead of display names after restart
After boot, the conversation list called recall_app_data() once during
initial load. If announces hadn't arrived yet (or known destinations
hadn't been loaded with app_data), conversations showed raw hashes
permanently until the user navigated away and back.

Add a lazy name resolution check to update_status() (called every 3s):
if any conversations have unresolved names, try recall_app_data() again
and refresh the list when a display name becomes available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:24:30 -05:00
torlando-tech 609a3bc62b LXMF propagation sync, manual node entry, and status improvements
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>
2026-03-02 23:03:32 -05:00
torlando-tech 6744eb136d LXST voice call stability: fix hangup crash, signal queue, TX pump, mic tuning
- Fix use-after-free crash on hangup: set _call_state=IDLE before deleting
  _lxst_audio, preventing pump_call_tx() (runs without LVGL lock) from
  accessing freed memory
- Replace single-slot _call_signal_pending with 8-element ring buffer queue
  to prevent signal loss when CONNECTING+ESTABLISHED arrive in rapid succession
- Extract TX pump into pump_call_tx() called right after reticulum->loop()
  for low-latency audio TX without LVGL lock dependency (was buried at step 10)
- Tune ES7210 mic gain to 21dB (was 15dB) to improve Codec2 input level
  without ADC clipping that occurred at 24dB
- I2S capture: use APLL for accurate 8kHz clock, direct 8kHz sampling
  (no more 16→8kHz decimation), DMA 16x64 for encode burst headroom
- Reduce Reticulum log verbosity to LOG_INFO (was LOG_TRACE)
- BLE: add ble_hs_sched_reset() tiered recovery before reboot on desync,
  widen supervision timeout to 4.0s for WiFi coexistence
- Add UDP multicast log broadcasting and OTA flash support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:57:14 -05:00
torlando-tech ddd19a04db Fix LXST TX audio wire format to match Columba's expected batch size
Columba's native OboePlaybackEngine ring buffer expects exactly
frameSamples (1600 for Codec2 3200 mode) decoded samples per
writeEncodedPacket call = 10 sub-frames of 160 samples each.

Changes:
- Batch exactly 10 sub-frames per fixarray element (82 bytes each:
  codec_type + mode_header + 10*8 raw bytes)
- Up to 2 batches per msgpack packet, matching Columba C2C format
- Proper fixarray wrapping for multi-batch, bare bin8 for single
- Add codec_type byte (0x02) prefix per batch element
- Respond to PREFERRED_PROFILE negotiation with LBW (Codec2 3200)
- Add capture diagnostics (raw PCM peaks, I2S dump, rate logging)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:57:14 -05:00
torlando-tech 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-tech 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-tech 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-tech 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-tech 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-tech 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-tech 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-tech 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-tech 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
Astra c30dba58e6 Display blackout debugging 2026-02-11 21:41:09 +09:00
torlando-tech 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