Meshtastic-compatible mesh radio firmware for ESP32 handheld LoRa devices.
Website and browser flasher: https://camillia.sumat.org/
- Hardware
- Supported Devices
- Features
- First-Time Setup
- Configuration
- Releases
- Use of AI
- Support
- Acknowledgments
- License
- Usage and Controls Guide (docs/USE.md)
- Build and Flash Guide (docs/BUILD.md)
- Maps (docs/MAPS.md)
- Remote Administration Terminal (docs/ADMIN_TERMINAL.md)
- Hardware Targets (docs/HARDWARE.md)
- Bluetooth Keyboards (docs/BLUETOOTH_KEYBOARDS.md)
- LilyGo T-Deck — ESP32-S3, SX1262 LoRa, 320x240 display, physical keyboard, trackball, L76K GPS
- LilyGo T-Deck Pro — ESP32-S3, SX1262 LoRa, 240x320 e-paper touchscreen, physical keyboard, MIA-M10Q GPS
- LilyGo T-Lora Pager TFT — ESP32-S3, SX1262 LoRa, 480x222 TFT, physical keyboard, roller wheel + click, GPS
- LilyGo T-Display P4 — ESP32-P4, SX1262 or LR2021 LoRa, 568x1232 RM69A10 AMOLED, GT9895 touch, ESP32-C6 wireless coprocessor, optional keyboard expansion
- M5Stack Cardputer + Cap LoRa/GPS module
- Heltec WiFi LoRa 32 V4 + TFT expansion kit (Heltec V4 expansion profile)
- Heltec WiFi LoRa 32 V4-R8 + Expansion Kit V2 — ESP32-S3, SX1262 LoRa, 8 MB PSRAM, 320x240 touch TFT, GPS, microSD
- Attaky Mesh Deck — ESP32-S3, SX1262 LoRa, 320x240 touch display, 48-key QWERTY, D-pad, GPS
- Elecrow ThinkNode M9 — ESP32-S3, LR1110 LoRa, 2.4" 320x240 display, 37-key QWERTY + d-pad, GPS, 2300 mAh
- Seeed Wio Tracker L2 — ESP32-S3, SX1262 LoRa, 320x240 touch UI, GNSS, 16 MB flash and 8 MB PSRAM
- Elecrow CrowPanel Advance 3.5" — ESP32-S3, SX1262 LoRa module, 480x320 IPS touch panel, optional UART GPS
- Elecrow CrowPanel Advance 5.0" — ESP32-S3, SX1262 LoRa module, 800x480 IPS touch panel
The T-Display P4's ESP32-C6 companion firmware is flashed through its dedicated 3.3 V UART connector and may require a USB-to-UART adapter; see docs/BUILD.md.
- LilyGo T-Deck (
tdeck): keyboard + trackball + touch input, microSD config import/export, GPS, and full mesh UI support (channels, DMs, Live, map, config, web config). - LilyGo T-Deck Pro (
tdeck-pro): e-paper touch UI with a fixed black-on-white, outline-only interface, T-Deck-compatible keyboard shortcuts, microSD config import/export, GPS, and full mesh UI support. Initial port; physical display/touch/radio validation is pending. - LilyGo T-Lora Pager TFT (
tlora-pager-tft): keyboard + roller wheel input, microSD config import/export, GPS, and full mesh UI support (channels, DMs, Live, map, config, web config). - M5Stack Cardputer + Cap LoRa/GPS (
cardputer-cap): keyboard-driven input/navigation, microSD config import/export, GPS, and full mesh UI support (channels, DMs, Live, map, config, web config). - Heltec WiFi LoRa 32 V4 + TFT expansion kit (
heltec-v4): touch-first UI in either orientation — landscape or portrait is a setting on the device (Config → Orientation), not a separate build. GPS and full mesh UI support (channels, DMs, Live, map, config, web config); no microSD slot, so config, DM history and the node archive live in internal flash. - Heltec WiFi LoRa 32 V4-R8 + Expansion Kit V2 (
heltec-r8): as above, plus a working microSD slot. 8 MB octal PSRAM. Untested on hardware — see docs/BUILD.md. - Attaky Mesh Deck (
mesh-deck): keyboard + D-pad + touch input, GPS, and full mesh UI support; the keyboard is optional (detected at boot and on hot-plug; without it the touch interface and on-screen keyboards take over); no microSD — config, DM history and the node archive live in internal flash. - Elecrow ThinkNode M9 (
m9): keyboard + d-pad input (no touch), microSD config import/export, GPS, and full mesh UI support (channels, DMs, Live, map, config, web config). The only LR1110 board in the lineup. - Seeed Wio Tracker L2 (
wio-tracker-l2): bring-up target with a touch-first 320x240 UI, optional external BLE keyboard, ES8311 sound notifications, GNSS, browser VNC Host/Remote control, and 1-bit SD_MMC storage, including firmware config import/export at/camillia/config.yaml. LP5814 brightness, ADS1115 battery, audio, SD, BLE, and Remote support still need hardware verification. - Elecrow CrowPanel Advance 3.5" + SX1262 module (
crowpanel-35), the Meshtastic bundle sold by Muzi Works: bring-up target with a touch-first 480x320 UI (portrait selectable on the device), ILI9488 + GT911, browser VNC Host/Remote, optional UART GPS on the UART1 connector, and internal-flash storage (the SD slot is not used). No battery reading. Pin map comes from Meshtastic'selecrow-adv-35-tftvariant; untested on hardware. - Elecrow CrowPanel Advance 5.0" + SX1262 module (
crowpanel-50): bring-up target with the 3.5"'s touch-first UI on an 800x480 RGB panel (portrait selectable on the device), GT911 touch, browser VNC Host/Remote, and internal-flash storage. Built for the V1.2/V1.3 boards Elecrow sells now (board MCU for the backlight, radio CS on GPIO8);-DCROWPANEL_50_HW_V10=1builds for V1.0. The board's function DIP switch must select the wireless module, which also disconnects the SD slot, so there is no SD card. No GPS or battery reading. Pin map comes from Meshtastic'selecrow-adv1-43-50-70-tftvariant and Elecrow's schematic and examples. Tested on hardware: display, touch and radio. - LilyGo T-Display P4 AMOLED (
p4-amoled-sx1262,p4-amoled-lr2021): ESP32-P4 bring-up targets with a portrait large-panel UI, RM69A10 MIPI-DSI display, GT9895 touch, the selected SX1262 or LR2021 radio, L76K GNSS, BQ27220 gauge, 4-bit SD_MMC, browser VNC Host/Remote control, and the detachable TCA8418 keyboard expansion, which can be attached or removed while running and adds F-key navigation and an adjustable backlight (see docs/USE.md). WiFi uses ESP-Hosted over the onboard ESP32-C6; the C6 image is released separately because P4 OTA cannot update it. All hardware behavior remains unverified until supported by serial logs or measurements.
Notes:
- All keyboard-specific shortcuts apply to keyboard builds (
tdeck,tdeck-pro,tlora-pager-tft,cardputer-cap, andm9) and tomesh-deckand bothp4-amoled-*builds while their detachable keyboard is present. - Environmental telemetry via BME280/BMP280/AHT20 is available on Heltec V4 expansion builds when a compatible sensor is present.
- 10 configurable LoRa channels (8 on the Cardputer) — each independently named, keyed, color-coded, and optionally given its own hop limit. Stock 8-channel Meshtastic nodes interoperate unchanged
- Direct messages — one-to-one conversations in their own tab, with delivery acks
- Live feed — real-time decoded RX/TX traffic with a per-type filter
- Tools — neighbor discovery, beacons, current weather, and an MQTT monitor
- Maps and line of sight — OpenStreetMap view of a node's last position with offline tiles (docs/MAPS.md), and terrain line-of-sight checks (docs/LOS.md)
- Remote administration — read and change another node's config from a terminal (docs/ADMIN_TERMINAL.md)
- Browser VNC — mirror and drive the device UI from a browser (all boards except the Cardputer)
- Signed packets and Store and Forward client — Meshtastic 2.8 packet signatures, and catch-up replay from an S&F router
- 13 UI themes and 22 languages (the Cardputer is English-only)
- Web configuration — browser-based settings UI, on by default, served over the device's own Wi-Fi AP or your network
- YAML config — import/export all settings and channel keys via microSD at
/camillia/config.yaml
On first boot, connect to the camillia-mt Wi-Fi access point, then open http://192.168.4.1 in a browser. Set your node name, region, and channel keys. All settings are saved to the device and persist across reboots.
Web config is enabled by default on a new device, so a freshly flashed board comes up as the camillia-mt access point. Connect to it and navigate to http://192.168.4.1. All settings (node identity, LoRa parameters, channel keys, etc.) can be configured here without reflashing. Changes persist across reboots.
Once the device joins your own Wi-Fi network it serves the full page — the same settings plus Utilities, a live feed, chat, and the node map — at the address shown on the Config screen. In access-point mode it serves Web Config Lite: the complete settings form without those extra tabs, which do not fit in the memory left once Wi-Fi is running. The Cardputer always serves Lite, and pauses chat while web config is on; see USE.md.
The Config screen's Choose WiFi list includes an AP entry that forces the device's own access point even when a network is configured, so web config stays reachable out of range. That choice persists across reboots.
Export or import a full YAML configuration file via the CFG tab. The file is read from and written to /camillia/config.yaml on the microSD card.
./scripts/release.sh cuts a release without compiling anything on your machine. It
works out the version, drafts the release notes and shows them to you, commits
them, pushes, and dispatches the release
workflow — which builds every device profile on
GitHub, merges the factory images, signs the OTA images and publishes the
.bin/.sig assets.
./scripts/release.sh # stable, prompts for the version
./scripts/release.sh --alpha -y # next alpha in the current series
./scripts/release.sh --version 4.9.0 # exactly this versionLike a local release always has, it commits your whole working tree into the
release (git add -A) — the release carries the work, not just its notes. It
lists exactly what that is and waits for confirmation first, because GitHub
builds the commit you push rather than the tree on your disk, and anything left
uncommitted would be missing from the firmware while the notes still described
it.
The notes are reviewed before anything is dispatched ([Y]es / [e]dit / [n]o).
Be aware that -y accepts them unread as well as picking the version; pass
--version instead to skip the version prompt but keep the review.
RELEASE_NOTES.md is committed rather than handed to the workflow, because the
build bakes it into the firmware so the device shows the same text the release
publishes. The workflow's notes input defaults to committed and publishes
that file verbatim; generate has the workflow write them itself, unreviewed,
and needs an ANTHROPIC_API_KEY secret.
Signing uses the OTA_SIGNING_KEY secret on the release environment. The
workflow refuses to publish if that key does not match the public key baked into
src/ota_signing_pubkey.h, since a mismatch would lock out every device in the
field. T-Deck Pro publishes the tdeck-pro factory and OTA images; T-Display P4
also publishes its matching ESP-Hosted C6 image. The release script verifies the
target list and every OTA signature before publication.
Debug symbols (.elf) are uploaded as a debug-symbols-<tag> workflow artifact
rather than bloating the release by ~35MB per profile.
Other modes:
./scripts/release.sh --notes-only # draft notes only — no commit, no dispatch
./scripts/release.sh --check-targets # validate release/OTA slugs, build nothing
./scripts/release.sh --build-local # build, sign and publish from this machine--build-local needs the full toolchain and ota_signing_key.pem present. The
workflow's own run uses that mode, recognised by GITHUB_ACTIONS rather than
the flag — on a runner the name would be a lie.
The build workflow compiles every environment from a clean checkout on pushes and PRs. It is a breakage check on ordinary work, not a release gate, and it never uploads release assets.
On-device OTA and the website flasher both track GitHub's latest release, which is why an alpha publishes as a GitHub prerelease — that is what keeps it off the stable channel.
Hello! I've been a developer professionally since about 2001 working on a large list of technologies. I've created this project in my spare time so I could contribute to my favorite new hobby (mesh networking) and try out coding with an AI partner (Claude). Lots of this code has been touched by AI but as I go through the process I'm reviewing the code. AI is tool, and like any other tool can be used well or used poorly.
This project is a bit more than a proof of concept but not something that has any commercial value. I'm doing this for fun and to learn. Feel free to contribute, use or ignore.
Camillia is free and stays free — this is a hobby project, not a product. If it has been useful to you and you feel like buying me a coffee, the tip jar is at https://buymeacoffee.com/oumike. Entirely optional, and nothing in the firmware is gated behind it.
A large thank you to wadamesh and its authors. It is genuinely good software, and it has been generous software — both in what it makes available and in how freely it has been shared. Camillia is better for it.
Concretely, wadamesh has been a reference for how these boards are meant to behave. Where this firmware and wadamesh run on the same hardware, Camillia tries to put things where wadamesh already puts them, so that anyone moving between the two is not relearning the device — the Mesh Deck's button layout and the placement of sleep/wake on it are the clearest example, and the source comments say so at the call sites.
The SD card scan is theirs in spirit too. When Elecrow disclosed that some
ThinkNode M9 cards had shipped carrying a Windows worm, wadamesh shipped SD
Scan — which finds Windows programs, scripts, shortcuts and autorun files on a
card and removes them once you confirm. Camillia's M9 scan follows the same
reasoning, including the idea of looking for a real MZ program header rather
than trusting file extensions. If you want a tool built for the job rather than
a feature bolted onto a mesh radio, go and use theirs.
wadamesh is GPL-3.0, the same license family this project uses.
Thanks also to the Meshtastic project, and to the maintainers of the libraries this firmware stands on — LVGL, RadioLib, Arduino-ESP32, LovyanGFX and nanopb among them.
Camillia for Meshtastic is released under the GNU General Public License v3.0 or later
(SPDX-License-Identifier: GPL-3.0-or-later).
Copyright © 2025–2026 Michael A. Cojocari and contributors.
This firmware is Meshtastic-compatible and links against, or interoperates with, portions of the Meshtastic project (also GPLv3), so it is distributed under the same license. There is no warranty; see the full license text for details. The full license text accompanies this source distribution in LICENSE; you can also obtain a copy at https://www.gnu.org/licenses/gpl-3.0.html.
Third-party libraries used by this project (e.g. LVGL, RadioLib, Arduino-ESP32, TFT_eSPI / LovyanGFX, nanopb) remain under their respective licenses; see each library's source for terms.