Repository navigation
Explaining the project
Launcher is a small resident firmware that manages other firmware images on ESP32 devices. It is not only an app picker. It also understands firmware layouts, partition tables, SD card storage, WebUI uploads, online firmware metadata, serial control, and device-specific input/display differences.
The current project is built with PlatformIO, Arduino framework, and the pioarduino ESP32 platform based on ESP-IDF 5.5 libraries. Board support lives under boards/, shared logic lives under src/, and shared libraries are pulled in as git submodules under lib/.
Launcher is installed in its own protected app partition. When the device powers on, Launcher shows a start screen. If you press the select/enter button, it opens the Launcher menu. If you do nothing and Boot to Launcher is disabled, Launcher starts the selected installed app.
The start screen also draws the installed apps as cards. Touching a card, or pressing its digit shortcut on a keyboard device, boots that app immediately without going through the main menu.
When more than one app is installed, Launcher can show the installed apps as menu items. Selecting one opens actions to launch, rename, back up, or delete that app.
Launcher is split into these major areas:
- Boot and app registry (
app_registry.cpp): tracks installed OTA app partitions, stores display names in NVS, links apps to their data partitions and backups, and selects which app should boot. - Installer (
install_shared.cpp,partition_install_layout.cpp): writes app images, SPIFFS/LittleFS data, and FAT data to the right partitions, and picks the install layout. - Partition manager (
partitioner.cpp,partition_table_model.cpp): reads, validates, edits, compacts, backs up, restores, and writes ESP32 partition tables. - Backup manager (
backup_manager.cpp): stores data partition backups on the SD card and keeps the backup-to-firmware relationship. - SD manager (
sd_functions.cpp): browses SD files, installs local.binfiles, and binds files to keyboard shortcuts. - Online installer (
onlineLauncher.cpp): queries LauncherHub firmware metadata, downloads firmware, merges multi-part firmware, and checks downloaded firmware for updates. - WebUI (
webInterface.cpp,src/idf/idf_web_server.cpp): exposes file management, OTA upload, text editing, NVS editing, and partition management through a browser. - Serial console (
serial_console.cpp): a command interface over the serial port for WiFi, partitions, navigation, calibration, and SDIO pins. - NVS helpers and BLE bonds (
nvs_helpers.cpp,ble_bonds.cpp): centralized NVS access and the NimBLE bond compatibility layer. - Mass storage (
massStorage.cpp): exposes the SD card over USB on devices with OTG support. - Board layer: each supported device has its own PlatformIO environment, pinout header, and
interface.cppsetup code.
Launcher can install normal application binaries, merged firmware binaries, and multi-part firmware packages.
A normal application binary is the .bin produced by Arduino, PlatformIO, or ESP-IDF for the app itself. It usually starts with the ESP image magic byte 0xE9 and is written to an OTA app partition.
A merged binary may include bootloader, partition table, app image, and sometimes data partitions in one file. Launcher inspects the binary, reads partition metadata when available, extracts the application section, and can install associated data partitions when the firmware package declares or contains them.
A multi-part package is published on LauncherHub as separate files — typically bootloader.bin, partitions.bin, firmware.bin, and data.bin. Launcher downloads each part in turn and writes them into a single merged file on the SD card, which is then installed like any other merged binary.
Full flash dumps are larger images that may contain unused or unknown bytes. Launcher does not blindly write an entire dump over its own flash layout. It identifies useful regions, checks app image validity, and writes only what fits into the selected install layout.
Older Launcher versions depended mostly on fixed partition schemes. Current Launcher uses a dynamic partition model.
The partition manager can:
- list app and data partitions
- show details for each partition
- create OTA app, FAT, and SPIFFS partitions in free space
- resize or remove editable partitions
- format editable data partitions
- back up and restore FAT or SPIFFS data partitions
- associate a data partition with the firmware that owns it
- compact the table and migrate moved data when needed
- apply or discard pending changes
Protected system partitions are kept safe. Launcher validates alignment, offsets, sizes, OTA metadata, and required system entries before writing a generated partition table.
The same operations are available from the WebUI, which is the practical path on headless builds.
When installing a firmware, Launcher chooses an install layout based on the app size and any required data partitions. It can use an existing OTA slot, create a new app partition, resize free space, or offer repartition choices when the current layout is not large enough.
For firmware with data files, Launcher can install:
- the application image into an OTA app partition
- SPIFFS or LittleFS-style data into a SPIFFS/LittleFS partition, using the label declared in the firmware's partition table
- FAT data into
vfsorsyspartitions, used by firmware such as UIFlow2, CircuitPython, Tactility, and MicroHydra-style environments
Firmware that ships a valid, populated data partition has that partition copied as-is. Empty data partitions are created with a minimum size of 0x70000 (448 KB), or larger when the flash size allows. FAT partitions labelled sys or system are copied whole; other data partitions are copied and resized rather than reformatted, which is what keeps UIFlow, Tactility, and similar firmware working after an install.
Installing a firmware also prunes app metadata left behind by partitions that no longer exist and resets the nvs.net80211 namespace, which avoids stale entries and NVS exhaustion over many installs.
After installation, Launcher stores app metadata and can expose the installed firmware as a launcher menu entry.
Data partition backups are stored on the SD card under /bkp, in folders named {appNum}-{appName}. The relationship between a backup and the firmware that produced it is recorded in /bkp/backupData.json.
Because the link is stored, Launcher can offer the correct backup when you reinstall a firmware, and both the app icon and PMan can show whether backup data exists for a given app. With Auto Backup enabled, removing or overwriting an app that owns data offers to back it up first.
An SD card is required for this feature.
Different firmware versions use different NimBLE bond formats, which used to break pairings when switching firmware. Launcher now saves NimBLE bond data per device, in both the older and newer formats, and swaps and restores it at runtime when you change firmware. This only happens once a firmware has actually paired with something over BLE and created bond data.
Bonds can be cleared from PMan > Erase BLE Bonds or from the WebUI NVS editor.
Launcher stores current settings in NVS and can also keep SD-card configuration data in /config.conf for compatibility and portability. WiFi credentials are encrypted before being saved. WebUI credentials, UI colors, dim time, orientation, file listing behavior, boot behavior, auto backup, auto connect, keyboard shortcut bindings, and the last installed app are all persisted.
The default WebUI login is:
- user:
admin - password:
launcher
Change these from the WebUI Config > User/Pass page, the configuration file, or the NVS editor if the device is used on an untrusted network.
Display handling lives in the external DisplayDrivers library. Launcher talks to its superclass rather than to individual panel drivers, which replaced the old in-tree tft.h / tft_inits.h files and the Arduino_GFX DSI patch scripts.
Launcher supports many hardware families, not a single fixed board. Board files were reorganized to make porting easier: a new port needs two files plus a CI entry.
A device port usually includes:
-
boards/[board]/platformio.inifor the build environment -
boards/[board]/interface.cppfor board-specific setup -
boards/[board]/connections.mdfor pinout documentation on DIY devices - a pinout header describing display, SD, buttons, touch, keyboard, battery, and bus pins
Per-board PlatformIO JSON files were replaced by one file per chip family — esp32, esp32s2, esp32s3, esp32c3, esp32c5, esp32c6, esp32p4 — under boards/_jsonfiles/. Environments that only differ by pinout were merged, for example m5stack-plus, lilygo-t-embed-all, and xteink-x3-x4.
Devices are described by capability macros such as HAS_2_BUTTONS and HAS_6_BUTTONS plus long-press navigation, which replaced a pile of per-board button workarounds.
The repository currently includes targets for M5Stack devices, LilyGo devices, CYD displays, Marauder/AWOK-style boards, Elecrow display and Crowpanel boards, e-paper devices including XTeink and M5Paper, SeeedStudio reTerminal Sticky, headless ESP32/ESP32-S3 builds, Arduino Nesso N1, Reaper, Smoochiee, Pancake, Waveshare, Xueersi-XiaoMiao, and other ESP32 display modules.
See Supported devices for the full per-vendor list, or the Launcher Flasher for the always-current version.
native/ builds the real main.cpp and display.cpp against an SDL2 window instead of a panel, using the DisplayDrivers USE_NATIVE_SDL backend. It runs the real setup(), loop(), menu items, navigation, and drawing code, so menu layout and new screens can be iterated without flashing a board.
cd native
pio run -e native -t upload
Arrow keys act as a 5-button pad, Enter selects, and Esc goes back. sd_functions.cpp runs for real against an in-memory fake filesystem; OTA, WUI, CFG, and PMan are stubbed with fake data handed to the real drawing functions. See native/README.md for the details of what is real and what is faked.