[env:tdeck] extra_scripts = pre:version.py pre:generate_splash.py pre:patch_nimble.py pre:patch_msgpack.py ; Mirror an explicit local microReticulum override before patch scripts run. ; PIO does not reliably refresh file dependencies, and a newly fetched stale ; dependency can have a newer mtime than the local corrected checkout. pre:sync_file_libdeps.py ; patch_filestore.py silences upstream microStore's spammy ; "[ustore] get: key not found in index" print (fires on every ; path-store miss; saturated USB CDC during LXST calls). Set ; PYXIS_FILESTORE_DIAG=1 to also enable exists()/put() diagnostic ; prints when investigating path-store drift. pre:patch_filestore.py ; patch_littlefs_paths.py normalizes microStore's LittleFS adapter paths to ; a leading "/" — ESP32 Arduino LittleFS rejects "./"-prefixed paths, which ; broke the path store (no peer paths learned). See the script header. pre:patch_littlefs_paths.py platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino ; T-Deck Plus has ESP32-S3 with 8MB Flash + 8MB PSRAM board_build.flash_mode = qio board_build.partitions = partitions.csv board_build.arduino.memory_type = qio_opi board_upload.flash_size = 8MB ; Enable PSRAM (required for LVGL buffers) board_build.arduino.psram_type = opi ; Serial monitor monitor_speed = 115200 monitor_filters = esp32_exception_decoder time ; Library dependency finder mode (deep search) ; deep+ scans #include statements and auto-fetches matching libs from the ; registry — that pulled a parallel copy of microReticulum into ; .pio/libdeps/tdeck/microReticulum/ alongside our deps/microReticulum/ ; overlay, and the linker picked the registry copy (silently dropping ; all our local .cpp changes). chain+ follows only explicit dependencies ; declared in library.json, so deps/microReticulum (via lib_extra_dirs) ; is the only microReticulum source. lib_ldf_mode = chain+ ; Build type build_type = release lib_deps = ; Install the immutable microStore pin before microReticulum. Its upstream ; metadata declares an unconstrained registry microStore dependency; if ; microReticulum resolves first, PlatformIO can populate the shared library ; name with a newer registry package while misleadingly labeling the graph ; as this VCS pin. https://github.com/attermann/microStore.git#c5fb69d68229e684c7fbd17692a67ae8193b84e2 ; Explicit git dep so PIO links exactly ONE microReticulum (the ; live source). lib_extra_dirs was creating duplicate compilations ; — PIO compiled deps/microReticulum/ as one library AND auto- ; fetched the same source as another, leaving two copies of ; Transport::_path_store / _new_path_table in BSS. Different TUs ; ended up linking against different statics, so put() and ; exists() landed in different stores. ; ; Pinned to torlando-tech/microReticulum:pyxis-fixes-on-0.3.0 at ; SHA 3ee2bd8 — vanilla 0.3.0 + 4 PR-ready Cryptography + 1 link- ; proof + Resource progress-callback firing fixes. Bump SHA in ; tandem with conformance-bridge/CMakeLists.txt's FetchContent ; tag if either side updates. ; microReticulum: torlando-tech/microReticulum:pyxis-fixes-on-0.4.1 — ; upstream attermann/microReticulum master (0.4.1+16) + the small ; still-needed fixes: PKCS7/HMAC/X25519 crypto (proven byte-identical to ; python RNS 1.3.1), Packet link-proof callback, Identity short-sig guard, ; and the bz2 layer + decompress-on-receive in Resource::assemble(). The ; far-diverged 0.3.0 fork's Resource/Transport/Identity work is subsumed by ; upstream's reimplementation. Bump in lockstep with microLXMF below. ; cb71ace: OS::ltime() now uses the monotonic 64-bit esp_timer instead of a ; racy 32-bit millis() rollover counter (static low32/high32 raced across ; FreeRTOS tasks, inflating OS::time() by N*49.7 days — garbage convo ; timestamps, e.g. "Future"/"49w ago"/year-2046 now). ; 97fecfe: Identity::recall now accounts for the optional ratchet when ; extracting announce app_data (matches validate_announce). Without it, ; ratchet-bearing announces (Sideband/Columba) had app_data read 32 bytes ; too early, garbling peer display names ("ySId!M@pB`Z:ev"-style prefix). ; 3885b90: DestinationEntry caps persisted random_blobs to 16 (upstream defines ; PERSIST_RANDOM_BLOBS but never enforced it) — the unbounded set pushed path ; entries past microStore's 1024B value cap so they were rejected (empty path ; table), and under a raised cap bloated the FS until compaction crashed. ; 6054f6b: mirrors learned paths into the in-memory _path_table so path_table() ; (the UI announce list + T:PATHS) can enumerate them. *** STOPGAP ***: upstream ; attermann/microReticulum is mid-migration to the microStore-backed ; _new_path_table (no enumeration API) and left path_table()'s source ; (_path_table) unpopulated. When bumping past upstream finishing that migration ; (_path_table removed / a for_each API added), DELETE the mirror — see the long ; comment at the _path_table mirror in Transport::inbound for details. ; 6ac0d32: initializes endpoint path storage and bounds request responses, ; including Resource advertisements and streaming bzip2 decompression. https://github.com/torlando-tech/microReticulum.git#6ac0d3232cf705d538f9c556f0f82d2d2cc753bc ; microLXMF: chore/microreticulum-0.4.1-layout — includes namespaced to ; for the 0.4.x src/microReticulum/ layout. ; 3cdde79: faster load_message_metadata — single LittleFS open (read_file ; returns 0 on miss, no separate file_exists probe) + JSON field filter that ; skips the large "packed" blob. ~2x quicker conversation load. ; aad87f5: transactional message persistence and rollback recovery, keeps the ; rollback snapshot off the LVGL task stack, bounds-checks empty LXMF fields, ; and attaches delivery callbacks to the transport-owned PacketReceipt. ; 5a44862: 4c81297 plus persist-only-after-preparation semantics and ; serialized native bridge transport callbacks/conformance closure. https://github.com/torlando-tech/microLXMF.git#5a44862b85e902f351b756ee00482f3258b244f5 lvgl/lvgl@^8.3.11 bblanchon/ArduinoJson@^7.4.2 hideakitai/MsgPack@^0.4.2 rweather/Crypto@^0.4.0 mikalhart/TinyGPSPlus@^1.0.3 jgromes/RadioLib@^6.0 WiFi SPI Wire SD FS LittleFS tdeck_ui sx1262_interface tone auto_interface h2zero/NimBLE-Arduino@^2.1.0 ble_interface lxst_audio sh123/esp32_codec2@^1.0.7 ; carries modern codec2 since PR #4 (Jan 2026); -D__EMBEDDED__ + -DMEMORY_CRITICAL set in build_flags below put codebooks in flash libbz2 ; Build configuration build_flags = -std=gnu++17 -DBOARD_HAS_PSRAM -DBOARD_ESP32 ; Arduino loop task stack — Codec2 decode (lpc_post_filter + kiss_fft) ; uses ~6KB stack on top of ~10KB normal peak. 24KB gives safe headroom. -DARDUINO_LOOP_STACK_SIZE=24576 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 -DLV_CONF_INCLUDE_SIMPLE -Ilib -Ilib/libbz2 ; -Ideps/microReticulum/src — removed in favor of PIO's lib_deps ; auto-include of .pio/libdeps/tdeck/microReticulum/src. Keeping ; the hardcoded -Ideps/... path here would shadow PIO's path ; (build_flags are searched first) and let the submodule's stale ; headers win over the freshly-fetched git pin from lib_deps. ; ; -Ilib/microreticulum-shim alone is enough — subdir lookups use , ; , etc. Don't add -Ilib/microreticulum-shim/Utilities on top: ; that puts our Stream.h on the GLOBAL header path and breaks Arduino's ; Wire.cpp (`class TwoWire: public Stream` finds ours first). -Ilib/microreticulum-shim -DBZ_NO_STDIO ; codec2 codebooks in flash (.const) instead of RAM — without this ; ~127KB of codebook tables land in BSS and compete with LVGL's ; framebuffer. Moved here from lib/codec2/library.json when we ; swapped the vendor for sh123/esp32_codec2_arduino@1.0.7 ; (which carries modern codec2 since their PR #4 / Jan 2026). -D__EMBEDDED__ -DMEMORY_CRITICAL ; Move microReticulum allocations from internal DRAM (~320KB total) ; to PSRAM (~8MB on T-Deck Plus). The default `::operator new` on ; ESP32 prefers internal SRAM and would otherwise compete with LVGL ; framebuffers, BLE/WiFi, etc — under heavy announce flood we saw ; the path table fill internal heap and "Failed to add destination ; to path table" / "PropagationNodeManager: Pool full" spam. ; RNS_PSRAM_ALLOCATOR routes default new/delete to ps_malloc; ; RNS_PSRAM_POOL_ALLOCATOR routes long-lived STL containers (path ; table) into a dedicated 2MB TLSF pool in PSRAM. -DRNS_DEFAULT_ALLOCATOR=RNS_PSRAM_ALLOCATOR -DRNS_CONTAINER_ALLOCATOR=RNS_PSRAM_POOL_ALLOCATOR -DRNS_PSRAM_POOL_BUFFER_SIZE=2048000 ; Enable filesystem-backed path persistence in microReticulum. ; Without these, Transport::start() skips _path_store.init(), and ; every announce-driven _new_path_table.put() bails silently at ; TypedStore::isValid() → "Failed to add destination to path table" ; spam. With LittleFS as the backend (USTORE_USE_LITTLEFS) the ; sustained-write pattern is tractable. -DRNS_USE_FS -DRNS_PERSIST_PATHS ; microStore LittleFS adapter — replaces pyxis's pre-graft ; lib/universal_filesystem/ (which targeted SPIFFS directly). ; LittleFS reuses the existing partition labeled "spiffs" (LittleFS's ; default partitionLabel is "spiffs" too) and reformats it on first ; boot. SPIFFS was swapped out because its GC-induced flush stalls ; caused FileStore::put → flush_buffer() to fail under sustained ; announce writes (~3/sec), surfacing as path-table-add error spam. -DUSTORE_USE_LITTLEFS ; Cap the path-store live record count (defense-in-depth). microReticulum now ; bounds each entry's random_blobs (the actual leak that filled the FS), but this ; also bounds the TOTAL live set so the compaction output segment (path_store_0) ; cannot bloat the 1.875MB partition over long uptime — prune_index runs inside ; put() and compact(). 400 records x ~560B ~= 225KB, far from full. -DUSTORE_DEFAULT_MAX_RECS=400 -DUSE_NIMBLE -DCONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=1 -Os -DCORE_DEBUG_LEVEL=2 ; Enable memory instrumentation (heap/stack monitoring) ; Remove this flag to disable instrumentation and eliminate overhead -DMEMORY_INSTRUMENTATION_ENABLED ; Enable boot profiling (timing instrumentation for setup phases) ; Remove this flag to disable boot profiling -DBOOT_PROFILING_ENABLED ; Reduce log verbosity during boot for faster startup ; CORE_DEBUG_LEVEL=2 (WARNING) reduces INFO-level log output -DBOOT_REDUCED_LOGGING ; Production release environment. Keep the exact hardware/dependency contract ; from tdeck while removing test commands, private test-endpoint macros, and ; runtime instrumentation. The instrumentation headers compile their call ; sites to no-ops when these flags are absent. [env:tdeck-release] extends = env:tdeck build_unflags = -DPYXIS_TEST_HOOKS -DPYXIS_TEST_TCP_HOST -DPYXIS_TEST_TCP_PORT -DMEMORY_INSTRUMENTATION_ENABLED -DBOOT_PROFILING_ENABLED ; Instrumented physical-test firmware. Production/release builds use `tdeck` ; and contain neither test-only macros nor the serial T: command surface. [env:tdeck-test] extends = env:tdeck build_flags = ${env:tdeck.build_flags} -DPYXIS_TEST_HOOKS '-DPYXIS_TEST_TCP_HOST="${sysenv.PYXIS_TEST_TCP_HOST}"' '-DPYXIS_TEST_TCP_PORT="${sysenv.PYXIS_TEST_TCP_PORT}"' ; OTA flashing environment (wireless upload via ArduinoOTA) ; Usage: pio run -e tdeck-ota -t upload ; The old upload_command pointed at tools/espota.py (which does not exist in the ; repo); use PlatformIO's built-in espota uploader instead. ; NOTE: espota resolves the host via gethostbyname, which on macOS does NOT do ; mDNS, so the .local default may fail to resolve -- if upload can't find the ; host, pass the device's LAN IP explicitly: ; pio run -e tdeck-ota -t upload --upload-port [env:tdeck-ota] extends = env:tdeck upload_protocol = espota upload_port = pyxis-tdeck.local upload_flags = --port=3232