news 2026/9/14 2:07:12

Flipper Zero JavaScript SDK 开发指南:基于 @flipperdevices/fz-sdk 构建与运行 Flipper Zero JS 应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flipper Zero JavaScript SDK 开发指南:基于 @flipperdevices/fz-sdk 构建与运行 Flipper Zero JS 应用

Flipper Zero JavaScript SDK 开发指南:基于 @flipperdevices/fz-sdk 构建与运行 Flipper Zero JS 应用

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

导读

本文以 Flipper Zero 固件仓库中的官方 JS SDK 开发包(@flipperdevices/fz-sdk)为线索,系统讲解如何用交互式脚手架创建 Flipper Zero JavaScript 应用、理解其构建上传流水线与配置文件,并掌握 SDK 版本兼容检查机制和内置 API。读完本文,你将能够独立完成一个 Flipper Zero JS 应用的初始化、编译、烧录运行与兼容性验证,并理解其底层实现原理。


一、@flipperdevices/fz-sdk 是什么

@flipperdevices/fz-sdk是 Flipper Zero 官方为 JavaScript 应用开发提供的工具与类型声明包(Type declarations and documentation for native JS modules available on Flipper Zero)。它位于仓库的 applications/system/js_app/packages/fz-sdk 目录下,主要包含三部分能力:

  1. TypeScript 类型声明(typings):以*.d.ts文件形式描述 Flipper Zero 固件内置(native)JS 模块的完整 API 表面,供开发者在 PC 上获得代码补全与类型检查;
  2. CLI 工具(sdk.js):提供buildupload两个命令,负责把 TypeScript 源码编译为 Flipper Zero 上 mJS 引擎可执行的脚本,并通过串口上传到设备;
  3. 文档生成配置:基于 TypeDoc 将类型声明中的 JSDoc 注释渲染为 API 文档。

从 package.json 可以看到,该包依赖esbuild(打包压缩)、esbuild-plugin-tsc(TypeScript 编译)、typedoc(文档生成)、serialport(串口上传)等工具链组件,版本为1.0.0,License 为 GPL-3.0-only。

它对应的"解释器实现"位于仓库固件侧:applications/system/js_app目录下的js_app.cjs_thread.c以及modules/中各原生模块(js_gui.cjs_gpio.cjs_storage.c等),这些 C 模块就是 JS API 在固件端的实际落地实现。

二、快速开始:交互式脚手架与一键运行

2.1 创建应用

官方推荐的起步方式是使用交互式向导创建应用骨架:

npx @flipperdevices/create-fz-app@latest

运行后向导会在当前目录生成一个 Flipper Zero JS 应用工程(默认目录名如my-flip-app)。脚手架的模板文件位于仓库 applications/system/js_app/packages/create-fz-app/template 下,包含:

  • index.ts:应用入口源码,演示了事件循环 + GUI 视图的基本用法;
  • package.json:定义build/start脚本与依赖;
  • tsconfig.json:TypeScript 编译配置;
  • fz-sdk.config.json5:SDK 构建与上传配置。

2.2 构建并运行

cd my-flip-app npm start

npm start会依次执行模板 package.json 中定义的两条脚本:

"build": "tsc && node node_modules/@flipperdevices/fz-sdk/sdk.js build", "start": "npm run build && node node_modules/@flipperdevices/fz-sdk/sdk.js upload"

即:先用tsc把 TypeScript 编译到dist/,再调用sdk.jsbuild命令完成 mJS 兼容转译,最后通过sdk.js upload将产物经串口上传到 Flipper Zero 并立即运行。官方文档同时说明,你完全可以使用pnpmyarn替代npm,流程不变。

2.3 向导生成的入口模板解读

模板 index.ts 本身就是一个最小可运行的 Flipper Zero JS 应用,值得逐段拆解:

