A fun journey to discover Flipper Zero and how a FAP (Flipper-zero APplication) is developed.
VERY IMPORTANT: Most likely I will write some bullshit: feel free to clone my repository, try, change and why not, improve my code.
FlipperZero uses a particular structure to build its application called SCons (more information can be found here: https://scons.org): this ambitious project is a substitute of the classic MAKE, and the configurations files are Python scripts that use the power of a real programming language to solve build problems.
NOTE: for those that are interested learning more about SCons, I advise you to NOT continue. This tutorial helps you create applications for your FlipperZero, but does not provide you with information about compilation and/or internal FreeRTOS.
A The Flipperzero APplication FAP has a particular filesystem structure like this:
[FAP-name]
โโโ application.fam (type: text file, compulsory)
โโโ icon.png (type: image, optional)
โโโ README.md (type: text file, optional)
โ
โโโ [images] (type: folder, optional)
โ โโโ {all the images required by the FAP}
โ
โโโ main.c (type: text file, optional)
โโโ {others .c/.h files used by the FAP}
Let's analyze the parts of a FAP:
If you want to create a FAP, this file is mandatory. For example, if you are familiar with Java/Kotlin (especially for Android OS) you will probably remember AndroidManifest.xml. The concept is the same. If you do not know what a manifest is, don't worry: this is a simple text (ascii) file with a list of "declarations" required to run the FAP correctly. Within application.fam, you will find all the information regarding the behavior of the FAP. You can specify the resources that the FAP will use (like images), which kind of application it will be (a plugin or external), the entry point of the FAP (the appid), the category, and so on. Most of them will be analyzed individually, allowing you to implement the functionality of all of the statements.
When you turn your Flip.x0 on, you see many FAP in the various menus you navigate. Every FAP has a name and an icon, that means you can personalize your apps as you want.
For convenience, in this tutorial I will use the name icon.png for the small FAP icons we will create together. But feel free to change the name of the file and its declaration inside the application.fam (we will see this later). Remember that the declaration of an icon for a FAP is not necessary. When you compile your application, the icon will be not shown in the list and you will only see the name with a blank space on the left.
I don't want to dwell too much on this part. Everyone knows (if you're a developer) the importance of a README file, Especially if you want to distribute your FAP using the most common channels like GitHub or something like it.
if your FAP needs to display some images, it's a good reason for keep all the resources organized inside a folder. In the same way, feel free to change the name of this folder as you prefer, but remember; change the name in the application.fam.
Somewhere the main function will be defined, right? During the compilation phase, the SCons system will check the contents of all .c files inside your FAP folder and it will recognize the name of the main function (declared inside the application.fam). So...it's better to keep the code organized. It doesn't matter what the names of the .c files inside you FAP application are. The important thing, for your convenience, is to have a .c file with an easily recognizable name. One that allow you to understand where the main entry of your application is. If you know the C language, you probably well understand what am I referring to:
int main(int argc, char *argv[]) {
// Do something here...
return 0;
}
For a Flipper Zero Application, we need the same thing. Keep calm, we will return to it later in detail.
This is an example of the contents of the manifest for your Flip.x0 application.
Let's use my_first_app as an example (inside the 01_my_very_first_fap/my_first_app/ folder of this repository)
You will only find two files: - my_first_app.c - application.fam
The content of the application.fam is the following:
App(
appid="my_first_app",
name="My First App",
apptype=FlipperAppType.EXTERNAL,
entry_point="my_first_app_main",
stack_size=1 * 1024,
fap_category="Examples",
# Added for explanation
fap_icon="icon.png",
fap_icon_assets="images",
)
Let's analyze now all the declaration of the fam.
This instruction helps your Flip.x0 identify your FAP inside its ecosystem. The appid has to be unique. Otherwise, the compiler (fbt) will throw an error. My suggestion is to choose a name that can be understood easily.
This field represents the string that will be displayed on your Flip.x0 when you use the browser, as shown below.
This directive specifies which kind of FAP you want to create. Currently, the documentation is very poor and I only found four types of apptype:
- FlipperAppType.PLUGIN
- FlipperAppType.EXTERNAL
- FlipperAppType.SETTINGS
- FlipperAppType.METAPACKAGE
PLUGIN is used if you want to create a FAP to interact with of Flip.x0.
The plugins are compiled into the kernel and will be flashed as part of the firmware on the main SoC. Furthermore, writing plugins requires the developer to edit/maintain /applications/meta/applications.fam
EXTERNAL is used (as in this case) for delopy FAP The compiled FAP are separated programs and you can store the build version (.fap file) inside your Flip.x0 microSD.
SETTINGS I'm not an expert, but I suppose that if you want to develop a FAP using the Settings app-type, you probably want to have integration with the FreeRTOS inside your Flip.x0
METAPACKAGE Unfortunately, I do not understand what this option represents. I will satisfy my curiosity when the the documentation becomes available.
FOUND Something new !! METAPACKAGE : Does not define any code to be run, it is used for declaring dependencies and application bundles.
All appplication type can be found here: https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md#application-definition
Is the main function of your FAP, like C with the main function. Here you have different naming options, but the concept is the same. As an example,I have a portion of code:
int32_t my_first_app_main(void* p) {
UNUSED(p);
}
As you can see above, this kind of function is equivalent to this one when you write in pure C (forUnix/Linux).
int main(int argc, char *argv[]) {
// Do something here...
return 0;
}
I don't think there is much need to explain this statement. You can specify the size of the stack inside your FAP :P
Use this declaration if you want to give to your FAP a bit of elegance. In this case, we have an "icon.png" as a FAP icon. The important information you need to know are:
- the size of the icon: 10 pixel x 10 pixel
- the colors palette: only white and black are allowed
This directive specifies a folder name and basically tells your Flip.x0 where to load the images to draw into the GUI. If you use this declaration, make sure to have the folder created before compiling the FAB. Otherwise the compilation phase will fail.
Let's use the first application in this tutorial as an example. First, open a terminal and move yourself into your favorite folder for development. Use git to clone the official repository of Flip.x0 firmware. Or use a custom firmware like RogueMaster, as I did. If you want to use the official firmware repository you need launch this command:
git clone https://github.com/flipperdevices/flipperzero-firmware.git
Otherwise (like me) this is the RogueMaster firmware:
git clone https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git
Once the repository is cloned, enter the firmware folder and explore what is inside:
drwxrwxr-x 19 user user 4096 dic 22 13:35 .
drwxrwxr-x 14 user user 4096 dic 23 12:49 ..
drwxrwxr-x 9 user user 4096 dic 18 14:09 applications
drwxrwxr-x 5 user user 4096 dic 21 21:09 applications_user
drwxrwxr-x 8 user user 4096 dic 18 14:09 assets
drwxrwxr-x 2 user user 4096 dic 18 14:09 brew-cask
-rw-rw-r-- 1 user user 134 dic 18 14:09 Brewfile
drwxrwxr-x 4 user user 4096 dic 21 21:56 build
-rwxrwxr-x 1 user user 643 dic 20 09:48 buildRelease.sh
-rw-rw-r-- 1 user user 3419 dic 20 09:48 CHANGELOG.md
-rw-rw-r-- 1 user user 5358 dic 18 14:09 .clang-format
-rw-rw-r-- 1 user user 5226 dic 18 14:09 CODE_OF_CONDUCT.md
-rw-rw-r-- 1 user user 2874 dic 18 14:09 CODING_STYLE.md
-rwxrwxr-x 1 user user 92 dic 21 21:55 compile.sh
-rw-rw-r-- 1 user user 4764 dic 18 14:09 CONTRIBUTING.md
drwxrwxr-x 4 user user 4096 dic 18 14:09 debug
drwxrwxr-x 3 user user 4096 dic 21 21:56 dist
drwxrwxr-x 3 user user 4096 dic 20 09:48 documentation
-rw-rw-r-- 1 user user 9486 dic 18 14:09 .drone.yml
-rw-rw-r-- 1 user user 173 dic 18 14:09 .editorconfig
-rwxrwxr-x 1 user user 727 dic 18 14:09 fbt
-rw-rw-r-- 1 user user 388 dic 18 14:09 fbt.cmd
-rw-rw-r-- 1 user user 2271 dic 18 14:09 fbt_options.py
drwxrwxr-x 3 user user 4096 dic 18 14:09 firmware
-rw-rw-r-- 1 user user 8047 dic 18 14:09 firmware.scons
drwxrwxr-x 3 user user 4096 dic 18 14:09 furi
-rw-rw-r-- 1 user user 605 dic 18 14:09 GAMES_ONLY.md
drwxrwxr-x 9 user user 4096 dic 22 13:35 .git
-rw-rw-r-- 1 user user 64 dic 18 14:09 .gitattributes
drwxrwxr-x 3 user user 4096 dic 18 14:09 .github
-rw-rw-r-- 1 user user 624 dic 22 13:35 .gitignore
-rw-rw-r-- 1 user user 1198 dic 18 14:09 .gitmodules
drwxrwxr-x 34 user user 4096 dic 18 14:09 lib
-rw-rw-r-- 1 user user 35149 dic 18 14:09 LICENSE
-rw-rw-r-- 1 user user 3393 dic 18 14:09 MACOS_GUIDE.md
-rw-rw-r-- 1 user user 1259 dic 18 14:09 Makefile
-rw-rw-r-- 1 user user 54919 dic 20 09:48 patreon.png
-rw-rw-r-- 1 user user 575 dic 18 14:09 .pvsconfig
-rw-rw-r-- 1 user user 279 dic 18 14:09 .pvsoptions
-rw-rw-r-- 1 user user 31567 dic 22 13:35 ReadMe.md
-rw-rw-r-- 1 user user 3986 dic 18 14:09 RoadMap.md
-rw-rw-r-- 1 user user 9881732 dic 22 09:50 .sconsign.dblite
-rw-rw-r-- 1 user user 8326 dic 18 14:09 SConstruct
drwxrwxr-x 9 user user 4096 dic 18 14:55 scripts
drwxrwxr-x 3 user user 4096 dic 18 14:09 site_scons
-rw-rw-r-- 1 user user 1080 dic 20 09:48 SUPPORT.md
-rw-rw-r-- 1 user user 10760 dic 18 14:09 test_iso7816_helpers.c
-rw-rw-r-- 1 user user 20665 dic 18 14:09 test_mrtd_helpers.c
drwxrwxr-x 3 user user 4096 dic 18 14:11 toolchain
drwxrwxr-x 3 user user 4096 dic 18 14:13 .vscode
it will be possible to notice an executable file inside the firmware folder called fbt that is the core of our entertainment.
For more information about fbt please use the following link: https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md
As you can read from the documentation, fbt stands for Flipper Build Tool and it's the entry point for firmware-related commands and utilities. It is invoked by ./fbt
in the firmware project root directory. Internally, it is a wrapper around the SCons build system.
Personally, for the development of IoT firmware I am very happy with Visual Studio Code, but you can use what you prefer.
fbt
includes basic development environment configuration for VSCode.
To deploy it, run ./fbt vscode_dist
from the Flip.x0 firmware folder.
That will copy the initial environment configuration to a .vscode
folder. After that, you can use that configuration by starting VSCode and choosing the firmware root folder in "File > Open Folder" menu.
- On first start, you'll be prompted to install recommended plug-ins. Install them for the best development experience. You can find a list of them in
.vscode/extensions.json
. - Basic build tasks are invoked in Ctrl+Shift+B menu.
- Debugging requires a supported probe. That includes:
- Wi-Fi devboard with stock firmware (blackmagic),
- ST-Link and compatible devices,
- J-Link for flashing and debugging (in VSCode only). Note that J-Link tools are not included with our toolchain and you have to download them yourself and put on your system's PATH.
- Without a supported probe, you can install firmware on Flipper using USB installation method.
Finally it's time to fill out our first application. For this part of the tutorial, I will use my_first_app as an example.
Navigate into the folder tree of the Flip.x0 firmware and enter the application_user
folder.
Copy the folder my_first_app into the application_user
folder.
The content should be something like this:
user@yourpc:flipperzero-firmware-wPlugins/applications_user$ tree
.
โโโ my_first_app
โ โโโ application.fam
โ โโโ my_first_app.c
โโโ README.md
1 directory, 3 files
user@yourpc:flipperzero-firmware-wPlugins/applications_user$
Now return to the root of Flip.x0 firmware with a cd ..
command and prepare yourself for the compilation phase.
On the terminal type:
./fbt fap_my_first_app
and wait for the magic.
When you create a FAP and want to compile that application, you need to run the compiler with the './fbt' command before the name of your app. In this case, the name of the FAP is *my_first_app (that is also the name of the folder...) the word fap_
before the name tells SCons to compile THAT application.
Every kind of application must be compiled with a fap_
as prefix before its name.
A small example. Suppose you have an amazing FAP to compile and its name is awesome_program (as the name of the folder in which it is contained). The command for compilation will be the following:
./fbt fap_awesome_program
Now let's go back to the previous stage, and see what happened to the FAP compilation.
2022-12-24 16:44:41,216 [INFO] Packing
LINK build/f7-firmware-C/.extapps/my_first_app_d.elf
2022-12-24 16:44:41,291 [INFO] Complete
2022-12-24 16:44:41,305 [INFO] Complete
PBVER build/f7-firmware-C/assets/compiled/protobuf_version.h
PREGEN build/f7-firmware-C/sdk_origin.i
SDKSRC build/f7-firmware-C/sdk_origin.i
SDKCHK firmware/targets/f7/api_symbols.csv
API version 11.3 is up to date
APPMETA build/f7-firmware-C/.extapps/my_first_app.fap
FAP build/f7-firmware-C/.extapps/my_first_app.fap
APPCHK build/f7-firmware-C/.extapps/my_first_app.fap
Near the end of our compilation output, we can clearly see that we successfully created our FAP binary. Your first FAP is waiting for you in this folder!
build/f7-firmware-C/.extapps/my_first_app.fap
At this point we can deploy our application in two different ways. Let's see how we can proceed.
For deploy our FAP into our Flip.x0, we can proceed in two ways:
- Launch the application using the
./fbt
command - Copy the compiled FAP inside our Flip.x0
1) Using the Flipper Build Tool
Connect your Flip.x0 to your computer using the USB-Type C cable (the device will be powered on automatically). Make sure that the serial device associated to your Flip.x0 is not used by any program. Close any instance of the qFlipper application or any other kind of software that can perform serial communication with your Flip.x0 like minicom or miniterm.
When you're ready, type the following command:
./fbt launch_app APPSRC=my_first_app
After some time the Flipper Build Tool (fbt) will call the script runfap.py
and as if by magic, your first FAP will appear on your Flip.x0!
You can exit from the application by pressing the back button.
2) Copy the .fap file inside your Flip.x0 and run the application manually Locate your .fap binary reading the output of the compilation phase. In my case the file was created into this folder, and the full path is:
[your_working_folder]/flipperzero-firmware/build/f7-firmware-C/.extapps/my_first_app.fap
Launch the qFlipper application, select the file-manager, then drag 'n' drop the file into the folder Examples
(just for keep all the stuff organized) like in the image below.
Once you complete the drag n' drop operation, you can find your FAP in the list of applications and it will be accessible by pressing the center button (OK) and navigate through Applications โ Examples โ My First App
Learning, for sure :) Now you know how to compile and deploy FAP inside your Flip.x0: all you have to do is learn to develop with the examples that I will add into this repository.
The code will be strong commented to allow you to better understand all the API calls to FreeRTOS and Furi
And here, a small gift for you: https://ilin.pt/stuff/fui-editor/
It's a web-base application for build Flip.x0 GUI very easily.
N. | Example name | Small description |
---|---|---|
0x01 | My First App | The simplest possible FAP, using GUI |
0x02 | Keypad and GUI timer | This FAP showing you how to interact with the Flip.x0 buttons and timer |
0x03 | Notification | Learn how to use the notification system |
0x04 | More notifications | Let's see how to generate sounds, using the integrated LED and Vibration |
0x05 | File I/O | Basic filesystem operations (create, read, write, rename and delete) a file on microSD |
0x06 | Logging | Learn how to integrate the serial logging through FlipperCLI |
0x07 | Threading | Simple usage of the FuriThread and how to implements them |
0x08 | Advanced GUI (pt.1) | Learn how to use advanced GUI component like dialogs, view, view_dispatcher, etc... |
0x09 | Advanced GUI (pt.2) | Learn how to use advanced GUI component like Submenu, TextInput, TextBox and personalized buttons |
0x10 | UART (code in preparation) | Basic use of the GPIO with the UART system (Serial interface) |