Flipper Zero 固件资产(Firmware Assets)完全指南:编译流程、命名规则与目录结构解析
2026/9/14 14:02:51 网站建设 项目流程

Flipper Zero 固件资产(Firmware Assets)完全指南:编译流程、命名规则与目录结构解析

【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware

导读:本文以 Flipper Zero 固件仓库根目录的 assets/ReadMe.md 为主体,系统讲解固件资产的编译入口、图标与动画的命名规范、资产目录职责划分,并结合 scripts/assets.py、assets/SConscript 等源码深入剖析 PNG 图标到 C 数组、Dolphin 动画到独立资源包的底层实现。读完本文,你将掌握./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources一条命令背后的完整资产管线,能够为固件正确新增图标与动画资产,并理解 assets/dolphin/ReadMe.md 所定义的 Dolphin 动画配置文件格式。

一、什么是 Firmware Assets

在 Flipper Zero 固件仓库中,"资产(Assets)"泛指所有随固件打包的非代码资源,包括:

  • 图标(Icons):界面按钮、状态栏、应用菜单使用的单帧位图,源文件为 PNG;
  • 动画(Animations):多帧序列(Icons 动画)与桌面海豚(Dolphin)动画,由多张 PNG 帧组合而成;
  • Protobuf 定义:RPC 与存储等模块通信所用的.proto.options文件;
  • Slideshow:桌面系统一次性播放的引导/更新幻灯片;
  • Dolphin 游戏资产:分 internal / blocking / external 三类的桌面海豚动画资源。

这些资产集中在仓库的assets/目录,构建时被编译成 C 头文件/源文件(进入固件镜像)或被打包为资源文件(部署到 SD 卡)。

二、编译入口:一条命令完成全部资产构建

官方文档给出了资产编译的统一命令:

./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources

该命令实际包含 6 个构建目标(alias),其对应的真实构建逻辑在 assets/SConscript 中定义:

