Building Your First Custom Flipper Zero App: ufbt Setup, GUI, and GPIO
We've covered flashing custom firmware and using the Flipper Zero's built-in tools extensively, but building your own app from scratch is a different skill — and it's genuinely accessible even if you're new to embedded development. This guide walks through setting up the development environment and building a real, working custom app.
Development Environment Setup
Flipper apps are built using ufbt (micro Flipper Build Tool), the official lightweight build toolchain that doesn't require compiling the entire firmware to build a single app — a huge time saver over the full firmware SDK approach.
- Install Python 3 if you don't already have it (ufbt is a Python-based tool)
- Install ufbt: pip install ufbt
- Create a new project directory and initialize it: ufbt create APPID=my_app — this scaffolds a basic app structure with the files you need to get started
- Connect your Flipper via USB and confirm qFlipper or ufbt can see it before proceeding
Anatomy of a Flipper App
A minimal Flipper app consists of a few key pieces:
FilePurpose application.famApp manifest — name, ID, entry point function, category, icon reference, and stack size your_app.cThe actual app logic — entry point, event loop, rendering icon.png (10x10)The icon shown in the Flipper's app menu, small and specific dimensions requiredA Minimal Working App
The simplest useful Flipper app pattern — draw something to the screen and respond to button input:
#include <furi.h> #include <gui/gui.h> #include <input/input.h> typedef struct { FuriMessageQueue* input_queue; int counter; } AppState; static void draw_callback(Canvas* canvas, void* ctx) { AppState* state = (AppState*)ctx; canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 10, 10, "Hello, Flipper!"); char buf[32]; snprintf(buf, sizeof(buf), "Count: %d", state->counter); canvas_draw_str(canvas, 10, 30, buf); } static void input_callback(InputEvent* event, void* ctx) { AppState* state = (AppState*)ctx; furi_message_queue_put(state->input_queue, event, FuriWaitForever); } int32_t my_app_main(void* p) { UNUSED(p); AppState state = {0}; state.input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, draw_callback, &state); view_port_input_callback_set(view_port, input_callback, &state); 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(state.input_queue, &event, FuriWaitForever) == FuriStatusOk) { if (event.key == InputKeyBack && event.type == InputTypeShort) { running = false; } if (event.key == InputKeyOk && event.type == InputTypeShort) { state.counter++; } view_port_update(view_port); } } gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(state.input_queue); return 0; }This pattern — a draw callback, an input callback feeding a message queue, and a main loop processing events — is the skeleton nearly every Flipper app builds on. The OK button increments a counter and redraws; the Back button exits cleanly.
Building and Deploying
ufbt build ufbt launchufbt build compiles your app into a .fap file; ufbt launch pushes it to your connected Flipper and runs it immediately, which is the fast iteration loop you'll use constantly during development — edit, build, launch, test, repeat.
Adding GPIO Access
A genuinely useful category of custom app reads or controls the Flipper's GPIO pins — building on what our Flipper GPIO hardware hacking guide covers for raw protocol work, but wrapped in a proper app UI instead of using the built-in generic tools:
#include <furi_hal_gpio.h> // Configure a pin as output furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); // Set it high/low furi_hal_gpio_write(&gpio_ext_pa7, true); furi_hal_gpio_write(&gpio_ext_pa7, false); // Configure a pin as input and read it furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeInput); bool state = furi_hal_gpio_read(&gpio_ext_pa6);This is the foundation for building custom sensor readers, simple controllers, or protocol tools tailored to a specific project rather than relying on the Flipper's generic built-in apps.
Debugging
ToolUse FURI_LOG_* macrosPrint debug output visible over USB serial — FURI_LOG_I("MyApp", "value: %d", x) for info-level logging ufbt cliOpens a serial console to your connected Flipper, where log output appears in real time as your app runs On-screen debug textFor quick checks, just draw variable values directly to the canvas rather than setting up serial monitoring — faster for very simple debuggingPublishing to the App Catalog
Once an app is working well, the official Flipper App Catalog accepts community submissions through a GitHub-based process (application manifest, source, and metadata submitted as a pull request to the catalog's repository) — worth pursuing if you've built something genuinely useful others might want, though entirely optional if you're just building tools for your own use.
Common Beginner Mistakes
MistakeResult Not freeing allocated resources (queues, records) before exitMemory leaks that can destabilize the Flipper over repeated app runs Blocking operations in the draw callbackUI freezes/lags — keep draw callbacks fast, do heavy work elsewhere and just render pre-computed state Wrong icon dimensionsApp fails to build or displays incorrectly in the menu — must be exactly 10x10 for the standard app icon Forgetting furi_record_close for opened recordsResource leaks that can cause issues for other apps or system stabilityThe message-queue-driven event loop pattern above covers a surprising amount of ground once you're comfortable with it — from here, building toward a specific project (a GPIO-based tool, a display-driven game, a protocol utility) is mostly a matter of filling in your own logic around that same skeleton.
Related Guides
- Build a Flipper Zero-Controlled Robot Rover: Motor Driver, Servos, and a Custom Control App
- Build a Flipper Zero GPIO Environmental Sensor Add-On
- Writing Your First FAP App
- How to Install Custom Firmware and Develop Apps for the Flipper Zero
- How to Use the Flipper Zero GPIO for Hardware Hacking: UART, SPI, I2C, and Debugging
- Flipper Zero: Getting Started with BadUSB, Sub-GHz, and NFC
- Flipper Zero GPIO: Reading Sensors and Controlling LEDs
- GPIO Basics on Flipper Zero — Wiring and Using Pins