资讯动态

Flipper Zero 计数插件 dolphin-counter 全解析:源码结构、GUI 渲染与 ufbt 部署指南

发布时间:2026/9/14 17:13:14 来源:尧图企业网站定制
Flipper Zero 计数插件 dolphin-counter 全解析源码结构、GUI 渲染与 ufbt 部署指南【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper这是一篇围绕 Flipper Zero 开源插件 dolphin-counter关联文档展开的技术指南。该插件是一个极简的屏幕计数器可通过方向键上/下进行加一、减一操作是理解 Flipper Zero FAP 插件工程结构、GUI 绘制与输入事件处理链路的绝佳入门样本。读完本文你将掌握该插件的完整安装方式、每个源码文件的职责与核心 API 调用关系并具备在此基础上自行改造扩展的能力。一、项目定位一个小而完整的 Flipper Zero 插件原文档对这一插件的定义非常简洁This is a simple plugin for the Flipper Zero. It gives you access to a counter which you can increment and decrement using the up and down buttons respectively.这是一个面向 Flipper Zero 的简单插件你可以分别用上、下按键对计数器进行加一和减一。尽管功能极简但结合源码来看它实际是一个覆盖了 Flipper Zero 应用开发全部核心要点的完整示例在 counter.c 中完整实现了计数功能上限MAX_COUNT 99按 UP 键加一、按 DOWN 键减一减到 0 后不再继续递减见 counter.c#L93-L101退出机制按 BACK 键即可安全退出并释放全部资源按键视觉反馈每次按键时屏幕中央的数字外框会闪烁三层圆角矩形动画boxtimer计数驱动全屏 GUI 渲染以GuiLayerFullscreen图层全屏绘制标题、数字与边框。值得一提的是这份源码在仓库中并非孤本source-OLDER目录下 kyhwana/dolphin-counter-main、grnch/dolphin-counter-main 以及 xMasterX/counter 三处的 README 与counter.c内容完全一致说明这是一个在 Flipper 社区中流传较广、被多方镜像收录的入门插件读者在仓库中无论从哪个副本入手学习效果相同。二、安装与部署用 flipperzero-ufbt 免刷固件快速运行原文档给出的安装建议非常明确推荐使用 flipperzero-ufbt原文Id recommend using flipperzero-ufbt, which is a lightweight tool for quickly testing Flipper Zero applications.其核心优势在于——The app will stay present on your device so it is not necessary to flash the entire firmware.应用会常驻在设备上无需重新刷写整个固件。这意味着插件的部署形态是FAPFlipper Application Package即独立编译、以应用包形式存放于 SD 卡的插件文件。从本仓库 Applications/Official/ReadMe.md 的说明可以看出官方固件的插件文件FAP通常通过应用商店或社区渠道分发而 dolphin-counter 这类源码目录则适合用 ufbt 直接编译生成.fap文件后拷入 SD 卡运行。整个流程可以概括为按 flipperzero-ufbt 项目的说明安装ufbt命令行工具依赖 Python 环境进入本插件的源码目录包含application.fam与counter.c的目录执行构建命令生成 FAP 文件将生成的.fap文件放入 SD 卡的apps目录ufbt 的启动命令可自动完成拷贝并拉起应用在 Flipper Zero 上进入应用程序 → 工具Misc分类即可看到Counter应用并启动。采用 FAP 方式的最大好处是无需为一个小工具重新编译整个固件迭代速度极快这也是原文档推荐 ufbt 的根本原因。需要注意的是本仓库为只读归档实际编译请在本地另行拷贝源码目录后进行。三、工程结构application.fam 清单与图标资源一个 FAP 插件的工程由三部分组成本插件一个不落地全部具备文件作用application.famFAP 元数据清单声明应用 ID、类型、入口点、依赖与分类counter.c全部业务逻辑与 GUI 代码约 108 行icons/counter_icon.png10×10 像素的应用图标Flipper 屏幕为 128×64 单色图标通常极小application.fam是构建系统识别插件的关键配置其字段逐一解析如下App( appidcounter, # 应用唯一 ID内部标识符 nameCounter, # 应用在菜单中显示的名称 apptypeFlipperAppType.PLUGIN, # 应用类型独立插件FAP entry_pointcounterapp, # 入口函数名对应 counter.c 中的 int32_t counterapp(void) requires[ gui, # 声明依赖 gui 子系统GUI、输入、视口相关 ], fap_categoryMisc, # 在应用菜单中归属的分类杂项 fap_iconicons/counter_icon.png, # 菜单图标 fap_icon_assetsicons, # 图标资源目录 )从这份清单可以读出两个关键信息entry_pointcounterapp与 counter.c#L81 的int32_t counterapp(void)一一对应固件加载 FAP 后会以该函数为入口启动应用requires[gui]表明它依赖 Flipper 的 GUI 子系统这正是代码中大量使用gui/gui.h、input/input.h等头文件的编译前提。四、源码深度剖析counter.c 的分层实现counter.c虽然只有百余行却完整展示了 Flipper Zero 应用的标准骨架状态结构体 → 初始化 → 输入回调 → 渲染回调 → 主循环 → 资源释放。下面按源码顺序逐层拆解。4.1 常量与宏屏幕布局如何计算源码开头counter.c#L7-L12定义了一组与 Flipper Zero 128×64 屏幕强相关的宏#define MAX_COUNT 99 // 计数器上限达到 99 后 UP 键无效 #define BOXTIME 2 // 按键反馈外框动画持续帧数 #define BOXWIDTH 30 // 中央数字外框的边长 #define MIDDLE_X 64 - BOXWIDTH / 2 // 外框左上角 X以屏幕水平中心 64 为基准 #define MIDDLE_Y 32 - BOXWIDTH / 2 // 外框左上角 Y以屏幕垂直中心 32 为基准 #define OFFSET_Y 9 // 垂直偏移量为顶部标题让出空间理解这些宏的关键在于 Flipper Zero 屏幕坐标系屏幕宽 128 像素、高 64 像素因此MIDDLE_X实际等于 64 − 15 49MIDDLE_Y等于 32 − 15 17。OFFSET_Y的加入则让数字框整体下移 9 像素避免与顶部居中的标题 Counter :)绘制在 y10 处重叠。4.2 Counter 状态结构与资源生命周期Counter结构体counter.c#L14-L23集中封装了应用的全部运行时状态typedef struct { FuriMessageQueue* input_queue; // 输入事件消息队列容量 8元素为 InputEvent ViewPort* view_port; // GUI 视口负责接收绘制与输入回调 Gui* gui; // GUI 子系统句柄 FuriMutex** mutex; // 互斥锁保护 count 等共享状态 int count; // 当前计数值 bool pressed; // 本次按键是否触发过反馈动画 int boxtimer; // 反馈动画剩余帧数 } Counter;配套的初始化与释放函数形成对称的生命周期管理state_init()counter.c#L67-L79依次完成分配结构体内存 → 创建容量为 8 的FuriMessageQueue→ 分配ViewPort→ 通过furi_record_open(RECORD_GUI)打开 GUI 子系统记录 → 分配普通类型互斥锁 → 注册输入回调与绘制回调 → 以GuiLayerFullscreen全屏图层将视口挂载到 GUIstate_free()counter.c#L25-L32则按相反顺序回收移除视口 → 关闭 GUI 记录 → 释放视口 → 释放消息队列 → 释放互斥锁 → 释放结构体内存。这种申请与释放对称的写法是 Flipper 应用开发的规范姿势任何一处遗漏都会造成资源泄漏。4.3 渲染回调 render_callback字体、对齐与三层边框动画render_callbackcounter.c#L41-L65是每次屏幕刷新时被 GUI 调用的绘制函数逻辑如下加锁furi_mutex_acquire保护绘制期间的状态读取避免与主循环并发修改count清屏与设色canvas_clear清空画布canvas_set_color(canvas, ColorBlack)设定黑色前景128×64 单色屏只有黑/白两色绘制标题以FontPrimary字体、AlignCenter双向居中在(64, 10)绘制字符串Counter :)绘制数字外框切换到FontBigNumbers大数字字体专为数字显示设计随后根据pressed或boxtimer 0决定是否绘制三层圆角矩形canvas_draw_rframe圆角半径 5形成按键瞬间的外框放大闪动反馈效果canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y OFFSET_Y, BOXWIDTH, BOXWIDTH, 5); canvas_draw_rframe(canvas, MIDDLE_X-1, MIDDLE_YOFFSET_Y-1, BOXWIDTH2, BOXWIDTH2, 5); canvas_draw_rframe(canvas, MIDDLE_X-2, MIDDLE_YOFFSET_Y-2, BOXWIDTH4, BOXWIDTH4, 5);第一层是常态边框第二、三层仅在按键反馈期间出现并逐帧递减boxtimer从而在约 2 帧内快速收拢形成清晰的按压反馈绘制数字用snprintf将count格式化为字符串缓冲区scount[5]足以容纳 099 的两位数字在(64, 32 OFFSET_Y)居中绘制解锁furi_mutex_release。值得留意的是即便count小于 10%d也只输出一位数字而不会补零这是实现细节上的一个可扩展点下文第五节会讨论。4.4 输入回调 input_callback从硬件事件到消息队列input_callbackcounter.c#L34-L39在输入事件发生时被 GUI 子系统调用但它只做一件事——将类型为InputTypeShort短按的事件放入消息队列static void input_callback(InputEvent* input_event, void* ctx) { Counter* c ctx; if(input_event-type InputTypeShort) { furi_message_queue_put(c-input_queue, input_event, 0); } }这里体现了 Flipper 应用的标准架构模式回调只负责投递事件真正的业务处理统一放到主循环中串行执行。这避免了在中断/回调上下文中直接操作状态导致的竞态也保证了按键处理与渲染刷新之间的顺序一致性。短按类型之外的长按InputTypeLong、重复InputTypeRepeat等事件在此被有意过滤意味着长按不会连续计数。4.5 主循环 counterapp事件分发、按键逻辑与线程安全应用入口counterappcounter.c#L81-L108构成整个应用的事件驱动核心while(1) { InputEvent input; while(furi_message_queue_get(c-input_queue, input, FuriWaitForever) FuriStatusOk) { furi_check(furi_mutex_acquire(c-mutex, FuriWaitForever) FuriStatusOk); if(input.key InputKeyBack) { // 按 BACK退出应用 furi_mutex_release(c-mutex); state_free(c); return 0; } else if(input.key InputKeyUp c-count MAX_COUNT) { // 上限保护 c-pressed true; c-boxtimer BOXTIME; c-count; } else if(input.key InputKeyDown c-count ! 0) { // 下限保护 c-pressed true; c-boxtimer BOXTIME; c-count--; } furi_mutex_release(c-mutex); view_port_update(c-view_port); // 请求重绘 } }按键语义与边界条件非常清晰按键行为边界条件UP计数值 1仅当count 99MAX_COUNTDOWN计数值 −1仅当count ! 0BACK释放资源并退出无条件每次有效按键都会把pressed置为true并将boxtimer重置为 2随后调用view_port_update触发下一次重绘从而在屏幕上呈现 4.3 节描述的反馈动画。整个处理流程在互斥锁保护下完成保证与渲染回调的并发安全。从源码结构看计数数值仅保存在内存中的c-count退出应用后不会持久化——代码中未使用 Flipper 的storageAPI也没有任何文件读写操作这是该插件极简定位的直接体现详细扩展方案见下节。4.6 线程模型小结三件套协作综合来看该应用并发模型由三件套协作完成FuriMessageQueue消息队列连接输入回调与主循环实现事件的生产者—消费者解耦FuriMutex互斥锁保护count、pressed、boxtimer等共享状态避免渲染线程与主循环同时读写ViewPort视口GUI 子系统与业务代码之间的桥梁承载输入/绘制两个回调并负责图层管理。这套模式是所有 Flipper Zero GUI 应用甚至更大规模应用的统一骨架理解 dolphin-counter 就相当于掌握了 Flipper 应用开发的第一课。五、改造与扩展把这个计数器变成你自己的工具基于以上源码结构读者可以在不推翻整体框架的前提下做多种实战改造以下方案均以本仓库源码为出发点1. 调整计数范围修改 counter.c#L7 的MAX_COUNT即可改变上限例如改为999。但需同步注意两点一是scount[5]缓冲区需扩为scount[6]以上以容纳三位数字二是数字变宽后BOXWIDTH 30的边框可能不够宽需一并加大。2. 增加清零与长按连加在counterapp的按键分支中增加InputKeyOk分支即可实现一键归零若要支持长按连加只需将input_callback中过滤条件从InputTypeShort放宽为同时接收InputTypeRepeat并将重复事件也投递入队。3. 支持负数当前 DOWN 在count 0时被限制counter.c#L97。移除该条件并配合snprintf的%d格式化即可天然显示负数只需为负号预留一位缓冲区空间。4. 数据持久化当前计数不落盘。若希望重启后保留数值需要引入 Flipper 的 Storage APIstorage/storage.h与FuriString在state_init时读取配置文件、退出前写回——这是从现有代码结构可以自然延伸的方向。5. 界面定制标题字符串、字体FontPrimary/FontBigNumbers、外框圆角半径canvas_draw_rframe第 5 个参数均为直接可改的常量也可以将单色外框换成canvas_draw_icon加载自定义图标fap_icon_assets已声明图标目录。六、总结dolphin-counter 是一个麻雀虽小、五脏俱全的 Flipper Zero FAP 插件范例它的 README 明确了基于 ufbt 免刷固件部署的安装路线而 counter.c 则完整示范了application.fam清单声明、GUI 全屏视口、输入事件队列、互斥锁保护的渲染回调与事件主循环这一整套标准开发范式。对于希望入门 Flipper Zero 应用开发、或想快速生成一个能在屏幕上交互的原型的开发者而言以本插件为起点配合 xMasterX/counter 等仓库内同名副本交叉参考是最直接有效的学习路径。【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价