Skip to content

Commit 3358ca1

Browse files
committed
Merge esp-flasher: on-device ESP flashing, bundled firmware, one-step install (v0.5.0)
2 parents 9a0bdb4 + df6abb5 commit 3358ca1

46 files changed

Lines changed: 5542 additions & 8 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,22 @@ jobs:
4141
with:
4242
python-version: "3.12"
4343

44-
- name: Build Flipper app (fap)
44+
- name: Build Flipper app (fap, with bundled portal + firmware)
4545
run: |
4646
pip install --upgrade ufbt
4747
ufbt update --index-url=https://up.momentum-fw.dev/firmware/directory.json --channel=release --hw-target=f7
48-
cd flipper/flytrap && ufbt
48+
tools/build-fap.sh # populates assets/ from the ESP build above, then ufbt
49+
50+
# Ready-to-flash bundle for the on-device flasher: drop the "flytrap" folder
51+
# into /ext/apps_data/flytrap/firmware/ on the SD (or deploy-to-flipper.py).
52+
- name: Assemble firmware bundle
53+
run: |
54+
mkdir -p bundle/flytrap
55+
cp esp32/flytrap-fw/flash.txt esp32/flytrap-fw/boot_app0.bin bundle/flytrap/
56+
cp esp32/flytrap-fw/build/flytrap-fw.ino.bootloader.bin \
57+
esp32/flytrap-fw/build/flytrap-fw.ino.partitions.bin \
58+
esp32/flytrap-fw/build/flytrap-fw.ino.bin bundle/flytrap/
59+
(cd bundle && zip -r ../flytrap-firmware-bundle.zip flytrap)
4960
5061
- name: Publish release
5162
env:
@@ -58,6 +69,7 @@ jobs:
5869
--title "Flytrap $TAG" \
5970
--generate-notes \
6071
flipper/flytrap/dist/flytrap.fap \
72+
flytrap-firmware-bundle.zip \
6173
esp32/flytrap-fw/build/flytrap-fw.ino.bin \
6274
esp32/flytrap-fw/build/flytrap-fw.ino.bootloader.bin \
6375
esp32/flytrap-fw/build/flytrap-fw.ino.partitions.bin

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ build/
1313
*.o
1414
*.a
1515
esp32/**/build/
16+
# ...but the firmware-bundle boot_app0 is a shipped asset, not a build artifact
17+
!esp32/flytrap-fw/boot_app0.bin
1618

1719
# Python tooling (deploy/flash scripts)
1820
venv/
@@ -29,3 +31,6 @@ __pycache__/
2931
# vendored ESP build deps (fetched by tooling)
3032
esp32/libs/
3133
.gstack/
34+
35+
# generated fap-bundled assets (portal + firmware), see tools/build-fap.sh
36+
flipper/flytrap/assets/

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,38 @@
22

33
All notable changes to Flytrap are documented here.
44

