Flashing
The easiest way to flash Fantasi is the web flasher - it drives the USB bootloader paths described below straight from Chrome or Edge (no toolchain), auto-installs the BLE radio stack, and verifies the image before boot. The command-line paths on this page are for building from source, automation, and recovery.
Every target has two paths: a USB path (the blessed, user-facing flow that goes through the device's own bootloader) and an SWD/JTAG path (for bring-up, dead firmware recovery, or CI). USB paths are preferred - each is designed so a bad image can't brick the device.
All four devices run the same Fantasi CLI once flashed. Connect at 115200 baud to the enumerated CDC port (/dev/ttyACM* on Linux, /dev/tty.usbmodem* on macOS):
fantasi> device
FZ # or KIISU or CU or PM3
Flipper Zero (STM32WB55)
USB - ST ROM DFU
The STM32WB55 has a ROM-resident DFU bootloader in system memory at 0x1FFF0000. It is physically separate from user flash and cannot be erased by dfu-util, so this path is always a safe recovery.
- Build:
make PLATFORM=flipper - Enter DFU mode. Either run
fantasi> dfufrom a running Fantasi, or hold OK + BACK for 30 seconds with the cable disconnected (device must be charged). - Verify the ROM DFU enumerated:
lsusb | grep 0483:df11 - Flash:
make PLATFORM=flipper flash
Internally tools/flash.py runs:
dfu-util -a 0 -d 0483:df11 -s 0x08000000:leave -D build/flipper/fantasi-flipper.bin
The :leave suffix tells the ROM to detach and reset into user flash when the transfer finishes.
SWD - Black Magic Probe
Wire BMP to the Flipper's SWD test pads and attach via GDB:
arm-none-eabi-gdb \
-ex 'target extended-remote /dev/ttyACM1' \
-ex 'monitor swdp_scan' \
-ex 'attach 1' \
-ex 'monitor erase_mass' \
-ex 'load build/flipper/fantasi-flipper.elf' \
-ex 'kill'
This erases all user flash and reprograms it. The ROM DFU at 0x1FFF0000 is read-only and untouched - recovery via dfu-util is always possible.
Kiisu (STM32WB55)
The Kiisu is a Flipper-compatible STM32WB55 board, so the flashing path is identical to the Flipper Zero above - same ST ROM DFU (0483:df11), same address (0x08000000), same radio stack. Only the application image differs (make PLATFORM=kiisu → build/kiisu/fantasi-kiisu.bin), because the Kiisu's display is an SH1106 driven by a companion MCU rather than the Flipper's direct ST7565.
- Build:
make PLATFORM=kiisu - Enter DFU mode (as above):
fantasi> dfu, or the button hold. - Flash:
make PLATFORM=kiisu flash
Detection. The Kiisu and Flipper share both their runtime (1209:0001) and DFU (0483:df11) USB IDs. A running device is told apart by the device command (KIISU vs FZ), which make flash uses to auto-select the platform. A device sitting in DFU is indistinguishable by USB ID alone; the flasher reads the 8th byte of the OTP device-name field (0x1FFF7000+31) - the Kiisu's factory provisioning space-pads it (0x20) while a Flipper NUL-pads it (0x00). If that byte is neither (an 8-character name has no padding), make flash asks you to pass PLATFORM=kiisu or PLATFORM=flipper explicitly.
Proxmark3 (AT91SAM7S)
USB - PM3 Bootloader
The PM3 bootloader lives in the first 8 KB of flash (0x100000-0x101FFF) and is the only recovery path without a JTAG adapter. The bundled flasher (tools/pm3_flasher.py) never writes to that region.
- Build:
make PLATFORM=proxmark3 - Enter bootloader flash-mode. Either run
fantasi> dfufrom a running Fantasi, or hold the PM3 button while plugging in USB. - Verify:
lsusb | grep 9ac4:4b8f - Flash:
make PLATFORM=proxmark3 flash
Internally tools/flash.py runs tools/pm3_flasher.py (requires pyserial), which writes only the osimage region starting at 0x102000.
JTAG - CMSIS-DAP (dangerous)
AT91SAM7S flash has 16 KB lock regions aligned at 0x100000. The first region spans 0x100000-0x103FFF and covers both the bootloader (0x100000-0x101FFF) and the first 8 KB of the Fantasi osimage (0x102000-0x103FFF). OpenOCD's sam7 flash driver must erase a full lock region to write any page in it, so naively flashing Fantasi alone erases the bootloader as collateral damage.
Always flash bootrom + Fantasi together in one OpenOCD session:
openocd -f interface/cmsis-dap.cfg \
-c 'transport select jtag; adapter speed 2000' \
-f target/at91sam7sx.cfg -c '
init; reset halt
flash write_image erase /path/to/bootrom/obj/bootrom.elf
flash write_image /path/to/fantasi-proxmark3.elf
verify_image /path/to/bootrom/obj/bootrom.elf
verify_image /path/to/fantasi-proxmark3.elf
reset run
exit'
If the bootrom gets erased, recovery requires either the ERASE pad (shorts GPNVM0, forces boot into the on-chip SAM-BA ROM) or a full JTAG replay of the bootrom ELF.
Post-Reset Note
If the Proxmark stalls, unplug and replug.
Chameleon Ultra (nRF52840)
USB - Signed DFU via nrfutil
The stock Chameleon Ultra ships with a Nordic Secure DFU bootloader around 0xF3000. It requires cryptographically signed packages; unsigned packages are silently rejected. The matching private key is vendored as platforms/chameleon/dfu_key.pem.
- Build:
make PLATFORM=chameleon - Enter DFU mode. Either run
fantasi> dfufrom a running Fantasi, or hold the user button while plugging in USB. - Verify:
lsusb | grep 1915:521f - Flash within 30 seconds:
make PLATFORM=chameleon flash
Internally tools/flash.py runs:
nrfutil pkg generate \
--hw-version 0 --sd-req 0x00 --application-version 1 \
--application build/chameleon/fantasi-chameleon.hex \
--key-file platforms/chameleon/dfu_key.pem \
build/chameleon/fantasi-chameleon-dfu.zip
nrfutil dfu usb-serial -pkg <zip> -p /dev/ttyACM<n>
30-second inactivity timeout. The bootloader is built with NRF_BL_DFU_INACTIVITY_TIMEOUT_MS=30000. If no DFU traffic arrives within 30 seconds, the bootloader chains back to the valid app.
SWD - CMSIS-DAP (requires matching settings page)
The bootloader settings page at 0xFF000 contains boot_validation_app, an ECDSA-P256-SHA256 hash of the installed app. Flashing a new Fantasi over SWD invalidates the old hash, and the bootloader refuses to chain to the new app.
Regenerate the settings page and flash both in the same session:
BUILD=build/chameleon
nrfutil settings generate --family NRF52840 \
--application $BUILD/fantasi-chameleon.hex \
--application-version 1 \
--bootloader-version 2 \
--bl-settings-version 2 \
--app-boot-validation VALIDATE_ECDSA_P256_SHA256 \
--key-file platforms/chameleon/dfu_key.pem \
$BUILD/bl_settings.hex
openocd -c 'adapter driver cmsis-dap' \
-c 'transport select swd' \
-c 'adapter speed 1000' \
-f target/nrf52.cfg -c "
init; halt
program $BUILD/fantasi-chameleon.hex verify
program $BUILD/bl_settings.hex verify reset
exit"
Quick Reference
| Target | USB Bootloader VID:PID | App Base | Bootloader Preserved | SWD Landmine |
|---|---|---|---|---|
| Flipper Zero | 0483:df11 (ST ROM DFU) | 0x08000000 | ROM at 0x1FFF0000 (read-only) | None |
| Kiisu | 0483:df11 (ST ROM DFU) | 0x08000000 | ROM at 0x1FFF0000 (read-only) | Shares USB IDs with Flipper - see detection above |
| Proxmark3 | 9ac4:4b8f (PM3 bootloader) | 0x00102000 | 0x100000-0x101FFF | 16 KB lock-region erases bootrom |
| Chameleon Ultra | 1915:521f (Nordic Secure DFU) | 0x00027000 | 0xF3000+ | Settings page at 0xFF000 must match app hash |