// 注意导入顺序:eventLoop 必须先于 gui 导入,gui 必须先于任何 gui 子模块导入 import * as eventLoop from "@flipperdevices/fz-sdk/event_loop"; import * as gui from "@flipperdevices/fz-sdk/gui"; import * as dialog from "@flipperdevices/fz-sdk/gui/dialog"; const views = { dialog: dialog.makeWith({ header: "Hello from <app_name>", text: "Check out index.ts and\nchange something :)", center: "Gonna do that!", }), }; // 按下中间键退出应用 eventLoop.subscribe(views.dialog.input, (_sub, button, eventLoop) => { if (button === "center") eventLoop.stop(); }, eventLoop); // 按下返回键退出应用 eventLoop.subscribe(gui.viewDispatcher.navigation, (_sub, _item, eventLoop) => { eventLoop.stop(); }, eventLoop); // 切换到对话框视图并启动事件循环 gui.viewDispatcher.switchTo(views.dialog); eventLoop.run();

这段代码集中体现了 Flipper Zero JS 编程的三个核心概念:事件循环(event_loop)视图工厂(makeWith)视图调度器(viewDispatcher),其 API 语义在类型声明 event_loop/index.d.ts 与 gui/index.d.ts 中有完整定义。

三、sdk.js 构建与上传流水线原理

