«Assemble firmware for Flipper Zero» — a phrase that hides four very different tasks. Some people need their own application, some a patch in the system code, some a fork with Sub-GHz restrictions lifted, and some simply don’t need to build anything at all. Before choosing among them, it’s worth understanding what’s actually inside and which tools you get together with the sources.
Everything below was taken from a live demo: official tag repository on tag 1.4.3, a container with 2 vCPU and 7 GB RAM, Debian. The numbers are calculated from the source tree and from api_symbols.csv in the SDK, file sizes come from a real build, and a C application was actually compiled.
Inventory: what the stock firmware already does
It’s worth starting here because roughly half of the questions about whether you can build firmware to do X relate to what already exists.
Calculated from the 1.4.3 tag source tree and protocol registries
Ten user applications in applications/main aren’t ten buttons in a menu, but ten hardware subsystems with their own protocol stacks:
| Application | Hardware | Inside |
|---|---|---|
subghz |
CC1101 transceiver, 300–928 MHz | 52 protocols in registry: keeloq, came, nice_flo, somfy, hormann, secplus_v1/v2, princeton, holtek |
nfc |
ST25R3916, 13.56 MHz | 12 protocol stacks (iso14443-3a/3b/4a/4b, iso15693-3, felica, mf_classic/ultralight/desfire/plus) and 26 parsers for specific cards |
lfrfid |
own analog path, 125 kHz | 24 protocols: EM4100, HID H10301, Indala, Awid, FDX-B, Paradox, Viking, Jablotron, Noralsy |
infrared |
IR receiver and transmitter | 14 protocol variants: NEC and extensions, Samsung32, RC5/RC5X, RC6, SIRC/15/20, Kaseikyo, Pioneer, RCA |
ibutton, onewire |
1-Wire | Dallas DS1990, DS1971, DS1992, DS1996, generic-mode, working with blanks |
gpio |
8 output lines | manual pin control, USB-UART bridge |
bad_usb, u2f |
USB stack STM32 | emulation of HID keyboard and U2F hardware key |
archive |
SD filesystem | browser of saved cards, keys and signals |
Next to it — 16 background services (gui, storage, notification, power, bt, cli, loader, rpc, dolphin, region, crypto, input and others), 10 settings screens, 27 debugging benches, and 13 ready-made application examples you can take as a template for your own.
Practical takeaway from this table: 26 card parsers include troika, plantain, social_moscow, umarsh, two_cities — meaning common transit cards are already understood by the stock firmware. Build for “support my card” is only needed when a specific card isn’t in this list.
Where all of this fits
Boundaries — from linker scripts targets/f7, image size — from its own build
The firmware runs on the STM32WB55 — a dual-core chip where Cortex-M4 handles the application and Cortex-M0+ runs the radio stack Bluetooth. Boundaries are hard-coded in the linker scripts targets/f7: flash memory 1024 KB, of which the application processor has access to everything up to the radio stack address 0x080D7000, i.e. 860 KB.
My image, built with COMPACT=1 DEBUG=0, took 750 KB. About 110 KB free space remains — and this is precisely why build flags aren’t cosmetic: a debug build is noticeably heavier, and on a fork with ten additional applications, available space disappears quickly.
The same story with RAM: 192 KB in RAM1 for everything at once — firmware, your apps and graphics, plus two 10 KB blocks through which there’s communication with the radio processor. That’s why stack_size in the application manifest is stated explicitly in bytes, not as a vague “how much is needed.”
What this means for your code
An .fap application doesn’t link statically with the firmware — it talks to it via the table of exported symbols. The table is in the SDK file targets/f7/api_symbols.csv, and you can view it at a glance:
head -3 ~/.ufbt/current/sdk_headers/f7_sdk/targets/f7/api_symbols.csv
# entry,status,name,type,params
# Version,+,87.1,,
# Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
# how much of everything there is
cut -d, -f1 ~/.ufbt/current/sdk_headers/f7_sdk/targets/f7/api_symbols.csv | sort | uniq -c
Numbers from targets/f7/api_symbols.csv — the file used to link every .fap
In version 1.4.3 this is 3531 functions, 311 variables and 319 headers for API version 87.1. Breakdown by subsystems shows where the power is concentrated.
Core furi — 29 headers. Under the hood FreeRTOS, but externally there’s a dedicated layer: threads (furi_thread), mutexes, semaphores, message queues, streaming buffers, event flags, timers, event loop (furi_event_loop), pubsub bus, and service registry record. Plus furi_string — reference-counted strings because ordinary char* on 192 KB RAM gets annoying fast.
Hardware layer — 47 headers furi_hal_* and 488 functions. Direct access to peripherals: gpio, spi, i2c, adc, pwm, serial, usb (including cdc, hid, ccid), nfc, subghz, infrared, rfid, ibutton, bt, power, speaker, vibro, light, rtc, random, crypto, flash, sd, region. If you’re framing the task as “kick it and see what happens” — this is where it belongs, and you don’t need to rebuild the firmware.
Interface — 17 ready-made elements. submenu, menu, button_menu, button_panel, dialog_ex, popup, widget, text_input, byte_input, number_input, text_box, variable_item_list, file_browser, loading, empty_screen and validators. You don’t need to write your own menu from scratch — the 128×64 monochrome display is standard, and everything typical is already drawn.
Separately, there’s lib/flipper_application — five headers responsible for loading and executing .fap during runtime, including plugins. This exact mechanism enables 26 NFC card parsers to be separate files, not part of a monolith: the nfc app loads the required parser on demand.
Four levels: where you’ll end up
Levels differ not in command complexity but in the scope of responsibility
What you’ll need in any case: 64-bit Linux, macOS 12+ or Windows 10+, installed git and Python 3.8 or newer. You don’t need to install a compiler — both build systems for Flipper pull their own arm-none-eabi-gcc and do not touch the system PATH.
Level 0. Check that you don’t need to build
Official firmware releases are distributed through the update channel, and that is the most honest source of truth about versions:
curl -s https://update.flipperzero.one/firmware/directory.json \
| python3 -c 'import json,sys;[print(c["id"], c["versions"][0]["version"]) for c in json.load(sys.stdin)["channels"]]'
development a55e3939
release-candidate 1.4.2-rc
release 1.4.3
The stable channel serves 1.4.3 — a release from December 5, 2025. If the task sounds like “install the latest firmware” or “install someone else’s ready fork,” you don’t need to download the sources: qFlipper and the web updater will do it in a minute, and the image will be signed and verified.
Level 1. Your own .fap app: five commands
Flipper Zero apps are separate .fap files on the SD card; they’re not part of the firmware. They are built by ufbt — a trimmed build system that downloads a ready SDK instead of the whole firmware sources.
python3 -m pip install --upgrade ufbt
mkdir gig_hello && cd gig_hello
ufbt create APPID=gig_hello
```The first run pulls the SDK: in my case — `Using version: 1.4.3`, unpacking to `~/.ufbt/current`. After `ufbt create` exactly five files appear in the directory:
gig_hello.c application source
application.fam build manifest
gig_hello.png 10×10 icon, 1 bit
images/.gitkeep assets directory
.github/workflows/build.yml ready CI on GitHub Actions
The manifest is a standard Python call. Here is exactly what gets generated, word for word:
```python
App(
appid="gig_hello", # Must be unique
name="App gig_hello", # Displayed in menus
apptype=FlipperAppType.EXTERNAL,
entry_point="gig_hello_app",
stack_size=2 * 1024,
fap_category="Examples",
fap_icon="gig_hello.png", # 10x10 1-bit PNG
fap_icon_assets="images",
)
There are three important fields. entry_point is the name of the C function where the application starts; it must match what is written in the source. stack_size is how many bytes of stack are allocated to the application thread: 2 KB is enough for simple rendering, but working with NFC or files will require more. fap_category defines the subdirectory on the SD card: available Sub-GHz, RFID, NFC, Infrared, GPIO, iButton, Bluetooth, USB, Games, Tools, Media, Debug, Examples.
The template gig_hello.c can only write to the log. We’ll replace it with a minimal application with a screen and an exit button — this code is built and tested on SDK 1.4.3:
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
static void gig_hello_draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 12, "GIG Community");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 28, "Built from source");
canvas_draw_str(canvas, 2, 40, "Press Back to exit");
}
static void gig_hello_input_callback(InputEvent* event, void* ctx) {
FuriMessageQueue* queue = ctx;
furi_message_queue_put(queue, event, FuriWaitForever);
}
int32_t gig_hello_app(void* p) {
UNUSED(p);
FuriMessageQueue* queue = furi_message_queue_alloc(8, sizeof(InputEvent));
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, gig_hello_draw_callback, NULL);
view_port_input_callback_set(view_port, gig_hello_input_callback, queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
InputEvent event;
bool running = true;
while(running) {
if(furi_message_queue_get(queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == InputTypeShort && event.key == InputKeyBack) {
running = false;
}
}
}
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(queue);
return 0;
}
Note the order of release at the end: first the viewport is removed from the GUI, then it is freed. If you swap these lines, the system will continue to draw the already-freed structure — and this will not be a compilation error, but a device reboot in your hands.
Build is simply ufbt with no arguments:
$ ufbt
CC /tmp/fz/gig_hello.c
LINK /root/.ufbt/build/gig_hello_d.elf
FAP /root/.ufbt/build/gig_hello.fap
INSTALL /tmp/fz/dist/gig_hello.fap
APPCHK /root/.ufbt/build/gig_hello.fap
Target: 7, API: 87.1
The final file is dist/gig_hello.fap, 2200 bytes. The line APPCHK is the most useful one in the entire output.
Target: 7, API: 87.1 is the fingerprint the device uses to decide whether to run the application. Target is the hardware revision (7 — Flipper Zero), API is the version of the same export from api_symbols.csv. Firmware with a different major API version will refuse to run the application, although the file will copy without errors. Hence the rule: a .fap built for a stable channel is not obligated to work on a fork or a dev build. You can switch SDK without recreating the project:
ufbt update --channel=dev # or rc, or release
ufbt update --branch=1.4.3 # a specific SDK branch
ufbt update --local=/path/to/flipper-z-f7-sdk-local.zip # SDK from your own build
Upload and run immediately on the connected device:
ufbt launch
The command copies the .fap to the SD card under the subdirectory from fap_category and starts the application. qFlipper must be closed during this — it holds the serial port exclusively.
Where do people get applications from
All major API version checks are performed by any .fap, no matter who built it
Writing your own code is far from the only common way to get a new feature. There are four sources, and they differ not by ease of installation, but by which API version the binary was built against.
Official catalog: 437 applications
Major version numbers are taken from the catalog’s public API at the time the material was prepared
The catalog lives on lab.flipper.net, accessible directly from the browser or the mobile app, and the numbers by section are served by a public API — you can verify them yourself:
curl -s 'https://catalog.flipperzero.one/api/v0/category?limit=50' \\
| python3 -c 'import json,sys;[print(f"{c[\"name\"]:12} {c[\"applications\"]}") for c in json.load(sys.stdin)]'
The skew is telling: GPIO — 112 apps, Games — 102, Tools — 93, and iButton accounts for two. That is, the catalog revolves around external modules and utilities, not around the radio part — the radio part is already covered by stock apps.
You can also access the catalog with your own app, and the source code is not uploaded there. In the flipper-application-catalog repository, one file is placed: applications/<Category>/<app_id>/manifest.yml, which only references your repository and a specific commit:
sourcecode:
type: git
location:
origin: https://github.com/example/test.git
commit_sha: bf7019d16d7b50f6a98cc3abdac38c53952a4f30
short_description: Test app
description: "@README.md"
changelog: "@./docs/changelog.md"
screenshots:
- screenshots/ss0.png
The .fap is built by the catalog infrastructure. The rules are strict: id must be lowercase and match the appid from application.fam, category must correspond to the catalog where the manifest is placed, version must have the format major.minor, and an update with a higher number than the previous is rejected. Screenshots must be taken with the built-in qFlipper function.
Ready-made app from someone else’s repository
Half of the interesting stuff in the catalog doesn’t get there and sits simply on GitHub. The scheme is the same: clone, go into the app directory, run ufbt.
I tested on the official set flipperzero-good-faps — 15 apps that the Flipper team themselves maintain:
git clone --depth 1 https://github.com/flipperdevices/flipperzero-good-faps.git
cd flipperzero-good-faps/weather_station
ufbt # build
ufbt launch # upload to device and run
All built, with no edits. Real sizes:
| App | What it does | .fap size |
|---|---|---|
clock |
clock on screen | 3.8 KB |
signal_generator |
signal generator on GPIO | 11.8 KB |
mfkey |
recovery of Mifare Classic keys from intercept | 37.6 KB |
nfc_magic |
writing to “magical” cards with overwritable UID | 92.4 KB |
weather_station |
reception of wireless meteorological sensors on 433 MHz | 101.9 KB |
There is also dap_link (turns Flipper into a CMSIS-DAP debug probe), avr_isp_programmer (AVR programmer), spi_mem_manager (read/write SPI flash), air_mouse (gyroscope mouse), mass_storage, music_player, nfc_rfid_detector, video_game_module_tool, flip_tdi, air_arkanoid.
Building someone else’s app yourself is usually the better approach than downloading a ready-made .fap from chat: the build uses the SDK you have, and compatibility questions are resolved on the spot. The difference in effort is two commands.
Why an app from the catalog might not run on a fork
Official documentation states the rule briefly: the app launcher checks the major API version of the .fap against the firmware’s major API version, and if they disagree, the app will not run. The major version increases when a function is removed from the export or its signature changes; the minor version increases when a new function is added.
From this follows a practical rule experienced by many: the catalog builds binaries for the official firmware API, so on a fork with its own changes, many apps simply won’t start. That is why forks carry their own packs of apps inside the firmware — this is the only way to guarantee version match.
Level 2. Full firmware build: fbt
Here you clone the entire repository along with submodules. The --recursive flag is not optional, but mandatory: without it, the build will fail due to missing libraries.
git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git
cd flipperzero-firmware
./fbt COMPACT=1 DEBUG=0 updater_package
Measurements from a live build tag 1.4.3 on a container with 2 vCPU and 7 GB RAM
The first run of ./fbt looks like nothing is happening: the script checks tar and curl, downloads the toolchain archive and unpacks it into ./toolchain inside the repository. System packages are not installed — the compiler lives next to the sources, and you can remove the entire build environment by deleting one directory. The version I received was arm-none-eabi-gcc (GCC) 12.3.1.
A clean build after ./fbt -c took 144 seconds on two cores. This is noticeably faster than one would expect from a firmware with 700 MB of sources, as most of the space is taken by submodules that don’t end up in the target image.
What COMPACT and DEBUG mean
The default values are in fbt_options.py:
TARGET_HW = 7
COMPACT = 0
DEBUG = 1
DIST_SUFFIX = "local"
FIRMWARE_APP_SET = "default"
FBT_TOOLCHAIN_VERSIONS = (" 12.3.", " 13.2.")
That is, out of the box you build a non-compact debug build — it’s bigger and slower, but you can use step-by-step debugging with it. For daily-use firmware, you need COMPACT=1 DEBUG=0.
Do not modify fbt_options.py — it’s under version control and will conflict on every git pull. Create a nearby fbt_options_local.py; its build will be picked up automatically:
COMPACT = 1
DEBUG = 0
DIST_SUFFIX = "gig"
FIRMWARE_APP_SET = "default"
DIST_SUFFIX ends up in artifact names and in the Info field inside the update package, so your builds are easy to distinguish from official ones later. FIRMWARE_APP_SET switches the set of embedded apps — for example, unit_tests builds a firmware stand instead of the usual one.
Where to place artifacts and how to upload them
The dist/f7-C directory from my build contains 11 files; the breakdown is shown in the diagram above. You can find it without memorizing suffixes:
ls -1 dist/*/ | head -20
There are three ways to deliver the build to the device, and they are not equally suitable.
./fbt flash_usb_full # via USB, with resources and radio glass
./fbt flash # via SWD programmer: ST-Link, J-Link, CMSIS-DAP, Wi-Fi devboard
The third is manually via qFlipper: take dist/f7-C/flipper-z-f7-update-local.tgz, open qFlipper and choose “Install from file”. This path is preferable if you’re not sure about the build: qFlipper will verify the package integrity before it starts flashing.
Do not disconnect the cable and do not let the laptop sleep during flash_usb_full. The Core2 radio stack update is a separate phase, and interrupting exactly at that stage makes the device brick more reliably than anything else in this guide.
The Full Set of fbt Goals
./fbt is not a single build command, but an umbrella over dozens of goals. Below are the ones that are actually useful.
| Group | Goal | What it does |
|---|---|---|
| Build | fw_dist |
build firmware and unpack into dist (default goal) |
updater_package / updater_minpackage |
self-update package; minimal — only DFU, full — plus radio glass and SD resources | |
copro_dist |
build Core2 FUS bundle and stack for qFlipper | |
faps |
build all external applications as .fap |
|
fap_<appid> |
gather a single application by its identifier | |
fap_dist |
lay out external applications into dist |
|
| Firmware | flash_usb, flash_usb_full |
flash via USB |
flash, firmware_flash |
flash via SWD; pick adapter — SWD_TRANSPORT=…, serial — SWD_TRANSPORT_SERIAL=… |
|
jflash |
flash via J-Link using JFlash | |
| Debug | debug |
build, flash and immediately attach gdb with the loaded .elf |
updater_debug |
the same, but with the .elf updater |
|
debug_other |
gdb without loading symbols — symbols added manually via add-symbol-file |
|
blackmagic, openocd, get_blackmagic, get_stlink |
wrappers around debug probes | |
devboard_flash |
update Wi-Fi devboard, ARGS="-c dev" for the channel |
|
cli |
open device console over USB | |
| Quality | lint, format |
clang-format on C sources |
lint_py, format_py |
black on build scripts and manifests | |
lint_img, format_img |
check and normalize PNG assets | |
lint_all, format_all |
everything at once | |
doxygen, doxy |
generate API documentation and open it in a browser | |
| Assets | resources, icons, proto, proto_ver |
rebuild SD resources, icons from PNG and protobuf |
| Utility | firmware_list, updater_list |
listing of sources + assembler |
firmware_cdb, updater_cdb |
generate compile_commands.json without a full build |
|
vscode_dist |
deploy VS Code configuration with build and debug tasks |
Separately — working with a single application when you don’t want to rebuild the firmware:
./fbt launch APPSRC=applications_user/my_app # build, flash and run
./fbt build APPSRC=applications_user/my_app # only build
./fbt -c fap_my_app # clean artifacts of this app
Tools that come in the package
In addition to targets, the build system brings several things that are usually discovered by accident.
Shell with toolchain. The very arm-none-eabi-gcc, gdb and objdump can be obtained in your shell without adding paths:
source scripts/toolchain/fbtenv.sh # Linux and macOS
. `./fbt -s env` # the same, shorter
scripts\toolchain\fbtenv.cmd # Windows
Environment variables that change build behavior:
| Variable | Effect |
|---|---|
FBT_NOENV=1 |
do not configure toolchain, take everything from system PATH (Unix only) |
FBT_TOOLCHAIN_PATH |
keep toolchain in a common directory, not a clone copy |
FBT_NO_SYNC=1 |
do not update submodules automatically |
FBT_VERBOSE=1 |
show full compiler command lines |
Command-line keys. --extra-define=NAME=VALUE passes a global define to the compiler, --extra-ext-apps=app1,app2 forces building listed apps as external, --options another_file.py overrides the config file entirely, --proxy-env=VAR1,VAR2 forwards environment variables to child processes — by default fbt sanitizes its environment.
Hardware debugging. Supported Wi-Fi devboard from Flipper Devices themselves, ST-Link v2/v3, J-Link and any CMSIS-DAP compatible probes, including Raspberry Pi Debug Probe. The debug target builds, flashes and opens gdb with symbols pre-loaded; the SVD file for peripheral registers also comes with it.
CLI: a second interface to the device
People forget this part more often, but it gives access to all subsystems without writing a single line of code. The console is raised over USB:
./fbt cli # from the repo
ufbt cli # from the app project
The base set of commands registered in the firmware: help, ?, !, device_info, info, uptime, date, free, free_blocks, top, log, sysctl, power, sleep, vibro, led, input, gpio, i2c, storage, crypto, bt, js, factory_reset, update, unit_tests, start_rpc_session, reload_ext_cmds.
Radio subsystem work is hooked by external plugins from /ext/apps_data/cli/plugins — if after installing the app the command does not appear, it will be picked up by reload_ext_cmds. Most useful subcommands:
| Subsystem | Commands |
|---|---|
| Sub-GHz | rx, tx, tx_from_file, rx_raw, decode_raw, rx_carrier, tx_carrier, chat, encrypt_keeloq, encrypt_raw |
| NFC | scanner, field, raw, apdu, iso14a, iso14b, mfc (rdbl, wrbl, dump), mfu, mfp, felica, slix, st25, emulate |
| LF-RFID | read, write, emulate, raw_read, raw_emulate, raw_analyze |
| Infrared | rx, tx, decode, raw, universal, list |
| iButton / 1-Wire | read, write, emulate, search |
Before writing an app, try to solve the task via the CLI. The subghz rx command writes what is received to a file, nfc scanner shows everything that gets into the field, and gpio pulls pins. A host script talking to the device over a serial port often closes the issue without a single line of C — and without versioning hassles of the API.
JavaScript, if C is excessive
The firmware contains js_app — an interpreter based on mJS, and it is a different level between “press in the menu” and “write in C”. The script is placed on an SD card and runs from the menu or with the js command in the console.
Available modules: badusb, gpio, serial, storage, notification, math, event_loop, flipper, gui and tests. The gui module is not toy — from the script you have access to the same elements as in C: submenu, menu, button_menu, button_panel, dialog, popup, widget, text_input, byte_input, number_input, text_box, file_picker, loading, empty_screen.
The repository contains 18 examples — applications/system/js_app/examples: from badusb_demo.js and uart_echo_8e1.js to event_loop.js, gpio.js, storage.js and gui.js. For one-off automation this is more than enough, and the entry barrier is incomparable to firmware building.
Level 3. Fork — and what you take with it
Momentum and Unleashed are built with the same goals by fbt as the official firmware: ./fbt updater_package for TGZ and ./fbt flash_usb_full for direct flashing. Technically the difference is minimal — you clone another repository and run the same commands.
Difference is in content. Below is what is stated in project descriptions.
| Momentum | Unleashed | RogueMaster | |
|---|---|---|---|
| Basis | official firmware plus Unleashed enhancements | official firmware | fork of Unleashed |
| Sub-GHz | expanded ranges 281–361, 378–481, 749–962 MHz; “subdriving” — GPS coordinates preserved along with the signal | regional TX restrictions removed, protocols added, saving and sending hopping codes (FAAC SLH, BFT Mitto with manual creation), support for external CC1101 | inherits Unleashed changes |
| NFC and RFID | support Type 4 and NTAG4xx, additional card parsers | EMV parsing, extended Mifare Classic dictionary, arbitrary UID, fuzzers for LF-RFID and iButton | inherits plus own sets |
| Input and USB | Bad-Keyboard with configurable USB and Bluetooth parameters | BadKB — same BadUSB, but over Bluetooth, with layouts | inherits |
| Built-in apps | Momentum App for firmware settings, BLE Spam, FindMy Flipper, NFC Maker, Wardriver, file search on SD | community plugins and games in the build | claimed more than 150 games plus dozens of plugins |
| Design | Asset Packs system: animations, icons and fonts change without rebuild | own animations and desktop clocks | thematic animation and asset packs |
| Others | lock on boot, reset on wrong PIN, name, MAC and serial number spoofing, RGB lighting customization, file operations and disk image mounting | text cursor, byte editor, PIN code lock, dev builds on dev.unleashedflip.com |
in the description it says: first install stock, then remove apps before updating |
Asset Packs in Momentum — a separate mechanism that is worth knowing even for those who don’t fork: packs are on the map at /ext/asset_packs/<Name>/ with subfolders Anims and Icons, can be switched from settings directly on the device, and are assembled by the script scripts/asset_packer.py from the repository. That is, you can change animations, icons, and fonts without rebuilding the firmware at all.
There is also a technical cost. Each additional built-in application consumes part of the same 110 KB of spare flash memory, and having your own API version means that a .fap from the official catalog may not run.
Regional restrictions in the official firmware are not vendor whim, but a reflection of the fact that radio bands are allocated differently in different countries. Transmitting on a frequency not allowed in your jurisdiction is an administrative offense regardless of what you are transmitting. Building a fork transfers this responsibility entirely to you: neither the manufacturer nor the author of the fork participate in this scheme. It’s also worth remembering that a removed warranty is one of the least favorable consequences.
External modules: how they extend the hardware
Eight GPIO lines to the outside are not a trifle, but a full expansion bus: through it Flipper obtains what is not on the board. Hence the catalog tilt toward the GPIO section with 112 applications.
| Module | Chip | What it provides | What it is flashed with |
|---|---|---|---|
| Wi-Fi Developer Board | ESP32-S2-WROVER | officially — SWD debugging: breakpoints, memory inspection, step-by-step execution; standard firmware built on Black Magic Debug and CMSIS-DAP and compiled on ESP-IDF | via USB or Wi-Fi; goal ./fbt devboard_flash |
| The same board with ESP32 Marauder | ESP32-S2 | third-party firmware with Wi-Fi and Bluetooth scanning and a set of offensive features | flashed in place of the board’s stock firmware |
| Video Game Module | RP2040 | video output DVI-D to monitor or projector, six-axis motion sensor TDK ICM-42688-P, own 14-pin GPIO connector | UF2 mode over USB-C |
| External CC1101 | CC1101 | external Sub-GHz transceiver with a proper antenna; supported by official app via driver cc1101_ext |
does not require firmware |
| External IR transmitter | — | range instead of built-in LED | does not require firmware |
ESP32 Marauder is a toolkit for testing Wi-Fi and Bluetooth, and part of its capabilities (client deauthentication, flood with service frames) is legal in most jurisdictions only for your own network and only with the owner’s consent. The board remains the same: the official firmware from Flipper Devices turns it into a debugger, not a radio tool.
Firmware knows about the external radio part by default: in the source tree there is applications/drivers/subghz/cc1101_ext, and in the SDK — the family of functions subghz_devices. So an external CC1101 does not require forks or patches.
Signal bases and graphics
A separate layer of the ecosystem is not about code — it’s data that is placed on the memory card.
Infrared bases. The Flipper-IRDB repository is the largest shared dump of IR remotes. I cloned it and counted: 8901 .ir files in 47 sections — TVs, air conditioners, projectors, receivers, fans, vacuums, car stereos, cameras, down to fireplaces and elevators. The files are placed in /ext/infrared and opened by the built-in app.
Compiled collections. UberGuidoZ/Flipper is a large archive of files, documentation, and dumps of all kinds at once: Sub-GHz, NFC, IR, BadUSB scripts, graphics. An index over everything else is the list awesome-flipperzero, where firmware, apps, databases, and modules with short descriptions are gathered.
Graphics. In addition to Asset Packs, Momentum has separate community tools: animation managers and GIF-to-Flipper animation converters. The screen is monochrome 128×64, so there isn’t “heavy” graphics by definition.
Useful habit: keep everything downloaded from chats and repositories in a separate directory on the card and do not mix with what you wrote yourself. Flipper file formats are text and human-readable — .ir, .sub, .nfc open with a normal editor, and before installing someone else’s file into it it’s useful to take a look.
If the device won’t turn on
Good news: the Flipper Zero firmware lives in the main controller STM32WB55, and the bootloader is separate in it, and destroying it with a user build is practically impossible. Recovery is described in the official documentation and comes down to two combinations.
If the device turns on but the update doesn’t go through: hold left + back for 5 seconds, release “back”, continuing to hold “left” until the blue LED lights up.
If the device shows no signs of life: hold OK + back for 30 seconds. The screen will be blank — that’s normal. After this, the computer should detect a DFU device, and qFlipper should offer the Repair button, which installs the firmware from scratch.
If the DFU device does not appear in the system — that’s not a firmware issue, but a power or cable problem; use a known-good cable, preferably not a charging cable.
For those who enjoy the idea of building firmware for their own hardware, there is a related storyline with 3D printers: the Klipper calibration procedure — the exact same logic, where changing steps breaks the result.
Sources
- flipperdevices/flipperzero-firmware — official repository and README
- documentation/fbt.md — full list of targets and environment variables
- documentation/AppManifests.md — fields of manifest.application.fam
- documentation/AppsOnSDCard.md — categories and running apps from SD
- flipperdevices/flipperzero-ufbt — installation, commands and channel switching in SDK
- update.flipperzero.one/firmware/directory.json — official update channel
- Firmware recovery — official recovery documentation
- Next-Flip/Momentum-Firmware
- DarkFlippers/unleashed-firmware
- RogueMaster/flipperzero-firmware-wPlugins
- Flipper Lab — official app catalog
- flipper-application-catalog — manifest.yml requirements
- flipperzero-good-faps — apps from the Flipper team
- Wi-Fi Developer Board — official documentation
- Video Game Module — official documentation
- Asset Packs — Momentum wiki
- Lucaslhm/Flipper-IRDB — infrared dump database
- djsime1/awesome-flipperzero — ecosystem index
At what level did you stop — CLI and host script, your own .fap, a patch in the official firmware or a fork? And a separate question for those who build regularly: do you keep the toolchain inside the repository or have you moved it via FBT_TOOLCHAIN_PATH to a common directory so you don’t download 1–2 GB for every clone?







