Flipper Zero 二维码显示应用 flipperzero-qrcode 实战指南:从 .qrcode 文件制作、模式选择到源码级原理解析
【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper
Flipper Zero 的 64×64 像素小屏幕也能稳定展示可供手机扫码识别的二维码。本文以开源仓库 Flipper 中收录的flipperzero-qrcode应用(v1.1.0)为对象,完整讲解.qrcode文件格式、四种编码模式(Numeric / Alpha-Numeric / Binary / Kanji)的取舍、Wi-Fi 二维码的实战写法、应用操作与 fbt 编译流程,并结合源码深入解析其自动模式探测、容量表驱动的版本/ECC 选择、屏幕渲染与交互逻辑。读完本文,你可以自行制作任意内容的 Flipper 二维码文件,并理解"小屏二维码"的容量上限与踩坑点。
一、应用背景与定位
flipperzero-qrcode是一个运行在 [Flipper Zero](https://link.gitcode.com/i/04dc8c860d635fe1ed41a6ced4f0a0b7/blob/41fc08dbc4c53cf2c87dd4f2de3d3a5fe3eb016f/Applications/Custom (UL, RM)/Unleashed/ReadMe.md?utm_source=gitcode_repo_files) 上的外部应用(.fap),核心功能只有一个:把文本消息实时渲染成二维码显示在屏幕上。它是社区作品(作者 Bob Matcuk,即 README 中提到的 bmatcuk),在其 v1.1.0 时代曾以"源码 + 预编译 fap"的形式分发。
从仓库收录情况看,该应用源码以压缩包形式归档在 flipperzero-qrcode-1.1.0 目录下(含flipperzero-qrcode-1.1.0.zip),压缩包内共 10 个文件,主体是:
qrcode.c/qrcode.h:二维码编码核心库(基于 ricmoo/QRCode,源自 Project Nayuki 的 QR-Code-generator),约 32 KB 源码;qrcode_app.c:应用主逻辑(文件加载、模式探测、渲染、按键交互),约 20 KB;application.fam:FAP 应用的构建清单;scripts/:面向 CI 的固件版本检查/更新脚本。
这是一个"文件驱动、纯离线"的应用:它不联网生成二维码,而是读取 SD 卡上预置的文本文件,在本机完成编码与渲染,因此完全适合在无法联网的场合使用。
二、安装与目录约定
2.1 安装方式
README 给出的安装方式是把编译好的qrcode.fap复制到 SD 卡的apps/Tools目录(与application.fam中声明的fap_category="Tools"一致),然后在 SD 卡根目录新建qrcodes文件夹。使用 qFlipper 等工具时,操作步骤为:
- 将
qrcode.fap拖入apps/Tools; - 返回 SD 卡根目录(与
infrared、nfc等目录同级); - 新建名为
qrcodes的文件夹。
qrcodes这个目录名并非随意约定,它由源码中的宏直接硬编码:
#define QRCODE_FOLDER ANY_PATH("qrcodes") #define QRCODE_EXTENSION ".qrcode"参见 qrcode_app.c(解压后)第 14–15 行。应用启动时会自动以ANY_PATH("qrcodes")作为文件浏览器的base_path,只展示该目录下的.qrcode文件。
2.2 文件浏览与命令行参数
qrcode_app入口(qrcode_app.c第 473 行起)支持两种启动方式:
- 无参数启动:弹出内置文件浏览器(
dialog_file_browser_show),定位到qrcodes目录,文件过滤器为.qrcode扩展名,且hide_ext = true(隐藏扩展名显示); - 带参数启动:如果传入
p且非空,直接以该路径作为文件路径加载,显示完二维码后按 Back 即退出,不会循环回到浏览器。
while (true) { if (p && strlen(p)) { furi_string_set(file_path, (const char*)p); // 直接使用传入路径 } else { ... dialog_file_browser_show(...); // 否则弹出文件浏览器 } ... if (p && strlen(p)) break; // 带参启动,看完即退 }这一设计意味着该应用可以被其他应用或脚本以"指定文件路径"的方式拉起,属于可复用的组件式入口。
三、.qrcode 文件格式详解
3.1 文件模板
.qrcode文件本质是纯文本文件,内容格式如下:
Filetype: QRCode Version: 0 Message: your content here对应源码中的常量:
#define QRCODE_FILETYPE "QRCode" #define QRCODE_FILE_VERSION 0文件加载过程(qrcode_load_file,第 383 行起)使用 FlipperFormat 解析:
flipper_format_file_open_existing打开文件;flipper_format_read_header读取头部,校验Filetype必须等于"QRCode"且Version必须等于0,否则直接报错"Incorrect file format or version";flipper_format_read_string(file, "Message", temp_str)读取Message:字段;- 把
Message字符串交给qrcode_load_string编码生成二维码。
Version: 0是文件格式版本号(当前固定为 0),与二维码的"版本"(1–11)是两个完全不同的概念,不要混淆。该字段用于将来文件格式演进时的兼容性判断。
3.2 Message 的编码流程
qrcode_load_string(第 298 行起)是整个应用最核心的函数,其处理流程是:
- 计算消息长度
len; - 自动探测模式:依次调用
is_numeric和is_alphanumeric判断消息是否为纯数字或字母数字,否则默认使用 Binary(字节)模式:
uint8_t mode = MODE_BYTE; if (is_numeric(cstr, len)) mode = MODE_NUMERIC; else if (is_alphanumeric(cstr, len)) mode = MODE_ALPHANUMERIC;- 最小版本选择:从 version 0 开始,在
MAX_LENGTH[mode][ecc][version]容量表中查找能容纳len的最小版本,优先使用小版本以最大化每个模块(module)的像素尺寸,提升扫码成功率; - 最大 ECC 选择:在最小版本下,从
ECC_HIGH(3)向下寻找能容纳消息的最高纠错级别; - 调用
rebuild_qrcode完成编码。
这三步可以概括为"最小版本 + 该版本下最高纠错"策略——这是 README 提到"自动选择最佳模式"的底层实现。
四、四种编码模式与容量权衡
4.1 模式总览
二维码标准定义了四种数据编码模式,本应用支持前三种:
| 模式 | 允许字符 | 容量(相对) | 典型用途 |
|---|---|---|---|
| Numeric(数字) | 仅0–9 | 基准(最高) | 电话号码、纯数字 ID |
| Alpha-Numeric(字母数字) | 数字、大写字母、空格及$%*+-./: | 约为数字模式的 60% | 纯大写的域名(如HTTP://EXAMPLE.COM) |
| Binary(字节/二进制) | 8-bit 字节(Latin-1) | 约为数字模式的 40%,比字母数字少约 30% | 含路径的 URL、混合大小写文本 |
| Kanji(日文汉字) | 日文 Shift-JIS 汉字 | — | 本应用不支持 |
模式常量定义于 qrcode.h(解压后)第 51–53 行:
#define MODE_NUMERIC 0 #define MODE_ALPHANUMERIC 1 #define MODE_BYTE 2注意get_mode_char(qrcode_app.c第 88 行)仍为MODE_KANJI(3)保留了'K'字符映射,但编码库本身不产生该模式。
4.2 Numeric 模式
只包含数字。这是容量最高的模式,适合电话号码、学号、订单号等纯数字数据。README 特别提醒:想用数字模式,就绝不要在文件里混入任何多余标点(空格、连字符、括号都会让探测器切换到更低效的模式)。
4.3 Alpha-Numeric 模式
字符集为:数字0–9、大写字母A–Z、空格以及$%*+-./:九个符号。探测逻辑见is_alphanumeric(qrcode_app.c第 220 行起),其允许字符集与编码库getAlphanumeric(qrcode.c第 102 行起)严格一致。
适合编码 URL 的场景:仅域名部分且使用大写,例如HTTP://EXAMPLE.COM。因为域名通常不含大小写敏感信息、又大量使用.和/等合法符号,正好落在字母数字字符集内。但如果 URL 带有路径(通常区分大小写),就必须退回 Binary 模式。
4.4 Binary 模式
名为"二进制",实际指按 8-bit 字节编码。二维码标准规定文本使用 ISO-8859-1(Latin-1)编码,而不是现代常见的 UTF-8。这带来一个实用约束:为保持标准兼容,消息应限于拉丁字母、数字和符号;某些扫码器会自动嗅探 UTF-8 从而能识别中文等内容,但这属于"可能可用"而非标准行为,不能保证所有扫码器都能读。
容量方面:Binary 模式比 Numeric 模式少约 60% 容量,比 Alpha-Numeric 少约 30%。
4.5 Kanji 模式与屏幕容量上限
Kanji 模式不受支持,原因是底层 QRCode 库(ricmoo 版)的限制。这是 README 明确声明的事实。
另外,应用对二维码版本做了硬上限MAX_QRCODE_VERSION 11(qrcode_app.c第 23 行),原因也在源码注释中写得很清楚:
/** * Maximum version is 11 because the f0 screen is only 64 pixels high and * version 12 is 65x65. Version 11 is 61x61. */ #define MAX_QRCODE_VERSION 11Flipper Zero 屏幕仅 64 像素高,版本 11 二维码为 61×61 模块,是屏幕能容纳的最大尺寸;版本 12(65×65)已超出屏幕。若消息超过版本 11 的容量,加载会失败并提示Message is too long.(too_long标志位触发,见render_callback第 178–181 行)。
4.6 容量表:数字背后的硬约束
应用内置了一张按"模式 × ECC 级别 × 版本(1–11)"组织的最大字符长度表(MAX_LENGTH,qrcode_app.c第 26–48 行)。下表摘录各模式在 ECC Low 下的容量分布,可直观看出"小屏二维码"能塞多少数据:
| 版本 | 数字 | 字母数字 | 二进制 |
|---|---|---|---|
| 1 | 41 | 25 | 17 |
| 5 | 255 | 154 | 106 |
| 7 | 370 | 224 | 154 |
| 11(上限) | 772 | 468 | 321 |
以版本 11 + ECC Low 为例,最多只能编码 321 个字节(Binary 模式);若把 ECC 提到 High,容量进一步降至 137 字节。这就是 README 强调"屏幕小、数据多时很多扫码器读不出来"的根源。
五、Wi-Fi 二维码实战
5.1 标准格式
绝大多数手机系统支持扫描二维码直接连接 Wi-Fi。Wi-Fi 二维码的消息遵循工业通用格式:
Filetype: QRCode Version: 0 Message: WIFI:S:<ssid>;P:<password>;T:<encryption>;字段说明:
| 字段 | 含义 | 取值 |
|---|---|---|
S: | 网络名(SSID) | 你的 Wi-Fi 名称 |
P: | 密码 | Wi-Fi 密码 |
T: | 加密方式 | WPA、WEP;开放网络填None |
H: | 隐藏网络标记(可选) | true表示网络不广播 SSID |
; | 字段分隔符 | 每个字段以分号结尾 |
几种常见变体:
- 开放网络(无密码):
T:填None,并可去掉P:<password>;段; - 隐藏网络:在末尾追加
H:true;。
5.2 特殊字符转义
如果 SSID 或密码包含\";,:中任意字符,必须在其前面加反斜杠\进行转义。例如:SSID 为wifiball、隐藏不广播、密码为pa$$:word、WPA 加密,则消息为:
Message: WIFI:S:wifiball;P:pa$$\:word;T:WPA;H:true;这里$不需要转义(不在转义字符集内),而密码中的:必须写成\:。
5.3 其他实用消息示例
根据 README 中作者的实测(作者成功让 iPhone 读取了电话号码、Wi-Fi 信息和 URL,最高到版本 11 二维码),可参考以下消息写法:
# 电话号码(纯数字 → Numeric 模式,容量最优) Message: TEL:+1234567890 # URL(域名部分大写 → Alpha-Numeric 模式) Message: HTTP://EXAMPLE.COM注意:电话号码建议采用TEL:前缀的行业标准格式,但任何纯数字消息本身也会触发最高效的 Numeric 模式。
六、应用操作指南
6.1 基本操作流程
应用启动后自动打开文件浏览器并定位到qrcodes目录:
- 选择文件:用方向键(上下)在
.qrcode文件间移动,按中间键(OK)确认,二维码随即全屏显示; - 查看统计:按右键显示二维码统计信息;
- 隐藏统计:按左键隐藏;
- 返回浏览:按 Back 键返回文件浏览器;
- 退出应用:在文件浏览器中按 Back 键退出。
6.2 统计面板:Version / ECC / Mode
按右键后,屏幕右侧显示三项信息(对应render_callback第 136–174 行的绘制逻辑):
- Ver(Version):二维码版本号,直接对应二维码的物理尺寸(模块数 = 17 + 4 × version);
- ECC:纠错级别,取值为
L(Low,约 7%)、M(Medium,约 15%)、Q(Quartile,约 25%)、H(High,约 30%)。它决定二维码对污损、脏屏、划痕的抵抗能力; - Mod(Mode):编码模式,显示
N(Numeric)、A(Alpha-Numeric)、B(Binary)、K(Kanji)。
模式字符与 ECC 字符的映射分别见get_mode_char与get_ecc_char(qrcode_app.c第 74–96 行)。
6.3 手动调整 Version 与 ECC
统计面板下还可以手动改参数(input_callback与主循环第 503–573 行实现):
- 用上/下方向键在
Ver和ECC两项之间切换选中(右侧出现▶指示符); - 按 OK 进入"编辑"模式(选中项旁出现上下箭头);
- 编辑模式下上/下方向键增减数值:
- Version:可在
min_version(消息能容纳的最小版本)与 11 之间增减; - ECC:可在 0(L)与当前版本允许的最高值之间增减——若版本已大于最小版本,最高可到
H(代码第 532 行uint8_t max_ecc = instance->set_version == instance->min_version ? instance->max_ecc_at_min_version : ECC_HIGH;即体现"升级版本可解锁更高纠错"的逻辑);
- Version:可在
- 再次按 OK 确认并重新生成二维码(
rebuild_qrcode),或改回数值后按 OK 取消。
作者自述该功能"mostly added for my own amusement and testing",但理论上有一个实际用途:如果默认参数下扫码器读不出(默认 ECC 低于最高的H),可以把 Version +1 再设 ECC 为H,通过更高纠错冗余提高容错率——效果因扫码器而异。
6.4 失败提示
加载失败时,屏幕显示 "Could not load qrcode.";若因消息过长(超过版本 11 容量),还会追加一行 "Message is too long."(render_callback第 176–181 行)。
七、从源码编译
7.1 环境与目录
编译需要 Flipper Zero 固件仓库(flipperzero-firmware)。传统流程如下:
git clone git@github.com:flipperdevices/flipperzero-firmware.git cd flipperzero-firmware/applications_user git clone git@github.com:bmatcuk/flipperzero-qrcode.git把本应用源码克隆进固件的applications_user目录后,回到固件根目录用 fbt 构建:
cd .. ./fbt fap_qrcodefbt 会自动安装依赖并编译,产物为build/f7-firmware-D/.extapps/qrcode.fap(fbt 输出会显示实际的 .fap 路径,将来若有变化以输出为准)。
7.2 应用清单:application.fam 的字段解读
application.fam(压缩包内)以 Flipper 应用清单 DSL 声明了应用的全部元数据:
App( appid="qrcode", name="qrcode", fap_version=(1,1), fap_description="Display qrcodes", fap_author="Bob Matcuk", apptype=FlipperAppType.EXTERNAL, entry_point="qrcode_app", stack_size=2 * 1024, cdefines=["APP_QRCODE"], requires=["gui", "dialogs"], fap_category="Tools", fap_icon="icons/qrcode_10px.png", fap_icon_assets="icons", )关键字段的工程含义:
apptype=FlipperAppType.EXTERNAL:编译为外部.fap,无需刷固件即可运行;entry_point="qrcode_app":对应qrcode_app.c中的int32_t qrcode_app(void* p)入口函数;stack_size=2 * 1024:应用栈 2 KB——二维码编码是 CPU 密集但栈占用很小的纯算法任务,这也是它能以 .fap 方式运行的前提;requires=["gui", "dialogs"]:依赖 GUI(屏幕渲染)与 Dialogs(文件浏览器)两个系统服务;fap_category="Tools":决定 .fap 在应用菜单中的归属分类(Tools 工具类)。
7.3 二维码编码库:qrcode.c 的内部结构
qrcode.c是 ricmoo/QRCode 库的移植版(MIT 协议,派生自 Project Nayuki 的 QR-Code-generator C++ 实现),并被原作者小幅修改以修复编译错误。其内部值得注意的实现点:
- 容量表:第 43–67 行存放了
NUM_ERROR_CORRECTION_CODEWORDS、NUM_ERROR_CORRECTION_BLOCKS、NUM_RAW_DATA_MODULES三张覆盖版本 1–40 的表,并支持LOCK_VERSION宏裁剪(锁定版本可跳过大部分表以节省内存,未锁定时宏值为 0); - BitBucket 位流结构:第 164 行起定义
BitBucket,用位偏移实现紧凑的码字写入; - 对外 API(
qrcode.h第 86–91 行):
uint16_t qrcode_getBufferSize(uint8_t version); int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);本应用统一走qrcode_initBytes(rebuild_qrcode,qrcode_app.c第 280 行),把消息按字节数组编码。QRCode 结构体(qrcode.h第 70–77 行)记录版本、尺寸、ECC、模式、掩码及模块位图。
7.4 渲染与交互的线程模型
应用采用 Flipper 标准的"渲染回调 + 输入回调 + 消息队列"模型(qrcode_app.c):
render_callback在 GUI 线程被调用,负责把模块位图画到屏幕:pixel_size = height / size计算每个模块的像素边长,模块为 1 像素时用canvas_draw_dot、更大时用canvas_draw_box(第 127–131 行),并在显示统计时把二维码左移让出右侧 65 像素栏位(第 123 行);input_callback只把短按事件投递进FuriMessageQueue(容量 8),主循环在furi_message_queue_get中阻塞消费,避免在回调里做重活;- 渲染与主循环之间通过
FuriMutex互斥保护共享的qrcode/message状态。
这一架构保证了在"每帧全量重绘位图"(最多 61×61 模块)的情况下交互仍然顺滑。
7.5 配套脚本
压缩包内scripts/提供两个 CI 辅助脚本:
check-firmware.sh:接收固件仓库路径,用 git tag 对比当前固件版本与上次构建版本,输出是否有新版本;update-firmware.sh:按新固件版本更新 release 工作流配置并自动打 tag、推送。
它们服务于"固件升级后自动重建 .fap"的自动化流水线,普通使用者可忽略。
八、实用建议与已知限制
综合 README 与源码,给出如下经验总结:
- 控制消息长度:屏幕物理上限决定了版本上限 11,Binary 模式下消息超过 321 字节即无法显示;纯数字消息则可以放宽到 772 字符;
- 为模式优化消息:纯数字就删掉所有标点;域名用大写以命中 Alpha-Numeric 模式;路径类 URL 只能用 Binary 模式且受 Latin-1 约束;
- 扫码器的"宽容度"差异很大:作者实测 iPhone 可读取最高版本 11 的二维码(含电话号码、Wi-Fi 信息、URL),但小屏二维码整体上对老旧/低端扫码器不友好,脏屏会加剧问题;
- 善用统计面板:遇到读不出的场景,可手动 Version +1 并将 ECC 提到
H,提高容错冗余; - Wi-Fi 消息务必转义:SSID/密码含
\";,:时必须加反斜杠,否则手机可能解析失败。
九、参考资料
- 应用说明文档:flipperzero-qrcode-1.1.0/README.md
- 应用源码归档:flipperzero-qrcode-1.1.0.zip(内含
qrcode_app.c、qrcode.c、qrcode.h、application.fam) - Flipper Zero 项目仓库说明:ReadMe.md
- 应用安装相关:Flipper 官方 qFlipper 桌面工具(可在固件官方文档中查阅使用方式)
文中源码引用均基于仓库内归档的 v1.1.0 源码包;若在更新的固件版本上使用,请以该固件对应的构建输出为准。
【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考