Flipper Zero firmware DIY: four levels—from your own .fap to a fork

«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.

Important:

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.

Info:

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

Info:

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.

Warning:

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.

Success:

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.

Success:

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.

Error:

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
Success:

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.

Security:

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
Warning:

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.

Info:

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.

Important:

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

Question:

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?