


PC 通过 BLE 无线接收 PCM / Opus 音频
更新日期:2026-10-16。目标工程:ATS3089P 开发板 `bt_watch`。
2026-10-16 连接修复:已定位 Windows 旧配对保留、开发板对应 LTK 不可用造成的
`Could not get GATT services: Unreachable`。上位机新增 `repair`、分阶段中文提示和 UTF-8 诊断日志。
本次修复只更新 Python 工具和说明,继续使用下列 v1 固件,无需重新烧录。
1. 功能与使用方式
Windows PC 默认按广播名称 **`zkBLE` 精确过滤**,发现第一块匹配开发板后立即连接。
执行 `record` 或 `download` 时自动完成连接、订阅、配置、开始、接收和保存,不必先手动扫描或填写 MAC。
PC 需要支持 BLE 的蓝牙适配器及正常驱动,开发板需要烧录本次固件并开启 BLE 广播。
无需 USB 数据线、USB CDC、COM 口或串口板。
支持两种用途:
| 用途 | PC 命令 | 开发板行为 | PC 文件 |
| --- | --- | --- | --- |
| 实时麦克风 PCM | `record --format pcm` | ADC0/MIC0,16kHz、16bit、小端、单声道,边录边传 | `audio.pcm`、`audio.wav`、`metadata.json` |
| 实时麦克风 Opus | `record --format opus` | 同样的 MIC PCM,CPU libopus 压缩,16kbps、20ms 帧,边录边传 | 标准 Ogg 封装的 `audio.opus`、`metadata.json` |
| 下载已有录音 | `download --remote-path /NAND:/rec.opus` | 读取 NAND 已有文件,逐字节原样发送 | 原文件名、`metadata.json` |
`record` 默认 Opus、10 秒,时长可设 1~300 秒。板端按采样数自动结束,PC 等待尾包校验。
实时传输不另外写入开发板 NAND;文件下载不重新录音、不重新编码。
这里传送的是应用定义的 BLE GATT 数据,接收软件是本 Python 工具;不是 Windows 蓝牙声卡或标准 LE Audio 音频会话。
本次实时模式固定单 MIC;以前录好的双 MIC PCM 可以原样下载,再按真实双声道参数转 WAV。
2. 固件与上位机安装
烧录:
```text
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261016_ble_audio_v1.fw
```
同目录有 `_debug.fw` 和 `ats3089p_dev_watch_261016_ble_audio_v1_SHA256.txt`。
需要同时更新固件和 Python 工具。旧固件没有新增的 `1002/1003` 特征,无法只靠更新 Python 实现。
在 PowerShell 进入 `` 工程根目录,然后安装依赖:
```powershell
cd D:\Master\6project\ATS3089C_to_ATS3089P\
python -m pip install -r .\application\bt_watch\tools\ble_audio\requirements.txt
$ble = '.\application\bt_watch\tools\ble_audio\ble_audio.py'
```
使用 Python 3.8~3.13;已固定 `bleak==0.22.3`,兼容工程 SDK 自带 Python 3.8。
Bleak 的 Windows 后端使用系统 WinRT BLE API,版本支持范围见
Bleak 0.22.3 官方包说明(https://pypi.org/project/bleak/0.22.3/)。
首次安装需要联网;正常录音与下载不需要联网。
不必先在 Windows"添加蓝牙设备"里配对,也不必安装串口驱动。
若此前有旧配对或 GATT 缓存,烧录后出现缺少特征或 `Unreachable`,关闭其它连接开发板的 App,
执行 `python $ble repair`。工具将移除扫描到的这一块板子的 Windows 配对关联,重新扫描同一地址,
完成 GATT 检查并实际查询状态;只有收到有效响应才显示"修复验证成功"。
此操作不移除其它蓝牙设备;普通 `record/status` 不自动解除配对。
多块同名板子请先 `scan`,再用 `repair --address 实际地址` 指定目标。
也可在 Windows 蓝牙设置中手动移除旧 `zkBLE`;如板子未恢复广播,再重启板子。
3. 直接执行命令
3.1 录制并无线传输 Opus(优先推荐)
```powershell
python $ble record --format opus --seconds 10 --output-dir D:\logs\ble_audio
```
默认自动找 `zkBLE`,接收后保存到独立的时间戳目录,不覆盖上次录音。
`.opus` 文件带 Ogg、OpusHead、OpusTags 和 EOS 尾页,可由支持 Opus 的播放器打开。
如果还希望保存 Opus **解码后**的 PCM/WAV:
```powershell
python -m pip install imageio-ffmpeg
python $ble record --format opus --seconds 10 --decode --output-dir D:\logs\ble_audio
```
此时得到 `audio.opus`、`audio.pcm`、`audio.wav`。PCM/WAV 来自 PC 解码后的 Opus,
不是另外并行采集的压缩前原始 PCM。`--decode` 使用本机 FFmpeg;也可自行安装 FFmpeg 并加入 PATH。
不加 `--decode` 不需要 FFmpeg。
3.2 录制并无线传输原始 PCM
```powershell
python $ble record --format pcm --seconds 10 --output-dir D:\logs\ble_audio
```
得到原始 `audio.pcm` 和带相同样本的 `audio.wav`。
PCM 播放参数:**16000Hz、16bit signed little-endian、单声道**。
10 秒有效音频为 320000 字节 PCM;ADC 启动稳定时间不计入这 10 秒。
PCM 有效数据速率为 32000 字节/秒,即 256kbps;链路还需要承担 BLE 协议开销。
实际能否持续传输取决于电脑蓝牙适配器、驱动、连接间隔、信号和同时运行的蓝牙业务。
固件提供约 4.096 秒 PCM 队列;带宽不足时会明确报错 `-139`,不会跳过数据后伪装成完整录音。
建议先验证 Opus,再验证 PCM。需要可靠取回长时间原始录音时,用原来的板内录音功能录完,再用 `download`。
3.3 下载开发板已有录音
先停止录音,确保源文件已经关闭;若正在 U 盘模式,先退出 U 盘模式。
PC 看到的 U 盘根目录对应设备路径 `/NAND:/`,此处不是 Windows 的 `E:\`。
```powershell
下载已有 Opus,保持压缩文件内容不变
python $ble download --remote-path /NAND:/rec.opus --output-dir D:\logs\ble_audio
下载已有 PCM;知道实际录音格式时,另外生成 WAV
python $ble download --remote-path /NAND:/rec.pcm --sample-rate 16000 --channels 1 --output-dir D:\logs\ble_audio
下载之前的双 MIC 立体声录音
python $ble download --remote-path /NAND:/rec1.pcm --sample-rate 16000 --channels 2 --output-dir D:\logs\ble_audio
任意编号的已有 Opus,下载后再生成试听 PCM/WAV
python $ble download --remote-path /NAND:/rec2.opus --decode --output-dir D:\logs\ble_audio
```
没有提供 `--sample-rate` 和 `--channels` 时,下载 PCM 只保存原文件,不猜测录音格式。
双声道、44.1kHz 等已有 PCM 都可原样传输,WAV 参数必须填写真实值。
Opus 的 `--decode` 试听副本统一转为 16kHz 单声道;原 Opus 文件保留。
当前文件路径限定 NAND 根目录的 `.pcm/.opus`,文件名支持字母、数字、下划线、短横线和点,
总路径最长 95 字节。下载期间不要并发覆盖源文件,也不要启动编码任务改写源文件。
本模块检查常规录音是否仍在写入、源文件大小是否变化;如检测到变化则失败,不报告完整下载。
下载期间持有文件系统存储租约,切换 USB U 盘会返回忙;完成或断链后自动释放。
3.4 按名称扫描、选择板子、查询状态
```powershell
python $ble scan # 只显示名称 zkBLE 的设备
python $ble scan --all # 排查时显示所有 BLE 设备
python $ble status # 按 zkBLE 自动连接并查询
python $ble repair # 清理这一块板子的 Windows 旧配对,再连接并查询验证
python $ble record --name zkBLE --format opus --seconds 10
有多块 zkBLE 时,使用 scan 显示的实际地址,避免连接另一块板子
python $ble record --address AA:BB:CC:DD:EE:FF --format pcm --seconds 10
```
`--scan-timeout 15` 可以扩大查找等待时间。按名称连接找到第一块匹配设备就开始连接,不等待整轮扫描结束。
默认通常只需执行一条 `record` / `download` 命令。
3.5 连接结果提示及诊断日志
不确定是否连接成功时,先运行 `python $ble status`。该命令不会开始录音,正常输出类似:
```text
17:23:07 已发现:zkBLE,地址 F4:4E:FD:00:11:22。
17:23:09 正在连接 F4:4E:FD:00:11:22 并发现 GATT 服务(第 1/2 次,最多 20 秒)...
17:23:11 BLE 连接成功:F4:4E:FD:00:11:22,ATT MTU=247;控制/音频/配置特征检查通过。
17:23:11 控制通知订阅成功。
17:23:11 开发板控制响应:op=0x00,state=0x30。
17:23:11 无线音频状态:0x30(30=空闲,31=PCM,32=Opus,34=文件下载)
17:23:14 本次 BLE 连接已释放。
```
"已发现"只表示收到广播;"BLE 连接成功"表示连接和三个 GATT 特征已就绪;
"开发板控制响应"表示双向命令链路正常。`scan` 只扫描,结束后没有连接属于正常现象。
`status` 完成查询后主动释放连接,也属于正常行为。
`record` 在此后会显示音频通知订阅、开始指令确认、已接收字节数、最终保存及 CRC 校验结果。
连接/GATT 失败最多尝试两次;每次默认 20 秒,可用 `--connect-timeout 30` 调整。
重试只用于建立连接,不重复执行录音。失败时直接给出所在阶段、原始错误和修复建议。
连接时只枚举本功能的服务,且请求不使用旧服务缓存;注意**刷新 GATT 缓存不等于清理配对密钥**。
每次运行自动生成带时间戳的 UTF-8 日志,第一行打印完整路径:
-
录音/下载:`--output-dir` 目录下的 `connection_logs`;默认 `D:\logs\ble_audio\connection_logs`。
-
`scan/status/stop/repair`:默认 `D:\logs\ble_audio\connection_logs`。
-
可用 `--log-file` 指定完整文件名,记录阶段提示、WinRT 会话/MTU/服务发现和异常堆栈。
```powershell
python $ble status --log-file D:\logs\ble_audio_status.log
python $ble repair --log-file D:\logs\ble_audio_repair.log
```
遇到问题,请保留此日志和同一时间段的开发板普通串口日志,便于关联分析。
3.6 停止及普通串口诊断
录音到达设定时长自动结束。异常时可以在普通串口执行:
```text
app ble_audio status
app ble_audio stop
```
也可在正在执行的 Python 窗口按 Ctrl+C:工具尝试发送停止指令并断开 BLE,板端断链回调独立保证停止。
中止的数据保留为 `received.partial`,不标为成功录音。提前停止的实时录音不会满足请求时长校验。
```powershell
python $ble stop
```
这条命令使用它自己建立的 BLE 连接。正在被另一个连接控制的传输会返回忙,避免跨连接停止别人的录音;
因此停止正在运行的 PC 任务优先用原窗口 Ctrl+C,或普通串口 `app ble_audio stop`。
串口日志示例:
```text
ble_audio: complete result=0 bytes=320000 crc=xxxxxxxx samples=160000
ble_audio: command=0x30 result=0 bytes=320000 samples=160000 queue=0 overflow=0
```
状态 `0x30` 空闲、`0x31` PCM、`0x32` Opus、`0x34` 文件下载;`result` 为上一任务的结果。
4. 实现原理与资源管理
```text
实时 PCM:MIC0 → ADC/DMA → PSRAM PCM 队列 → BLE Notify → PC 校验 → PCM/WAV
实时 Opus:MIC0 → ADC/DMA → PSRAM PCM 队列 → CPU libopus → Ogg → BLE → PC .opus
文件下载:NAND 已关闭的 PCM/Opus → 分块只读 → BLE Notify → PC 原样保存
控制:PC → 原 AA 四字节控制特征 → 主线程派发 → 独立音频传输线程
```
实时录音使用与原录音相同的 MIC0 选择和增益 `200`(20dB),固定 16kHz 单声道。
DMA 回调仅拷贝数据、统计采样数、标记溢出和唤醒线程。编码、CRC、文件读取和 BLE 发送都在线程执行。
16kHz/20ms 每帧 320 样本,Opus 16kbps CBR、复杂度 0。使用工程已经集成的 CPU libopus;
没有改动原厂 DSP 通话 AEC、音乐解码或已实现的 A2DP dump。
Opus 文件的时间戳采用规范的 48kHz granule;OpusHead 写入编码器 lookahead 对应的 pre-skip,
结束时补零冲刷编码器并用 EOS granule 裁掉补零。不会用 16kHz 采样数直接替代 Ogg 的 48kHz 时间戳。
资源仅在收到开始指令后启用:
-
独立工作线程,PSRAM 中的 64KB 编码状态、64KB 线程栈和 128KB PCM 队列。
-
DMA 缓冲仅 1KB,放内部 SRAM;不增加紧张的 DSP SHARE_RAM 占用。
-
任务期间申请 PARTIAL 唤醒锁和高性能频率;结束后释放。
-
使用驱动原来的单 ADC 所有权,不抢占 ASR、其它录音或通话。资源忙时应先停止对应功能,例如 `app asr off`。
-
来电、断链、停止、缓冲溢出、发送超时都会关闭本模块的 ADC/文件并释放连接引用及存储租约。
-
开机只注册服务和等待线程,不自动打开麦克风,不改屏幕启动流程。
5. BLE 协议,兼容旧控制格式
服务:`7b2e1000-8e7f-4d3a-9c61-2a3089c00001`。
| 特征 | UUID 尾部对应段 | 用途 |
| --- | --- | --- |
| 原控制特征 | `7b2e1001-...` | Write with response + Notify,原 AA 四字节指令 |
| 新数据特征 | `7b2e1002-...` | Notify,音频和开始/结束记录 |
| 新配置特征 | `7b2e1003-...` | Write with response,录音秒数或文件路径分片 |
UUID 的完整后半部分都为 `8e7f-4d3a-9c61-2a3089c00001`。
旧的录音 `00~03`、Find My `10/11/20/21` 控制值继续兼容。
原广播未包含本服务 UUID,所以 PC 按 `zkBLE` 名称筛选,连接后再校验 GATT 特征。
5.1 控制帧
格式仍是 `AA op control CRC8`,CRC8:poly=07、init=00、不反射,计算前 3 字节。
| 操作 | 完整十六进制帧 |
| --- | --- |
| 查询无线音频状态 | `AA 00 30 5F` |
| 停止本连接的无线音频任务 | `AA 01 30 4A` |
| 开始实时 PCM | `AA 01 31 4D` |
| 开始实时 Opus | `AA 01 32 44` |
| 开始下载配置好的文件 | `AA 01 34 56` |
必须先订阅控制通知和数据通知。ATT 写入成功仅表示请求进入队列;控制 Notify 表示受理结果;
数据流 END 表示传输最终结果。控制错误仍用 `81` 忙、`82` 参数/IO 错误、`83` 连接/订阅失效。
同一时刻只允许一个无线音频任务。
5.2 配置特征
-
时长:`02 seconds_le16`,例如 10 秒为 `02 0A 00`,范围 1~300。
-
文件路径:`01 total_len offset ASCII_bytes...`。总长与偏移均为一字节。
第一片 offset=0 清空旧路径;后续 offset 必须严格连续;完整路径校验通过后才允许下载。
工具每次最多写入 20 字节,默认 MTU23 也能分片配置。
- 正在传输时配置返回忙;断开连接后配置恢复默认 10 秒,路径清空。
5.3 数据通知和校验
每个 Notify 的前 8 字节为小端 `<2sHI>`:
```text
"BA"(2字节) + session_id(uint16) + stream_offset(uint32) + 本次数据片段
```
offset 是本会话逻辑流的累积字节偏移,第一片必须为 0。PC 检查同一会话号和偏移连续性。
每次最多通知 244 字节,并按实际 MTU 缩小。默认 MTU23 可下载文件;实时采集要求 MTU 至少 64。
Notify 队列暂时忙时有界重试;3 秒仍不能发送则失败。固件请求 7.5~15ms 连接间隔,最终由 PC 决定。
去掉每个通知头后,逻辑流由若干记录组成:`type:uint8 + reserved:0 + length:uint16 + payload`。
记录允许跨多个通知分片;主机不能把一次 Notify 当成一个音频帧。
| type | payload |
| --- | --- |
| 1 / START | 24 字节 `<BBBBIHHIII>`:协议版本1、模式1/2/3、格式1PCM/2Ogg、声道、采样率、位深、保留0、秒数、源文件大小、码率 |
| 2 / DATA | PCM 字节、完整 Ogg 页或已有文件字节;最终文件是所有 DATA 按序连接 |
| 3 / END | 16 字节 `<iIII>`:有符号结果码、总 DATA 字节数、IEEE CRC32、实际 PCM 采样数 |
文件模式不臆测裸 PCM 参数,START 的采样率/声道/位深填 0,以原文件大小校验。
CRC32 与 Python `zlib.crc32` 一致,仅计算 DATA 内容。
PC 必须收到结果码0、正确总长度和正确 CRC32 才改名为正式音频;缺少 END、丢包、错序、错误 CRC 或提前停止都不报成功。
6. 代码位置和修改方法
以下路径相对 `` 根目录。
| 文件 | 负责内容 |
| --- | --- |
| `application/bt_watch/tools/ble_audio/ble_audio.py` | 上位机完整代码:`run()` 按名称扫描、repair 和命令流程;`connected_board()` 连接/服务检查/有界重试/释放;`connection_options()` 获取实际地址类型;`report()/setup_log()` 中文提示及 UTF-8 日志;`Receiver` 拼片与校验;`pcm_to_wav()/decode_opus()` 生成试听文件;`main()` 命令行 |
| `application/bt_watch/tools/ble_audio/requirements.txt` | Windows BLE 依赖版本 |
| `application/bt_watch/src/main/ble_audio_transfer.c/.h` | 固件主实现:配置、作业所有权、ADC、CPU Opus/Ogg、已有文件读取、分片发送、断链清理 |
| `application/bt_watch/src/main/ble_record_service.c` | 在原服务末尾添加数据/配置特征;原控制特征分派音频扩展命令 |
| `application/bt_watch/src/main/ble_record_protocol.h` | 新增 `0x30/31/32/34` 合法控制值,原 CRC8 格式不变 |
| `application/bt_watch/src/main/system_app_shell.c` | 普通串口 `app ble_audio status/stop` |
| `application/bt_watch/src/main/usb_serial_runtime.c` | 文件读取租约与 USB MSC 切换的同锁互斥检查 |
| `zephyr/include/drivers/console/usb_serial_runtime.h` | 存储租约接口声明 |
| `application/bt_watch/Kconfig` | `CONFIG_APP_BLE_AUDIO_TRANSFER` 开关 |
| `application/bt_watch/src/main/CMakeLists.txt` | 模块编译接入,复用已集成的 libopus |
| `application/bt_watch/tests/ble_audio/` | ARM 生产代码仿真、PC 解析/流程测试 |
重要改动采用 `//zk+`、`//zk+-` 和中文注释;Python/CMake/Kconfig 使用对应 `#zk+/#zk+-`。
若修改采样率或声道,需要同步 ADC 参数、Opus 参数、每帧采样数、START 元数据、PC 格式检查和 WAV 参数。
不要只修改 WAV 文件头,原始 PCM 必须与声明格式一致。
7. 故障定位
| 现象 | 原因及处理 |
| --- | --- |
| 找不到 zkBLE | 打开电脑蓝牙、开发板上电;关闭手机占用 BLE 的 App;普通串口可执行 `bt ble_test on`;用 `scan --all` 核对名称 |
| `Unreachable` / 服务不可访问 | 已发现广播,但 GATT 未就绪;本次实测为旧配对密钥状态不一致,执行 `python $ble repair` 后恢复。其它情况还需检查手机占用、距离、驱动和诊断日志 |
| 缺少 1002/1003 特征 | 旧固件或 Windows GATT 缓存;烧录本版并刷新设备连接 |
| 控制错误 81 / 结果 -16 | 另一无线任务、ASR、录音、通话或 U 盘模式占用资源;停止占用后重试 |
| 结果 -139 | PCM 队列溢出,实际无线速度不足;用 Opus、改善信号或录完后下载文件 |
| 结果 -122 | 实时模式 MTU 太小;更新蓝牙驱动后重连;文件下载可在 MTU23 工作 |
| 结果 -116 | 无 ADC 数据或 BLE 发送超时;查看串口状态、检查连接 |
| 结果 -2 | `/NAND:/` 下没有指定文件;核对自动编号 rec/rec1/rec2 |
| 只有 `.partial` | 数据不完整;`metadata.json` 保存失败原因,不应当作正常音频分析 |
| PCM 播放变调/时间异常 | 裸 PCM 采样率/声道填写错误;实时固定16k单声道,下载模式按文件实际参数 |
| Opus 保存成功、转换失败 | BLE 文件已校验保存;安装 FFmpeg 后可单独解码,不必重新录制 |
7.1 本次 `log_26_10_12.txt` 的具体结论
最新故障发生在 2026-10-16 的 17:08,不能把文件前面累积的其它通话/A2DP 日志混为同一故障:
- `17:08:54.844 Ble connected MAC: 5bd2b7778eec`:已经建立 BLE 物理连接。
字节倒序对应 `EC:8E:77:B7:D2:5B`,与本机 Intel 蓝牙适配器地址一致。
- `17:08:54.999 TX: 01 1b 20 02 01 06`:HCI 命令 `0x201B`,即
`BT_HCI_OP_LE_LTK_REQ_NEG_REPLY`,开发板无法提供本次加密请求所需的长期密钥。
工程 `framework/bluetooth/bt_stack/src/bt_stack/hci_core.c` 的 `le_ltk_request()`
在 `bt_smp_request_ltk()` 失败时走此分支。
- `17:08:55.109 ... reason: 19` / `reason=0x13`:约 265ms 后由对端终止连接。
随后的 `Not connected / Exchange failed mtu:0` 是断链后的结果,不能据此认定 MTU 是首因。
- PC 查询显示该板 `IsPaired=True`。用仅枚举目标服务、禁用 GATT 缓存的方式仍复现 `Unreachable`;
单独移除该板旧配对后,立即连接成功、MTU247、三个特征齐全,状态返回 `AA 00 30 5F`。
这些结果共同确认本次问题来自 PC 留存旧配对、开发板对应密钥不可用。
此日志无法确定开发板密钥是在何时被擦除或失效;不把烧录、重置或其它操作作为已证实原因。
手机可以连接并不矛盾:手机和电脑是不同对端,持有的配对记录不同。
旧工具在 `BleakClient.aenter()` 完成 GATT 发现之后才打印"已连接",
因此失败前只看到"正在查找",缺少过程提示。新版已分别打印并记录这些阶段。
8. 编译与验证范围
v1 固件完整编译和打包成功。新增模块通过 65588 项 ARM 断言;连接修复后的上位机通过 23 项测试;
原 BLE 录音测试通过,USB 自动模式及新增存储租约通过 220 项断言。
最终资源占用:SRAM 875776 字节、PSRAM 32809952 字节、SHARE_RAM 20948 字节。
SHARE_RAM 与修改前一致;固件 SHA256 见固件目录中的同名校验文件。
```powershell
cd .\zephyr
python build.py -n -b ats3089p_dev_watch -a bt_watch
cd ..
python -X utf8 -B .\application\bt_watch\tests\ble_audio\run_tests.py
python -X utf8 -B .\application\bt_watch\tests\ble_audio\test_pc_tool.py
```
ARM 测试运行生产传输代码,替换硬件边界,覆盖全部 op/control 组合、资源忙、MTU23 文件下载、
ADC 精确样本数、启动稳定窗口、Opus 尾部封装、缓冲溢出、断链、文件失败和资源释放。
PC 测试使用 ARM 生产发送器生成的通知流验证 PCM 逐样本一致、Ogg CRC/pre-skip/EOS、跨 MTU 分片、
丢包、重复、错序、CRC 错误、无 END、自动按名称连接及异常停止。
这些测试中的 Opus 编码调用使用替身以隔离状态机,不用于宣称编码音质或 CPU 实时性能。
2026-10-16 本机实板复测已完成旧配对故障复现及定向清理、MTU247 连接、三个特征发现、
状态查询、`repair` 命令及断开后重新连接。10 秒 Opus 实际收到 34167 字节,CRC32=`155633C0`,
并用 FFmpeg 成功解码为 PCM/WAV。另一次 10 秒原始 PCM 收到 320000 字节,CRC32=`47E8B954`,
同时生成 WAV。实测文件及连接日志在 `application/bt_watch/outdir/ble_audio_hardware/`。
这次连接修复仅修改 Python 上位机及相关测试/中文说明,未改动或重新打包固件。
以上结果用于确认链路和文件完整性;未做录音音质、远距离或长时间运行评估。