从按下 Voice 到生成 UF2,这个项目怎样工作?
我们从首次可构建的五键原型出发,沿着“按钮输入”和“固件构建”两条链路,理解代码为什么存在,以及每个目录和文件负责什么。
历史基线:首次可构建的五键 ZMK 原型这个仓库不是一个传统应用
仓库里没有自己编写的主循环,也没有一套 C/C++ 应用代码。它更像一组交给 的声明:选择哪块开发板、按钮接在哪些 GPIO、五个位置分别做什么,以及怎样构建和刷写。
设备上电以后
ZMK 扫描 D0–D4,识别按钮状态,再把对应的键盘事件通过 USB 或蓝牙发给电脑。
代码推送以后
GitHub Actions 读取构建矩阵和 shield 配置,调用 ZMK 官方工具链,最后产出可刷写的 UF2。
F13。麦克风仍在电脑或耳机上,电脑端语音软件负责监听 F13 并开始或停止录音。
按下一颗按钮以后发生了什么?
运行链路依次经过物理电路、GPIO 扫描、位置顺序、键位绑定和 HID 传输。每层只回答一个问题。
- 电路:按钮把一个 XIAO GPIO 接到 GND。
- 扫描:
ai_remote.overlay告诉 ZMK 读取 D0–D4,并启用内部上拉与低电平有效。 - 位置:同一个 overlay 把五个 GPIO 排成从 0 到 4 的一行五列。
- 行为:
ai_remote.keymap按相同顺序绑定 F13、↑、Enter、↓ 和 BT_NXT。 - 输出:ZMK 根据配置通过 USB HID 或 BLE HID 把事件发给电脑。
硬件输入:ai_remote.overlay
input-gpios
= <&xiao_d 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&xiao_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&xiao_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&xiao_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&xiao_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
GPIO_PULL_UP 让松开状态保持为高电平;GPIO_ACTIVE_LOW 表示按下接地后读到低电平时,才算按钮激活。这样每颗按钮只需要 GPIO 和 GND 两根连接。
位置到行为:ai_remote.keymap
bindings = <
&kp F13
&kp UP_ARROW
&kp RETURN
&kp DOWN_ARROW
&bt BT_NXT
>;
&kp 发送普通键盘按键;&bt BT_NXT 不发送字符,而是让 ZMK 切换到下一个 Bluetooth profile。五个 profile 的管理、配对信息保存和 BLE HID 传输由 ZMK 提供。
启用传输与持久设置:ai_remote.conf
CONFIG_ZMK_USB=y
CONFIG_ZMK_BLE=y
CONFIG_SETTINGS=y
USB 和 BLE 在原型期同时可用,便于先验证 USB 按键再排查蓝牙。CONFIG_SETTINGS 让配对等 ZMK 设置能够持久保存。
GitHub 怎样把这些声明变成 UF2?
构建链路的重点不是“执行一个神秘编译命令”,而是让 ZMK 能找到这个仓库作为额外模块,并知道要组合哪块 board 与哪套 shield。
.github/workflows/build.yml在 push、pull request 或手动触发时调用 ZMK 官方可复用工作流。config/west.yml固定 ZMKv0.3,并导入 ZMK 自己的依赖清单。zephyr/module.yml告诉 Zephyr:这个仓库根目录可以提供 board/shield 定义。build.yaml声明两个构建目标:正常遥控器和 settings-reset 恢复固件。- ZMK 根据
xiao_ble + ai_remote找到boards/shields/ai_remote/下的配置。 - 工具链合并 board、shield、Kconfig、Devicetree 和 keymap,生成 UF2 artifact。
.github/workflows/build.yml
只负责“什么时候构建”和“调用哪套官方流程”。它不重复实现 ZMK 的安装与编译步骤。
jobs:
build:
uses: zmkfirmware/zmk/.github/workflows/
build-user-config.yml@v0.3
build.yaml
它是构建矩阵。第一个目标生成日常固件,第二个目标生成清空 ZMK settings 的恢复固件。
- board: xiao_ble
shield: ai_remote
artifact-name: ai-remote
- board: xiao_ble
shield: settings_reset
artifact-name: settings-reset
首次可构建原型的完整目录地图
下面展示首次可构建原型使用的历史路径。现有仓库中的 ai_voice_remote 对应这里的 ai_remote,各层职责相同。
.
├── .github/workflows/build.yml # 云端构建入口
├── boards/shields/ai_remote/ # 五键硬件与行为定义
│ ├── Kconfig.defconfig
│ ├── Kconfig.shield
│ ├── ai_remote.conf
│ ├── ai_remote.keymap
│ ├── ai_remote.overlay
│ └── ai_remote.zmk.yml
├── config/west.yml # ZMK 依赖清单
├── zephyr/module.yml # 把仓库注册为 Zephyr 模块
├── build.yaml # 云端构建矩阵
├── scripts/ # 检查、构建、刷写
│ ├── check-config.py
│ ├── build-firmware.sh
│ └── flash-firmware.sh
├── docs/ # 操作与验收文档
│ ├── firmware.md
│ ├── product-spec.md
│ └── wiring.md
├── hardware/README.md # 接线证据与未来 PCB 边界
├── enclosure/README.md # 未来外壳工作的进入条件
├── README.md # 项目入口
├── .gitignore # 排除本地和生成文件
└── LICENSE # MIT 许可
每个目录和文件分别负责什么?
这里按职责分组。文件之间真正重要的契约是:build.yaml 选择的 shield 名、目录名、Kconfig 条件和文件前缀必须互相匹配。
README.md项目入口:产品边界、五键映射、当前阶段、构建方法和主要目录。
.github/workflows/build.yml定义云端构建触发条件,并调用 ZMK 官方构建工作流。
build.yaml列出 board、shield 与 artifact 名;决定云端要生成哪几份固件。
config/west.ymlWest manifest:固定 ZMK v0.3、远程地址和需要导入的依赖。
zephyr/module.yml把仓库根目录声明为 Zephyr 的 board_root,让构建系统能发现 shield。
.gitignore排除 .build/、west workspace、缓存、日志以及生成的 UF2/HEX/BIN。
LICENSEMIT 许可证,说明代码的使用、修改、分发条件和免责声明。
boards/shields/ai_remote/ 是项目核心。Shield 在这里不是外壳,而是一组叠加到 xiao_ble board 上的硬件和行为描述。
Kconfig.shield声明 SHIELD_AI_REMOTE,并在选择 ai_remote 时启用它。
Kconfig.defconfig只有该 shield 启用时,才把默认键盘/蓝牙名称设为 AI Remote。
ai_remote.conf启用 USB HID、BLE HID 和持久 settings。
ai_remote.overlay定义 D0–D4、按键扫描、防抖、唤醒、五个位置的顺序与物理布局元数据。
ai_remote.keymap把五个有序位置绑定为 Voice、Up、Enter、Down 和 Device 行为。
ai_remote.zmk.ymlZMK 的机器可读元数据:shield ID、显示名、项目 URL、XIAO 接口要求和 keys 功能。
scripts/check-config.py便宜的前置检查:确认必需文件、build 目标、五键顺序、D0–D4 和一行五列 transform。
scripts/build-firmware.sh本地构建入口:检查 west 与 workspace,先运行配置检查,再以 XIAO board、项目 shield 和额外模块参数调用 west build。
scripts/flash-firmware.sh检查 UF2 和用户明确给出的挂载目录,然后复制并 sync;故意不自动猜测可移动磁盘。
docs/firmware.md说明 Actions artifact、bootloader、UF2 刷写、三台电脑配对、settings-reset 恢复和本地构建。
docs/product-spec.md冻结 V0.1 产品边界、已实现输入、尚未实现的维护手势、功能验收和原型退出条件。
docs/wiring.md解释 GPIO—按钮—GND、电平、D0–D4 引脚表、四脚按钮方向和上电前通断检查。
hardware/README.md规定这里保存接线证据,实机原型通过后才进入 KiCad 载板;不提交未经实测的 PCB。
enclosure/README.md规定外壳工作要等待 BLE 原型通过,并要求保存参数化 CAD 与 STEP/STL,冻结前实测零件尺寸。
哪些东西已经存在,哪些仍然只是计划?
- 五键 GPIO 与 keymap 声明;
- USB HID 与 BLE HID 配置;
- 多 profile 切换行为;
- 正常与恢复固件构建目标;
- 本地检查、构建和安全复制脚本;
- 接线、刷写和验收文档。
- 真实按钮已经正确接线;
- 五个键在主机上没有漏键或双击;
- 三台电脑切换稳定;
- 睡眠后的第一次按键不丢失;
- PCB、电池和外壳尺寸可用;
- 连续使用时的真实续航。
check-config.py 抓明显结构错误;ZMK 编译证明配置能被工具链接受;只有刷入真实 XIAO 并测量按键、BLE 和电源,才能证明设备行为。
读完后,你应该能解释这些问题
内容依据首次可构建原型的 Git 历史快照 efd0458。这个 SHA 只用于固定证据版本,不是本课的学习目标。