命令目标SConscript 对应别名实际产物
iconsassetsenv.Alias("icons", icons)全部 PNG 图标/动画编译为assets_icons.[c,h],进入固件
protoassetsenv.Alias("proto", proto)protobuf/*.proto经 nanopb 生成.c/.h,同时生成protobuf_version.h(对应proto_ver别名)
dolphin_internalassetsenv.Alias("dolphin_internal", dolphin_internal)空闲海豚动画编译为assets_dolphin_internal.[h,c]
dolphin_blockingassetsenv.Alias("dolphin_blocking", dolphin_blocking)阻塞式系统通知动画编译为assets_dolphin_blocking.[h,c]
dolphin_extassetsenv.Alias("dolphin_ext", dolphin_external)外部海豚动画打包为 SD 卡资源目录
resourcesDolphinExtBuilder与后续打包步骤共同产出SD 卡资源文件(含 Manifest)

其中dolphin_ext仅在IS_BASE_FIRMWARE为真时构建(assets/SConscript),即只有基础固件才生成可部署到 SD 卡的外部动画资源。构建产物统一输出到assets/compiled/工作目录(SConscript 中ASSETS_WORK_DIR=env.Dir("compiled")),最终由assetslib = assetsenv.Library("${FW_LIB_NAME}", assets_parts)汇总为一个名为assets的静态库参与固件链接。

提示:./fbt是仓库根目录的构建封装脚本(见 fbt),在 Linux 下直接执行即可,Windows 对应fbt.cmd

三、图标与动画命名规则(Asset naming rules)

官方文档规定图片与动画资产文件名必须符合以下三段式结构:

NAME_VARIANT_SIZE

各段含义如下:

  • NAME(必填):资产名,使用 CamelCase 驼峰命名,仅允许字符[A-Za-z0-9]不允许特殊符号
  • VARIANT(可选):图标变体,用于表示状态或渲染条件,例如active(激活)、inactive(未激活)、inverted(反色);
  • SIZE(必填):像素尺寸。正方形如102024;长方形使用宽x高格式,如10x819x5

命名完成后,图标文件名会被自动加上I_前缀,动画文件名自动加上A_前缀,并统一汇入生成的icon.hicon.c。这一规则可以在 scripts/assets.py 的icons()函数中看到完整实现:

  • 普通图标:icon_name = "I_" + "_".join(filename.split(".")[:-1]),即去掉.png扩展名、用_连接各段、再补I_前缀;
  • 动画(目录内含frame_rate文件):icon_name = "A_" + os.path.split(dirpath)[1],即取目录名加A_前缀,目录名中的-会替换为_

以仓库真实文件为例,assets/icons/StatusBar 目录下的命名完全符合该规范:

  • Alert_9x8.pngI_Alert_9x8
  • Battery_26x8.pngI_Battery_26x8
  • Bluetooth_Connected_16x8.pngI_Bluetooth_Connected_16x8(VARIANT 为Connected
  • Charging-lightning_9x10.pngI_Charging_lightning_9x10(文件名中的-被替换为_

单帧图标 vs 多帧动画的判定

scripts/assets.py 的遍历逻辑以目录中是否存在名为frame_rate的文件来区分两种类型:

  • 图标:目录内直接放置 PNG 文件,每张图生成const uint8_t {name}[] = {...}帧数据与const Icon {name} = {...}图标描述符,frame_count固定为 1;
  • 动画:目录内放置frame_XX.png帧序列与一个frame_rate文本文件(内容为帧率数值,如 assets/icons/Animations/Levelup1_128x64/frame_rate 中的2)。所有帧必须尺寸一致(脚本通过assert width == temp_width强制校验),并按文件名排序生成帧数组,frame_rate从文件读取,frame_count为帧数。

生成产物格式

最终icon.c/icon.h的骨架如下(模板定义见 scripts/assets.py):

// icon.h #pragma once #include <gui/icon.h> extern const Icon I_Alert_9x8; extern const Icon A_Levelup1_128x64; // icon.c #include "assets_icons.h" #include <gui/icon_i.h> const uint8_t I_Alert_9x8_0[] = {...}; // 帧数据 const uint8_t* const _I_Alert_9x8[] = {_I_Alert_9x8_0}; // 帧指针数组 const Icon I_Alert_9x8 = {.width=9,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Alert_9x8};

四、PNG 到 C 数组的底层实现原理

scripts/flipper/assets/icon.py 实现了图标转换的完整管线,共 4 步:

  1. PNG → 1-bit XBM:优先使用 Pillow 将 PNG 转换为 1 位色深的 XBM 格式(im.convert("1")+ImageOps.invert),Pillow 缺失时回退到 ImageMagick 的convertCLI;
  2. XBM 解析:从 XBM 文本中读取widthheight与逐字节像素数据;
  3. Heatshrink 压缩:将像素数据用 heatshrink(窗口 8、前瞻 4)压缩,优先使用heatshrink2Python 模块,缺失时回退到heatshrink命令行工具;
  4. 编码选择:比较压缩前后长度,若压缩后更小(含 2 字节长度头)则采用\x01\x00 + 压缩数据格式,否则使用未压缩的\x00 + 原始数据(icon.py)。

此外脚本会对图像尺寸做上限校验:MAX_IMAGE_WIDTH = 2**16 - 1MAX_IMAGE_HEIGHT = 2**16 - 1(scripts/assets.py),超出即报错退出。

五、Dolphin 与游戏资产:按等级分组的扩展规则

官方文档指出,Dolphin(桌面海豚)资产与游戏资产的命名规则与图标动画相同,但额外要求"按等级(level)分组,且等级作为NAME的前缀"。以 assets/dolphin/internal 目录为例,真实动画目录名L1_Tv_128x47L1_NoSd_128x49即由L1(等级 1)+Tv/NoSd(名称)+128x47/128x49(尺寸)组成。

Dolphin 资产分为三部分(详见 assets/dolphin/ReadMe.md):

类型用途产物
blocking阻塞式系统通知动画(如低电量、无 SD 卡)打包进assets_dolphin_blocking.[h,c]
internal空闲状态的内置海豚动画转换为assets_dolphin_internal.[h,c]
external空闲状态的外部海豚动画打包到资源目录,部署至 SD 卡

Dolphin 动画目录的必备文件

每个 Dolphin 动画目录包含三类文件(见 assets/dolphin/internal/L1_NoSd_128x49):

  • manifest.txt:动画枚举清单,用于随机动画选择,是 Dolphin 的起始入口;
  • meta.txt:描述动画如何绘制(尺寸、帧序列、气泡对话框等);
  • frame_X.png:动画帧位图,编号从 0 开始。

manifest.txt 格式

采用 Flipper Format File(有序键值对)格式,头部固定为:

Filetype: Flipper Animation Manifest Version: 1

每条动画记录包含以下字段(仓库真实示例见 assets/dolphin/internal/manifest.txt):

含义仓库示例值
Name动画名,必须与动画目录名完全一致L1_NoSd_128x49
Min butthurt/Max butthurt海豚"不爽值"(butthurt)的允许区间0/14
Min level/Max level海豚等级的允许区间;若为 0,则该动画不参与随机空闲选择,只能按名称精确触发1/3
Weight随机选择时被选中的权重36

文档特别说明:某些动画可以被排除在随机选择之外(如L1_NoSd_128x49这类需要特定硬件状态触发的动画)。

meta.txt 格式

同样为 Flipper Format File,头部固定为:

Filetype: Flipper Animation Version: 1

字段包括(仓库真实示例见 assets/dolphin/internal/L1_NoSd_128x49/meta.txt):

含义
Width/Height动画宽高(宽 ≤ 128,高 ≤ 64)
Passive frames被动状态位图帧数
Active frames主动状态位图帧数(可为 0)
Frames order帧播放顺序,前 N 个为被动帧、后 M 个为主动帧;X 对应frame_X.bm帧,可重复引用同一帧
Active cycles主动帧的重复周期数,例如主动帧为 6、7 且 Active cycles=3,则完整主动段播放6 7 6 7 6 7;被动 + 主动的完整周期称为total period
Frame rate每秒播放帧数
Duration单次动画播放总秒数
Active cooldown退出主动模式后、再次进入主动模式前需要等待的秒数
Bubble slots气泡(对话框)序列的数量
Slot同一序列气泡的分组号
X/Y气泡左上角坐标
Text气泡文本,换行用\n
AlignH/AlignV气泡角落的水平(Left/Center/Right)与垂直(Top/Center/Bottom)对齐方式
StartFrame/EndFrame气泡在完整周期内显示所覆盖的帧索引范围

帧索引的换算方法

文档给出了一个关键示例帮助理解帧索引(frame indexes)与真实帧顺序(real frames order)的关系。假设配置为:

Passive frames: 6 Active frames: 2 Frames order: 0 1 2 3 4 5 6 7 Active cycles: 4

则索引换算如下:

passive(6) active (2 * 4) Real frames order: 0 1 2 3 4 5 6 7 6 7 6 7 6 7 Frames indexes: 0 1 2 3 4 5 6 7 8 9 10 11 12 13

即:被动段 6 帧依次播放后,主动段的 2 帧按Active cycles重复 4 次,共 8 帧;用于StartFrame/EndFrame的气泡帧索引为累计后的全局索引(0–13)。

六、资产目录结构总览

官方文档给出了assets/目录的结构划分,结合仓库实际布局可汇总如下:

assets/ ├── ReadMe.md # 本文档 ├── SConscript # 资产构建脚本(SCons 集成) ├── dolphin/ # Dolphin 游戏资产源 │ ├── blocking/ # 阻塞式通知动画 → 编译进固件 │ ├── external/ # 外部空闲动画 → 打包到 SD 卡 resources │ ├── internal/ # 内置空闲动画 → 编译进固件 │ └── ReadMe.md # Dolphin 动画格式规范 ├── icons/ # 图标/动画源(PNG + frame_rate) │ ├── StatusBar/ # 状态栏图标,如 I_Battery_26x8 │ ├── Animations/ # 多帧动画目录,如 A_Levelup1_128x64 │ └── MainMenu/ NFC/ SubGhz/ ... # 按功能分组的图标 ├── protobuf/ # Protobuf 源(.proto + .options + Changelog) └── slideshow/ # 桌面一次性幻灯片(first_start、update_default)

各目录的构建去向(官方文档 + assets/SConscript 双重印证):

  • dolphin→ 输出到 build 目录的compiledresources两个文件夹(分别对应编译进固件与部署到 SD 卡);
  • icons→ 输出到 build 目录的compiled文件夹(生成assets_icons.[c,h]);
  • protobuf→ 输出到 build 目录的compiled文件夹(生成 C 代码与版本头);
  • slideshow→ 桌面的单次播放幻灯片资源。

七、重要注意事项

官方文档强调了一条容易被忽视的构建约束:

不要引入未使用的资产,编译器不会剥离未使用的资产。

这意味着每一张 PNG、每一个动画目录最终都会以 C 数组形式被链接进固件镜像(图标走I_/A_前缀进入icon.c,Dolphin 动画走DolphinSymBuilder进入assets_dolphin_*.c)。从 assets/SConscript 可以看出,所有资产部分(icons、proto、dolphin_blocking、dolphin_internal、proto_ver)被统一收进名为assets的静态库,随固件链接,因此资产会直接占用固件 Flash 空间。新增资产前应评估其体积与必要性,尽量复用 assets/icons/Common 等目录下的通用图标。

八、实战:为固件新增一个图标或动画

综合上述规则,新增资产的完整流程如下:

新增单帧图标:

  1. 准备 1-bit(黑白)PNG,按NAME_VARIANT_SIZE.png命名,如MyIcon_Active_10x8.png
  2. 放入 assets/icons 下对应功能目录(如Common)或自建目录;
  3. 执行./fbt icons(或完整的./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources)重新生成assets_icons.[c,h]
  4. 在代码中通过生成的符号I_MyIcon_Active_10x8引用(含I_前缀),该符号声明于生成的icon.h,类型为const Icon

新增多帧动画:

  1. 自建目录,命名如MyAnim_10x8(目录名即动画名);
  2. 放入尺寸一致的frame_00.pngframe_01.png... 帧序列;
  3. 在目录内创建frame_rate文件,内容为整数帧率(如2,参考 assets/icons/Animations/Levelup1_128x64/frame_rate);
  4. 重新编译后通过A_MyAnim_10x8符号引用。

新增 Dolphin 动画:

  1. 在 assets/dolphin/internal(或 blocking/external)下创建Lx_Name_宽x高/目录;
  2. 放入frame_0.pngframe_1.png... 帧与meta.txt(按第五节格式填写);
  3. 将动画条目追加到对应目录的manifest.txt
  4. 重新执行./fbt dolphin_internal或完整构建命令,internal/blocking 类型编译进固件,external 类型由dolphin_ext打包至 SD 卡 resources。

说明:external 类型资产的打包依赖IS_BASE_FIRMWARE构建选项与 SD 卡部署流程,普通固件构建默认只处理 internal 与 blocking 类型。

九、延伸阅读

  • assets/dolphin/ReadMe.md:Dolphin 动画三类资源(blocking/internal/external)与 manifest.txt、meta.txt 的完整格式规范,是本文第五节内容的原始出处;
  • assets/SConscript:资产构建与固件静态库的 SCons 集成逻辑;
  • scripts/assets.py:资产处理命令行工具(iconsmanifestcoprodolphin四个子命令)与图标/动画生成实现;
  • scripts/flipper/assets/icon.py:PNG → 1-bit XBM → heatshrink 压缩 → C 数组的转换管线;
  • fbt:固件构建封装脚本,资产编译命令./fbt ...的入口。

通过本文,你可以从"按文档放置资产文件"跨越到"理解资产如何被编译、压缩、打包并最终进入固件镜像或 SD 卡资源",为深度定制 Flipper Zero 固件打下扎实基础。

【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询