Status: Beta testing — several users have flashed successfully. See Known Issues below.
Standalone off-grid LoRa mesh messaging firmware for the LilyGo T-Deck (ESP32-S3 + SX1262 + ST7789 240×320 TFT touchscreen + physical QWERTY keyboard).
Built on the MeshCore mesh networking protocol — fully interoperable with existing MeshCore repeaters, room servers, and companion radios.
Full credit to the MeshCore Dev team! I won't ever accept any money or donations for this project but if you wish to put your money to good use, and not the AI hivemind, then sponsor https://github.com/meshcore-dev/MeshCore
# Run the native host-side suite (no hardware needed)
pio test -e native_test
# Run a specific test module
pio test -e native_test -f test_battery
# Build the firmware image for LilyGo T-Deck
pio run -e SigurdOS_TDeck| Test Module | What's Covered |
|---|---|
test_battery |
Battery level monitoring, ADC reading, voltage conversion, percentage calculation |
test_build |
All headers compile together, cross-module API consistency |
test_build_info |
Firmware version string, git SHA, build environment metadata |
test_buzzer |
Buzzer notification patterns, tone durations, active-low control |
test_channel_menu |
Channel quick-action menu, private scope handling, leave/join logic |
test_channel_validation |
Channel name validation, allowed/hyphenated/mixed-case names, rejection rules |
test_chat_config |
Chat screen message capacity, normalization, clamp boundaries |
test_chat_truncation |
Chat message truncation, long text handling |
test_companion_protocol |
Companion device protocol frames, sync, message enqueue/drain |
test_contact_paging |
Contact list pagination, page count, start/end calculations |
test_contact_store |
Contact persistence format, magic header, version/bounds checks |
test_controller |
RF parameter parsing, frequency/SF/bandwidth/coding rate validation |
test_debug |
Debug level clamping, serial debug command stubs, non-debug build guards |
test_emoji |
Emoji font rendering, fallback logic, sizing |
test_emoji_fallback |
Emoji wrapped font pointers, writable copies, LVGL fallback chain |
test_emoji_integrity |
Emoji font glyph count, indexed entry presence, uniqueness |
test_github_ota_contract |
GitHub OTA state machine, API signatures, release URL generation |
test_gps |
NMEA parsing, coordinate conversion, fix detection |
test_hal_contract |
HAL display/gps lifecycle signatures, debug capture APIs, type stability |
test_home_screen |
Home screen layout, icon grid, status bars |
test_input_contract |
Trackball/keyboard input event types, remote hook signatures |
test_keyboard |
Matrix scan, keymap, debounce, ghost detection, LVGL mapping |
test_launcher_env |
Launcher runtime detection, partition/otadata probing, false-positive guards |
test_layout |
Adaptive layout helpers, responsive grid calculations, screen size adjustments |
test_lodepng_alloc |
lodepng PSRAM allocator, malloc/realloc fallback, heap caps delegation |
test_log |
Logging macro level prefixes, debug-build compile-time gating |
test_map |
Tile math, zoom levels, bounding box |
test_map_renderer |
Map tile mercator math, zoom validation, lat/lon/tile round-trip |
test_mesh_contract |
Mesh advert types, contact flags, login status, wire format stability |
test_mesh_messaging |
Message queue, send/receive, channel ops, contact export |
test_mesh_wrapper |
API signatures, return value ranges, unread count init |
test_message_store |
Message storage append/load/dedup, path length, ring rotation |
test_navigation |
Forward/back with history stack, deep nav chains, all pairs |
test_navigation_contract |
Screen enum inventory, contiguous values, stable position checks |
test_onboarding |
Onboarding wizard date/time validation, leap year, days-in-month |
test_pins |
GPIO ranges, SPI/I2C bus conflicts, duplicate detection, LoRa params |
test_prefs |
NVS preferences, radio config persistence, identity storage, save/load |
test_prefs_defaults |
NVS preference default values, radio/identity/mesh behavior defaults |
test_qr_show |
QR code layout, version sizing, canvas fitting, scale calculation |
test_regions |
Geographic region registry, add/find/remove, prefix search |
test_responsive |
Responsive column offsets, weight distribution, edge cases |
test_sdcard |
SPI init, mount, read/write, directory listing, edge cases |
test_tdeck_board |
TDeckBoard power management, auto-shutdown threshold, voltage critical levels |
test_telemetry_collectors |
Telemetry task watermark collection, null/zero capacity validation |
test_telemetry_crash |
Crash backtrace ring buffer, bounded count clamping |
test_telemetry_hb_ring |
Heartbeat ring buffer, read/write, wrap-around, logical indexing |
test_telemetry_input |
Telemetry input sampling, invalid trackball direction rejection |
test_telemetry_packet_log |
Packet log field formatting, null/empty string handling |
test_telemetry_protocol |
Telemetry record emission, signed/unsigned/float/string fields |
test_terminal |
Terminal buffer management, command parsing |
test_theme |
Color darkness, vibrancy, distinctness, readability hierarchy |
test_touch |
GT911 coordinate mapping, multitouch parsing, press/release lifecycle |
test_trackball |
Direction debounce, deadtime, click detection, idle calibration |
test_ui_contract |
UI screen show APIs, home/detail/settings screen signature stability |
test_ui_timing |
UI splash screen timing, transition elapsed checks |
test_wifi_scan |
WiFi network scanning, connection management, AP-mode OTA |
Full test documentation: test/README.md
| Component | Detail |
|---|---|
| MCU | ESP32-S3, 240 MHz, 16 MB Flash, 8 MB PSRAM |
| Display | ST7789 240×320 TFT (landscape via rotation) |
| Touch | GT911 capacitive (I2C) |
| Keyboard | Physical QWERTY (I2C, ESP32-C3 MCU) |
| LoRa | SX1262 (SPI) |
| GPS | Serial1 (optional) |
| SD Card | SPI (shared bus) |
SigurdOS-tdeck/
├── firmware/ ← Pre-built merged binaries (flash at 0x0)
├── lib/meshcore/ ← Git submodule: MeshCore protocol (routing, radio, encryption)
├── src/
│ ├── main.cpp ← Boot sequence (board → display → mesh → UI)
│ ├── lv_conf.h ← LVGL v9 config (16-bit, partial render)
│ ├── utils/
│ │ └── utf8_util.h ← UTF-8 string utilities (validation, truncation)
│ ├── hal/
│ │ ├── tdeck_pins.h ← Complete T-Deck pinout + version string
│ │ ├── tdeck_board.h ← TDeckBoard :: mesh::MainBoard
│ │ ├── display.cpp/h ← LovyanGFX ST7789 + LVGL driver
│ │ ├── trackball.cpp/h ← 5-direction trackball (debounce, event queue)
│ │ ├── battery.cpp/h ← ADC battery (mV + %)
│ │ ├── touch.cpp/h ← GT911 touch controller (I2C)
│ │ ├── keyboard.cpp/h ← I2C keyboard (ESP32-C3 MCU)
│ │ ├── gps.cpp/h ← NMEA GPS parser (Serial1)
│ │ ├── sdcard.cpp/h ← microSD card (SPI, shared bus)
│ │ ├── buzzer.cpp/h ← Active-low buzzer control (GPIO 46)
│ │ ├── prefs.cpp/h ← NVS preferences (radio config, identity, WiFi OTA)
│ │ ├── wifi_ota.cpp/h ← AP-mode OTA upload server
│ │ └── github_ota.cpp/h ← GitHub-release OTA downloader
│ ├── mesh/
│ │ ├── mesh_wrapper.cpp/h ← SX1262 radio init, RTC, mesh API
│ │ ├── message_store.cpp/h ← Shared persistent message store (dedup, ACK flags)
│ │ ├── contact_store.cpp/h ← Versioned contact persistence
│ │ ├── regions.cpp/h ← Region map / flood-scope support
│ │ └── sigurd_mesh_v2.cpp/h ← SigurdMeshV2 : BaseChatMesh subclass
│ ├── comms/
│ │ └── companion_bridge.cpp/h ← BLE companion protocol (official MeshCore app)
│ ├── diagnostics/
│ │ ├── log.h ← SIG_LOG* logging macros
│ │ ├── debug.cpp/h ← Debug dumps (SIGURDOS_DEBUG builds)
│ │ └── telemetry*.cpp/h ← Structured telemetry (heartbeat ring, crash capture)
│ ├── app/
│ │ ├── map_renderer.cpp/h ← Offline map tile renderer (PNG, PSRAM canvas)
│ │ ├── tile_cache.cpp/h ← Tile caching layer (LRU, PSRAM)
│ │ └── qr_show.cpp/h ← QR code display for contact sharing
│ ├── fonts/
│ │ ├── emoji_font.c/h ← Emoji font glyph definitions
│ │ ├── emoji_font_setup.cpp ← LVGL emoji font integration
│ │ ├── emoji_data.cpp/h ← Emoji unicode character tables
│ │ └── emoji_images/ ← Emoji picker image assets
│ └── ui/
│ ├── theme.cpp/h ← Discord-inspired dark palette
│ ├── responsive.h ← Adaptive layout helpers (bars, grids, dialogs)
│ ├── home_screen.cpp/h ← 4×3 icon grid + top/bottom bars
│ ├── chat_screen.cpp/h ← Discord-like chat (channels, bubbles, input)
│ ├── screens_common.cpp/h ← Shared screen chrome (make_screen_full, PIN gate)
│ ├── screens.cpp/h ← Slim dispatch shim + shared declarations
│ ├── screens/ ← One module per screen (screen_map, screen_settings_*, …)
│ ├── onboarding_screen.cpp/h ← First-boot setup wizard
│ ├── navigation.cpp/h ← Screen routing with animations
│ └── ui.cpp/h ← Splash → Home transition
├── boards/t-deck.json ← PlatformIO board definition
├── platformio.ini ← Build config (ESP32-S3 + LVGL + MeshCore)
└── test/ ← Native test suite and mocks
- PlatformIO (VS Code extension or CLI)
- LilyGo T-Deck with USB-C cable
Install everything from a PowerShell terminal:
# 1. Git
winget install Git.Git
# 2. Python 3.12
winget install Python.Python.3.12
# 3. PlatformIO CLI
pip install platformio
# 4. CP210x USB driver (for T-Deck USB-to-UART)
# Download from: https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers
# Unzip → right-click silabser.inf → Install
#
# Verify: Device Manager → Ports (COM & LPT) → "Silicon Labs CP210x USB to UART Bridge"Restart your terminal after installing Python, then verify:
git --version
python --version
pio --version# Ubuntu/Debian
sudo apt install git python3 python3-pip
pip install platformio
# Arch
sudo pacman -S git python python-pip
pip install platformioNo USB driver needed on Linux — the CP210x kernel module ships with the kernel.
# Homebrew
brew install git python platformioNo USB driver needed on macOS — the CP210x driver is built into the OS.
git clone --recurse-submodules https://github.com/hermes-gadget/SigurdOS-tdeck.git
cd SigurdOS-tdeckIf lib/meshcore/ is empty after clone, run:
git submodule update --init --recursivepio run -e SigurdOS_TDeckFirst build downloads the ESP32-S3 toolchain (~800 MB). Subsequent builds are fast.
Put the T-Deck in download mode: hold the trackball button while plugging in USB (or hold BOOT + tap RESET). The screen stays black — that's correct.
pio run -e SigurdOS_TDeck -t uploadpio device monitor -b 115200After cloning, these files must exist or the build will fail:
| File | Purpose |
|---|---|
boards/t-deck.json |
Board definition (16 MB flash, QIO, ESP32-S3) |
lib/meshcore/src/Mesh.h |
MeshCore submodule (must not be empty) |
platformio.ini |
Build configuration |
Pre-built merged binaries are in firmware/. Flash directly with esptool — no PlatformIO needed:
pip install esptool
esptool.py --chip esp32s3 --port COM21 --baud 921600 \
--before default_reset --after hard_reset write_flash \
--flash_mode qio --flash_freq 80m --flash_size 16MB \
0x0 firmware/sigurdos-tdeck-merged.binSee firmware/README.md for details.
All screens from the SigurdOS T-Deck UI, captured from a live device running the production firmware build.
GPL-3.0-or-later
This project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Dependencies remain under their original licenses (MIT, FreeBSD, LGPL-2.1, zlib/libpng, BSD-3-Clause) — see Open Source Acknowledgments below for the full audit.
This project builds on and incorporates open source software from the following projects:
| Project | License | Usage in SigurdOS |
|---|---|---|
| MeshCore | MIT | Mesh networking protocol (submodule at lib/meshcore/). Also: RTC clock (ESP32RTCClock), auto-off display timer, deep sleep patterns, and NodePrefs struct — all adapted from MeshCore's companion radio firmware. |
| LilyGo T-Deck Keyboard_ESP32C3 | MIT | I2C keyboard protocol reference — our keyboard.cpp driver is based on the command set and keymap from this firmware (© 2023 Shenzhen Xin Yuan Electronic Technology Co., Ltd) |
| LVGL | MIT | Embedded GUI framework (v9.3.0) |
| LovyanGFX | FreeBSD | Display driver for ST7789 TFT |
| RadioLib | MIT | SX1262 LoRa radio driver |
| Adafruit BusIO | MIT | I2C/SPI bus abstraction for sensor/display drivers |
| Arduino Crypto | MIT | AES/SHA for MeshCore packet encryption |
| Google Test | BSD-3-Clause | Unit testing framework |
| ed25519 | zlib/libpng | Embedded Ed25519 crypto (Orson Peters) — bundled in MeshCore at lib/meshcore/lib/ed25519/ |
| ESP32 Arduino Core | LGPL-2.1 | ESP32-S3 hardware abstraction and Arduino framework (LGPL→GPLv2+ bridge compatible) |
| PlatformIO | Apache 2.0 | Build system (not linked into firmware) |

