5+
## [0.5.0] — 2026-07-17
6+
7+
Flash the ESP firmware from the Flipper — no computer.
8+
9+
### Flipper app
10+
- **Flash Firmware** — a new menu item flashes the ESP over the GPIO UART using a
11+
vendored [esp-serial-flasher](https://github.com/espressif/esp-serial-flasher)
12+
(Apache-2.0) + a small `furi_hal_serial` port. Pick a firmware bundle from the SD
13+
(a `flash.txt` manifest of `<offset> <file>`); it **auto-detects** the board in
14+
download mode (hold BOOT, tap RESET) via the stub loader, streams each image with
15+
MD5 verify, and reboots the board. Progress + result on-device.
16+
- **Start Portal self-heals** — if the board isn't running Flytrap firmware (no
17+
beacon), Start now offers **Install firmware**, runs the flasher, and on
18+
**Continue** returns to the start flow, re-detects the flashed board, and brings
19+
the portal up. No computer, no separate step.
20+
- Only the **ESP32-S2** stub is compiled in (the full multi-chip stub table would
21+
balloon the fap's RAM footprint and OOM the app).
22+
- **One-step install** — the portal and firmware bundle are bundled in the fap
23+
(`fap_file_assets`, extracted to `apps_assets` on launch), so a fresh user just
24+
installs the `.fap`; the defaults point at the bundled assets.
25+
26+
### Fixed
27+
- **OOM starting the portal.** `furi_string_replace_all` on the ~38 KB portal built
28+
a second full copy (~2x heap); replaced with an in-place `furi_string_replace_str`
29+
loop. (Surfaced once the flasher library reduced free heap.)
30+
31+
### Tooling / CI
32+
- `tools/deploy-to-flipper.py` also uploads a firmware bundle to
33+
`/ext/apps_data/flytrap/firmware/flytrap/` (built images + `boot_app0.bin` +
34+
`flash.txt`).
35+
- The release workflow attaches a ready-to-flash `flytrap-firmware-bundle.zip`.
36+
537
## [0.4.1] — 2026-07-17
638

739
Fixes for the portal-start flow, all found on hardware.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ If that's not your use case, this isn't the project for you.
4646
everything also logged to `capture_<N>.txt` on the SD card.
4747
- **Live clients** list → detail (MAC, IP, joined time). Joins and leaves update the
4848
count in real time, so a device that disconnects drops off instead of lingering.
49+
- **Flash the ESP from the Flipper** — no computer. **Flash Firmware** picks a
50+
firmware bundle off the SD, auto-detects the board in download mode, and writes it
51+
over the GPIO UART (vendored [esp-serial-flasher](https://github.com/espressif/esp-serial-flasher)).
4952
- **Capture alerts** (haptic / beep / LED) with a **Settings** screen to toggle them.
5053
- A **Console** view (from the menu during a session) showing the raw serial protocol live.
5154
- Lightweight enough to run reliably on the SD-less ESP32-S2 dev board.

esp32/flytrap-fw/boot_app0.bin

8 KB
Binary file not shown.

esp32/flytrap-fw/flash.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Flytrap ESP32-S2 firmware — <flash offset> <file>
2+
# Read by the on-device flasher (Flytrap -> Flash Firmware) and by
3+
# tools/deploy-to-flipper.py, which uploads this next to the built images.
4+
0x1000 flytrap-fw.ino.bootloader.bin
5+
0x8000 flytrap-fw.ino.partitions.bin
6+
0xe000 boot_app0.bin
7+
0x10000 flytrap-fw.ino.bin

flipper/flytrap/application.fam

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,42 @@ App(
55
entry_point="flytrap_app",
66
requires=["gui"],
77
stack_size=8 * 1024,
8+
fap_private_libs=[
9+
# espressif/esp-serial-flasher @ 3c424d2 (Apache-2.0), UART interface only.
10+
# We provide the loader_port_* port in helpers/flytrap_esp_port.c.
11+
Lib(
12+
name="esp-serial-flasher",
13+
fap_include_paths=["include"],
14+
sources=[
15+
"src/esp_loader.c",
16+
"src/esp_targets.c",
17+
"src/md5_hash.c",
18+
"src/protocol_serial.c",
19+
"src/protocol_uart.c",
20+
"src/slip.c",
21+
"src/esp_stubs.c",
22+
],
23+
cincludes=["lib/esp-serial-flasher/private_include"],
24+
cdefines=[
25+
"SERIAL_FLASHER_INTERFACE_UART=1",
26+
"MD5_ENABLED=1",
27+
"SERIAL_FLASHER_WRITE_BLOCK_RETRIES=10",
28+
],
29+
cflags=["-Wno-sign-compare"],
30+
),
31+
],
32+
# App sources that include the flasher headers need these too.
33+
cdefines=[
34+
"SERIAL_FLASHER_INTERFACE_UART=1",
35+
"MD5_ENABLED=1",
36+
"SERIAL_FLASHER_WRITE_BLOCK_RETRIES=10",
37+
],
838
fap_icon="icons/flytrap_10px.png",
939
fap_category="GPIO",
40+
# Bundled portal + ESP firmware; extracted to /ext/apps_assets/flytrap/ on
41+
# launch so a fresh install works with no SD setup. Populated by build-fap.sh.
42+
fap_file_assets="assets",
1043
fap_author="Tarik Caramanico",
11-
fap_version="0.4.1",
44+
fap_version="0.5.0",
1245
fap_description="Captive portal (evil portal) for the ESP32-S2 WiFi dev board. Authorized use only.",
1346
)

flipper/flytrap/flytrap.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static bool flytrap_custom_event_callback(void* context, uint32_t event) {
2323

2424
static bool flytrap_back_event_callback(void* context) {
2525
FlytrapApp* app = context;
26+
if(app->flashing) return true; // swallow Back: a flash can't be safely aborted mid-write
2627
return scene_manager_handle_back_event(app->scene_manager);
2728
}
2829

@@ -103,11 +104,17 @@ static FlytrapApp* flytrap_app_alloc(void) {
103104
app->legacy_user = furi_string_alloc();
104105
app->session_raw = furi_string_alloc();
105106
app->logfile_buf = furi_string_alloc();
107+
app->flash_manifest = furi_string_alloc();
106108

107109
app->sound_on = true; // defaults; overridden by config if present
108110
app->vibro_on = true;
109111
flytrap_storage_ensure_dirs();
110112
flytrap_storage_load_config(app);
113+
// Fresh install with no saved selection: default to the bundled portal so
114+
// Start Portal works out of the box (custom portals still go in apps_data).
115+
if(furi_string_empty(app->portal_path)) {
116+
furi_string_set(app->portal_path, FLYTRAP_ASSET_PORTAL);
117+
}
111118

112119
app->uart = flytrap_uart_init(115200, flytrap_uart_notify, app);
113120

@@ -149,6 +156,7 @@ static void flytrap_app_free(FlytrapApp* app) {
149156
furi_string_free(app->legacy_user);
150157
furi_string_free(app->session_raw);
151158
furi_string_free(app->logfile_buf);
159+
furi_string_free(app->flash_manifest);
152160

153161
furi_record_close(RECORD_NOTIFICATION);
154162
furi_record_close(RECORD_DIALOGS);

flipper/flytrap/flytrap_custom_event.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,11 @@ typedef enum {
2424
// never freezes on a blank frame: detect the board, then send the portal.
2525
FlytrapEventDetectBoard = 110,
2626
FlytrapEventBeginSend = 111,
27+
// Flash Firmware: start (after the prompt paints), progress, done.
28+
FlytrapEventFlashStart = 112,
29+
FlytrapEventFlashProgress = 113,
30+
FlytrapEventFlashDone = 114,
31+
// From the "no board" prompt: install firmware, then continue where headed.
32+
FlytrapEventInstallFirmware = 115,
33+
FlytrapEventFlashContinue = 116,
2734
} FlytrapCustomEvent;

flipper/flytrap/flytrap_i.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030

3131
#define FLYTRAP_DATA_DIR EXT_PATH("apps_data/flytrap")
3232
#define FLYTRAP_PORTALS_DIR FLYTRAP_DATA_DIR "/portals"
33+
#define FLYTRAP_FIRMWARE_DIR FLYTRAP_DATA_DIR "/firmware"
34+
// Assets bundled in the fap, extracted here on launch (fap_file_assets).
35+
#define FLYTRAP_ASSETS_DIR EXT_PATH("apps_assets/flytrap")
36+
#define FLYTRAP_ASSET_PORTAL FLYTRAP_ASSETS_DIR "/portals/social.html"
37+
#define FLYTRAP_DEFAULT_FW FLYTRAP_ASSETS_DIR "/firmware/flytrap/flash.txt"
3338
#define FLYTRAP_CONFIG_PATH FLYTRAP_DATA_DIR "/config.txt"
3439
#define FLYTRAP_LOGS_DIR FLYTRAP_DATA_DIR "/logs"
3540
#define FLYTRAP_SSID_TOKEN "{{SSID}}" // replaced with the configured SSID in portals
@@ -101,6 +106,18 @@ typedef struct FlytrapApp {
101106

102107
FlytrapTextViewMode textview_mode;
103108

109+
// Flash Firmware (ESP-serial-flasher). The worker runs off the GUI thread and
110+
// posts progress/done events; `flashing` blocks Back while it can't be aborted.
111+
FuriThread* flash_thread;
112+
FuriString* flash_manifest; // selected flash.txt path
113+
volatile bool flashing; // true once connected (blocks Back mid-write)
114+
volatile bool flash_cancel; // set on exit to stop the download-mode poll
115+
volatile uint8_t flash_img, flash_cnt, flash_pct;
116+
char flash_stage[40];
117+
char flash_msg[80];
118+
bool flash_ok;
119+
uint8_t flash_phase; // 0=prompt, 1=flashing, 2=done
120+
104121
// Board liveness: the ESP beacons "PING" ~every 2s; if we hear nothing for
105122
// FLYTRAP_LINK_TIMEOUT_MS the board is likely unplugged and we flag the link.
106123
uint32_t last_rx_tick;

0 commit comments

Comments
 (0)