本文记录的是在开源项目基础上、针对本人自购设备所做的一次功能扩展。所有改动均基于公开源码,遵守原项目许可证;文中不包含任何账号、令牌、Wi-Fi 密码、设备密钥或他人隐私数据。
一、为什么选这个项目
FoloToy AI Passport 是一块很小的随身设备:ESP32-C3、8 MB Flash、240×320 屏幕,只有上/下/确定三个按键。社区固件 Passport OS(github.com/manchunx7-bit/passport-os)以 MIT 许可证开源,已经内置首页、收音机、AI 语音、小智、Muse、俄罗斯方块、秒表、番茄钟等应用,并提供统一的启动器、设置、配网和电源管理。
它的代码结构很适合做二次开发:
- 应用层在
main/apps/,每个应用是一个独立目录; - 应用统一通过
passport_app_t描述符注册到main/launcher/app_registry.cc; - 通用界面组件、字体、图标在
main/ui/、main/fonts/、main/launcher/launcher.cc; - 板级驱动在
components/bsp/,不需要为了加功能去改硬件驱动。
我想加的功能很简单:一个离线可用的计算器。它不依赖网络、不申请额外权限,也不会触碰设备上已有的账号或配网数据,是练习二次开发边界的一个合适小目标。
二、动手前的合规边界
二次开发最容易忽略的不是代码,而是许可与边界。这次我给自己定了四条规则:
- 只使用公开源码:基于 Passport OS 的 MIT 许可代码开发,保留原项目版权与许可声明;不复制、不反编译、不再分发任何未授权的第三方固件或专有资源。
- 不修改、不再分发第三方素材:项目中的 Muse、Jollybot 形象、字体等分别遵循各自目录下的 LICENSE/NOTICE,本次没有改动这些资源,也没有把它们单独抽出来传播。
- 不处理敏感数据:代码和本文都不包含 Wi-Fi 密码、API Token、设备密钥、序列号或他人数据;固件构建时使用项目的公开发布配置,清空默认的服务配置。
- 只在本人设备上验证:刷机仅针对自己持有的设备;不提供”破解””绕过””提权”类内容,也不把固件发布到任何应用商店或收费渠道。
在这个前提下,二次开发就是正常的开源软件修改:读代码、加模块、跑测试、编固件、刷自己的设备。
三、功能设计:三个按键怎么用计算器
设备只有 UP / DOWN / OK 三个键,所以不能照搬手机计算器的九宫格点按。最终采用的交互是:
- 屏幕下方是一块 4×5 的虚拟键盘:
7 8 9 /、4 5 6 *、1 2 3 -、0 . = +、C < +/- %; - UP/DOWN 在按键之间线性移动高亮(走到底自动回到另一端),OK 输入当前高亮键;
- 长按 UP 清空、长按 DOWN 退格、长按 OK 退出(沿用系统统一的返回手势);
- 屏幕上方显示表达式与结果,出错(例如除以 0)显示
Error。
计算逻辑被刻意做成纯 C 状态机,不依赖 LVGL/ESP-IDF:先按运算符优先级做乘除、再做加减,支持小数、取负、百分比、连续运算和表达式长度上限。这样可以用主机测试覆盖边界情况,而不必每次都刷机试。
四、代码改动
新增两个文件:
main/apps/calculator/calculator_core.c/.h:表达式状态机与求值。按键事件只修改状态,UI 只负责把expr/result显示出来;main/apps/calculator/app_calculator.cc:LVGL 页面、键盘布局、高亮、按键分发,以及passport_app_t描述符。
接入现有框架只需要几处小改动:
main/apps/app_base.h:应用 ID 枚举增加APP_ID_CALCULATOR;main/launcher/app_registry.cc:声明g_calculator_app、加入启动器轮播列表、把计算器纳入允许按键音效的应用集合;main/launcher/launcher.cc:为计算器补一个像素图标(机身 + 显示屏 + 3×2 按键点阵)和主题色;main/CMakeLists.txt:加入新源文件与头文件目录;tests/test_calculator.c+tools/test-host.sh:新增主机测试并接入测试脚本;- 中英文 README 的应用列表与数量同步更新。
这体现了这类项目的一个好处:新增应用不需要改动系统层。注册表负责”发现应用”,启动器负责”展示应用”,应用自己负责”生命周期和按键”,三者解耦得很好。
五、构建与验证
构建使用项目要求的 ESP-IDF 5.5.3,命令行大致是:
idf.py -B build-calc -D SDKCONFIG=build-calc/sdkconfig \
-D PASSPORT_PUBLIC_RELEASE=ON -D PASSPORT_MUSE_USE_JOLLYBOT=ON build
idf.py -B build-calc merge-bin -o build-calc/FoloToy-AI-Passport-full.bin
python tools/verify_firmware.py build-calc --public
验证分三层:
- 主机测试:计算器核心的状态机测试全部通过,覆盖优先级、小数、除零、退格、清空、取负、百分比、连续运算和长度上限;
- 固件校验:
verify_firmware.py --public检查分区布局、SRAM 预算、合并镜像和公开发布配置,全部 PASS; - 真机测试:刷入设备后,启动器能看到”计算器”卡片与图标,进入后按键导航、
1 + 2 = 3、错误显示和长按退出均正常。
六、升级时如何不丢数据
刷机有两种方式,区别很大:
- 完整镜像写
0x0:会覆盖引导、分区表和 app,并且把 0x8000–0x10000 之间的空隙填成0xFF,NVS(配网/资料)和 PHY 校准会被清掉;只适合首次安装或分区表变更。 - 应用镜像写
0x10000:只覆盖 app 分区,NVS、PHY、cardid、storage(头像/名片)都保留;适合日常升级。
python -m esptool --chip esp32c3 -p COM5 -b 460800 write_flash 0x10000 build-calc\passport-os.bin
如果必须整片刷又要保留数据,可以先备份再恢复:
python -m esptool -p COM5 read_flash 0x9000 0x6000 nvs.bin
python -m esptool -p COM5 write_flash 0x9000 nvs.bin
注意 NVS 结构要和新固件兼容,跨大版本时不要盲目恢复。
七、这次二次开发的可复用经验
- 先读框架,再写功能:这类多应用固件的关键不是画界面,而是理解注册表、生命周期和按键分发;接入点找对了,代码量会很小。
- 逻辑与界面分离:把计算逻辑做成纯 C 状态机,既方便测试,也方便以后换成别的交互方式。
- 先查字体再写中文:嵌入式 UI 的中文字体通常是子集,新增中文文案前要确认字库覆盖,否则会出现方框或空白。本次用到的字符都在现有字库内,因此不需要重新生成字体。
- 构建环境要可控:Windows 下用 MSYS/Git Bash 调 ESP-IDF 会遇到 shell 识别问题;把
idf.py的调用放到原生 cmd 环境、并显式处理/tmp路径映射,可以避免”配置成功但找不到产物”的坑。 - 测试分层:主机测试抓逻辑,固件校验抓布局与资源,真机测试抓交互与显示;三层都过再谈”完成”。
八、合规声明与致谢
- 本项目二次开发基于 MIT 许可证的 Passport OS 源码,保留原项目版权与许可声明;本文与改动不改变原项目的许可方式。
- 未修改、未再分发 Muse、Jollybot 及其他第三方素材;相关资源仍遵循各自目录下的 LICENSE/NOTICE。
- 未包含或传播任何账号、令牌、Wi-Fi 密码、设备密钥、序列号、日志或他人数据。
- 未提供任何绕过技术保护措施、破解、提权或规避授权的内容;刷机仅针对本人持有的设备。
- Passport OS 是社区固件,本文不代表 FoloToy 官方立场,也不宣称与原项目维护者有隶属或背书关系。
感谢 Passport OS 的作者与社区把这样一套完整的固件开源出来,让”给随身小设备加一个小功能”变成一件可以在一个下午完成、并且可以坦然公开记录的事。
九、后续可以做什么
计算器只是一个开始。按同样的注册方式,还可以继续加:单位换算、计时器、离线词典、二维码显示等。真正需要谨慎的始终是两件事:尊重许可证与第三方资源,以及不碰别人的数据与设备。在这两条边界内,二次开发是一件很纯粹的事——读代码、写功能、刷自己的设备、把过程分享出来。