sdk.js是 SDK 包的命令行入口(shebang#!/usr/bin/env node),支持buildupload两个子命令,从源码 sdk.js 可以还原其完整工作流程。

3.1 build:转译为 mJS 兼容子集

Flipper Zero 上的 mJS 引擎并不支持完整的现代 JavaScript 语法,因此build命令借助 esbuild 的supported选项显式禁用了一批语法特性,将其转译为 mJS 可执行的兼容代码。被禁用的特性包括(节选):

supported: { "array-spread": false, "arrow": false, "async-await": false, "class": false, "destructuring": false, "optional-chain": false, "template-literal": false, "for-of": false, // ... 完整清单见 sdk.js }

同时@flipperdevices/fz-sdk/*被声明为external,意味着源码中的import * as gui from "@flipperdevices/fz-sdk/gui"会保留为运行时的require("gui")调用,与固件内建模块一一对应。

构建产物默认写入dist/index.js(由config.output决定),并会在文件头部拼接let exports = {};前缀以适配 mJS 的执行模型。

3.2 upload:串口自动发现与命令行协议

upload命令实现了从 PC 到设备的完整烧录运行链路:

  1. 设备发现:通过SerialPort.list()枚举串口,优先匹配serialNumberflip_开头的设备;若为空(部分 Windows 驱动不报告序列号),则回退为按 STM32 VCP 的 VID:PID(0483:5740)过滤。多台设备时会弹出交互选择;
  2. 串口连接:以230400波特率打开串口;
  3. CLI 协议交互:依次向 Flipper Zero 的 CLI 发送storage remove <output>(删除旧文件)、storage write_chunk <output> <size>(分块写入脚本)、js <output>(启动脚本执行),并以">: ""Ready""Running""Script done!"等输出标记同步状态;
  4. 退出清理:进程退出时发送\x03中断脚本。

也就是说,npm start的"上传即运行"本质上是驱动了设备端 CLI 的存储与 JS 命令,相关 CLI 实现在固件侧可对应到applications/services/cliapplications/system/js_app/js_app.c

四、fz-sdk.config.json5 配置详解

构建与上传的行为由工程根目录下的fz-sdk.config.json5控制。模板默认配置如下(完整模板):

{ build: { // 编译产物路径 output: "dist/<app_name>.js", // 是否压缩代码(以可读性和错误信息清晰度为代价减小体积) minify: false, // 设为 false 可关闭自动插入的 SDK 版本检查(见下文第五、六节) enforceSdkVersion: true, }, upload: { // 上传源文件;若无额外后处理,应与 build.output 一致 input: "dist/<app_name>.js", // 设备端存放路径,默认是脚本应用目录 output: "/ext/apps/Scripts/<app_name>.js", }, }

各配置项说明:

配置项作用注意事项
build.output编译产物输出路径通常放在dist/
build.minify是否启用 esbuild 压缩开启后体积更小,但报错信息更难读
build.enforceSdkVersion是否自动插入checkSdkCompatibility调用默认true;关闭需自行做版本检查
upload.input上传的源文件无后处理时应等于build.output
upload.output设备端保存路径默认/ext/apps/Scripts/目录,与设备上"脚本"应用的浏览入口一致

五、版本兼容机制:SDK 版本与额外特性集

这是 SDK 设计中最关键的部分,原文档(README 的 Versioning 一节)给出了明确的版本语义:

  • 版本对齐@flipperdevices/fz-sdk每个发布版本的主版本号(major)和次版本号(minor)与它面向的 Flipper Zero JS SDK 版本一致,并遵循 semver 语义。例如,用 SDK 版本0.1.0编译的应用,与0.11.0(不含1.0)之间的 JS SDK 版本兼容。
  • 破坏性变更规则:major 版本在引入破坏性变更时递增(即需要开发者修改应用的变更),minor 版本在引入新的非破坏性特性时递增。由于官方已采用 TypeScript 类型声明,是否属于破坏性变更依据 semver-ts 标准判定,其核心是"no new red squiggles"(编译期不产生新的类型错误)。
  • 每个 API 的版本史:类型声明中每个 API 的 JSDoc 注释都记录了其引入/修改的版本,例如 global.d.ts 中大量出现@version Added in JS SDK 0.1@version Added in JS SDK 0.2, extra feature "gui-widget"@version Baseline since JS SDK 1.0等标记。

此外,SDK 定义了额外特性集(extra feature set)概念:每个 major 版本对应一组在部分固件发行版中存在、但并未进入上游的"额外特性"。各发行版之间可以互相移植这些特性;当某个特性被移植进上游固件后,在下一次 JS SDK major 版本发布时,它会被声明为基线特性(baseline feature),不再被视为"额外特性"。当前仓库中的 JS SDK 即为 v1.0(global.d.ts的 JSDoc 中明确标注 "You're looking at JS SDK v1.0")。

结论是:在使用任何特性之前,必须先检查运行脚本的解释器是否支持它,否则应用的跨固件可移植性将大打折扣。

六、兼容性检查 API 详解

针对不同的使用场景,global.d.ts 提供了五个兼容性检查函数(均为全局可用,无需导入):

函数签名用途
sdkCompatibilityStatus(expectedMajor, expectedMinor) => "compatible" \| "firmwareTooOld" \| "firmwareTooNew"需要详细的兼容性状态时使用
isSdkCompatible(expectedMajor, expectedMinor) => boolean需要布尔形式判断时使用
checkSdkCompatibility(expectedMajor, expectedMinor) => void \| never脚本绝对无法在不兼容的解释器上运行时使用;不兼容时会询问用户是否继续
doesSdkSupport(features: string[]) => boolean查询指定额外特性是否被支持(布尔形式)
checkSdkFeatures(features: string[]) => void \| never同上,但不支持时询问用户是否继续运行

其中sdkCompatibilityStatus的三态语义为:

  • "compatible":脚本与固件 JS SDK 兼容;
  • "firmwareTooOld":期望的 major 大于固件版本,或期望的 minor 大于固件版本;
  • "firmwareTooNew":期望的 major 低于固件版本。

doesSdkSupport/checkSdkFeatures有一个易踩的坑(类型声明中以@warning明确标注):如果被查询的特性如今已被认定为基线特性,函数会返回false(或视为未实现)。因此查询时应只针对"额外特性"列表,基线特性无需也不能通过它们检查。

自动版本强制

enforceSdkVersion: true(默认)时,build过程会在产物开头自动插入一行checkSdkCompatibility(<major>, <minor>);(见 sdk.js),其中 major/minor 取自@flipperdevices/fz-sdk包自身的package.json版本。如果你已通读文档、确定能用上文的手动检查 API 获得更好控制,可在fz-sdk.config.json5中将该选项设为false

补充说明:原 README 的 Versioning 一节提到可组合使用sdkCompatibilityStatusisSdkCompatibleassertSdkCompatibility三个函数;而在当前仓库的类型声明中,第三个函数的实际名称为checkSdkCompatibility(语义一致:检查失败时询问用户是否继续),并以doesSdkSupport/checkSdkFeatures补充了特性级检查。使用时应以 global.d.ts 的实际声明为准。

七、全局内置 API(global.d.ts 一览)

SDK 除模块化 API 外,还提供了一批全局可用的内置函数与类型,均在 global.d.ts 中声明:

  • 基础函数delay(ms)(暂停执行)、print(...args)(输出到 GUI 控制台视图)、parseInt(text, base?)(转数字,base 支持 2…16,默认 10)、chr(n)(ASCII 码转单字符,越界返回null)、require(module)(加载原生模块)、load(path, scope?)(加载并执行另一个 JS 文件,结果按会话缓存)、die(message)(携带错误信息退出);
  • 环境变量__dirname(当前脚本所在目录)、__filename(当前脚本文件路径);
  • 控制台console.log / debug / warn / error,分别输出到 UART 日志的[I]/[D]/[W]/[E]级别;
  • 二进制类型ArrayBuffer(含getPtrbyteLengthslice)、RawPointer(不透明指针类型,JS 侧只能持有并原样回传)、Uint8Array/Int8Array/Uint16Array/Int16Array/Uint32Array/Int32ArrayElementType联合类型;
  • 内建对象子集Arraysplice/push/length)、StringcharCodeAt/at/indexOf/slice/toUpperCase/toLowerCase)、Number.toString(base)等。

标准库的大部分特性尚未实现,该模块只声明了确实实现的部分(JSDoc 中明确说明 "Standard library features are mostly unimplemented")。编写代码时请以本文件声明为唯一依据,不要依赖未声明的 ECMAScript 标准库行为。

八、原生模块与 TypeScript 类型声明

@flipperdevices/fz-sdk*.d.ts覆盖了固件侧全部原生 JS 模块,与applications/system/js_app/modules/下的 C 实现一一对应:

SDK 模块(类型声明目录)固件侧 C 实现
event_loopjs_event_loop.c
gui及 15 个子模块(dialogsubmenuwidgettext_inputnumber_inputbyte_inputfile_pickerloadingmenupopupempty_screenbutton_menubutton_panelvi_listtext_boxiconjs_gui.c 及各视图 C 文件
gpiojs_gpio.c
storagejs_storage.c
serialjs_serial.c
mathjs_math.c
notificationjs_notification.c
badusbjs_badusb.c
flipperjs_flipper.c
testsjs_tests.c

在仓库自带的示例脚本 applications/system/js_app/examples/apps/Scripts 中(gui.jsstorage.jsgpio.jsevent_loop.jsuart_echo.jsnotify.jsmath.jsbadusb_demo.js等),可以看到这些模块在真实脚本中的用法。

8.1 GUI 体系:View、ViewFactory 与 ViewDispatcher

gui/index.d.ts 系统性地描述了 Flipper Zero 的 GUI 抽象层次:

  • Canvas:纯绘图区域,没有抽象层;
  • Viewport:指向画布矩形区域的窗口,应用总是通过 viewport 访问画布;
  • View:占据整个 viewport 并接管所有输入事件的"全屏设计元素",即 Flipper 术语中的视图。JS 适配器已覆盖button_menubutton_panelbyte_inputdialog(对应dialog_ex)、empty_screenfile_picker(对应file_browser)、loadingmenunumber_inputpopupsubmenutext_boxtext_inputvi_list(对应variable_item_list)、widget共 15 种视图;
  • ViewDispatcher:持有应用所需的所有视图并在请求间切换,提供switchTosendCustomsendTo以及navigation/custom事件源;
  • SceneManager:视图调度器的可选附加组件,用于复杂导航流程管理,当前版本在 JS 中不可用。

每种视图通过ViewFactory提供make()(默认属性创建)与makeWith(props)(自定义初始属性创建)两个工厂方法,且属性可在创建后用view.set(name, value)修改。GUI 依赖event_loop模块,因此必须先导入event_loop,再导入gui,再导入gui子模块——这一约束在模板index.ts的注释和类型声明中均有明确说明。

8.2 事件循环模型

event_loop/index.d.ts 说明了一个重要事实:Flipper Zero 的 mJS 子系统不支持闭包,因此subscribe(callback, ...extraArgs)允许把外部值作为额外参数传给回调;若回调返回一个与额外参数个数相同的数组,则下次触发时使用新值。示例(定时器):

let timer = eventLoop.timer("periodic", 1000); eventLoop.subscribe(timer, function(_sub, _item, counter, eventLoop) { print("Counter is at:", counter); if(counter === 10) eventLoop.stop(); return [counter + 1, eventLoop]; // 修改下次回调收到的额外参数 }, 0, eventLoop);

回调的前两个参数固定为订阅管理器(可cancel())和事件项(无数据的定时器事件为undefined)。

8.3 存储 API 要点

storage/index.d.ts 提供了File/FileInfo/FsInfo等类,以及两组重要枚举:

  • 访问模式AccessMode"r"(只读)、"w"(只写)、"rw"(读写);
  • 创建模式OpenMode"open_existing"(不存在即失败)、"open_always"(不存在则创建空文件)、"open_append"(打开并把读写指针置到文件末尾,不存在则创建)、"create_new"(已存在即失败)、"create_always"(截断并打开,不存在则创建空文件)。

FileInfo返回pathstat返回完整路径,readDirectory返回文件名)、isDirectorysizeaccessTime(UNIX 时间戳)。

九、开发工作流与最佳实践

综合以上内容,一个完整的 Flipper Zero JS 应用开发周期是:

  1. 初始化npx @flipperdevices/create-fz-app@latest生成工程骨架;
  2. 编码:在index.ts(或按需拆分的多个 TS 文件)中编写逻辑,依赖tsc的类型检查(模板 tsconfig.json 开启checkJsnoLib并显式包含global.d.ts,以保证全局 API 可用);
  3. 构建npm run build调用tscsdk.js build,产出 mJS 兼容的压缩脚本;
  4. 运行npm start自动执行构建 + 串口上传 + 启动,在设备上观察输出(RunningScript done!标记);
  5. 兼容性:保持默认的enforceSdkVersion: true,或按第六节手动调用sdkCompatibilityStatus/isSdkCompatible/doesSdkSupport做精细控制;关注每个 API JSDoc 中的@version历史以决定脚本可运行的固件范围。

最佳实践要点:在脚本无法运行于不兼容解释器时使用checkSdkCompatibility/checkSdkFeatures(自动询问用户);在脚本能利用多版本能力时使用isSdkCompatible/doesSdkSupport;需要详细状态时使用sdkCompatibilityStatus。谨记"先检查、再使用"的原则,这能确保你的应用在各类 Flipper Zero 固件发行版之间保持可移植性。


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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 2:05:57

后端砍41%、前端跌20%!脉脉CEO说只招AI人才?

有个身为HR的朋友向我吐槽, 说近期收到的简历之中, 十个里面有八个都写着“熟练运用AI编程工具”, 然而真正交谈起来, 在能讲明白Agent架构的方面, 一个人都不存在。当时我并没有太把它当作一回事, 一直到昨日, 我看见了脉脉首席执行官林凡的专访, 这才了解到这件事情比我所想象…

作者头像 李华
网站建设 2026/9/14 2:05:44

车载测试入门:仿真环境搭建与真实项目技能转化全指南

从“车载测试”这个岗位火起来之后&#xff0c;我隔三差五就会在后台看到类似的问题&#xff1a;这行到底要不要学仿真环境&#xff1f;培训机构宣传的“真实项目贯穿全程”是不是噱头&#xff1f;仿真练出来的技能&#xff0c;面试时真能用吗&#xff1f; 我最早注意到“博为…

作者头像 李华
网站建设 2026/9/14 2:04:59

LSB隐写从原理到实战:LSB替换、位平面分析与Python实现

简介&#xff1a;面向信息安全初学者与图像隐写研究者的MATLAB实现资源&#xff0c;聚焦LSB替换隐写&#xff1a;通过修改图像像素最低有效位完成秘密信息嵌入&#xff0c;并可从载体图中逆向提取。压缩包为zip格式&#xff0c;共5个文件&#xff0c;含LSBmain.m主程序、LSB_en…

作者头像 李华
网站建设 2026/9/14 2:04:56

昆虫目标计数系统:HSV增强+注意力CNN+DBSCAN聚类

简介&#xff1a;这是一套面向计算机相关专业本科生的毕业设计级昆虫识别与计数系统&#xff0c;聚焦图像分类与目标计数在农业病虫害监测等实际场景中的落地应用&#xff0c;适合具备Python基础与机器学习入门知识的学习者开展课程设计或科研实践。资源共197个文件&#xff0c…

作者头像 李华