Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54bf67cafa | ||
|
|
551a7bb1e6 | ||
|
|
3a0714c735 | ||
|
|
5c3e7eee6d | ||
|
|
3e463f57b6 | ||
|
|
4b565958f9 | ||
|
|
f283223b23 |
@@ -2,9 +2,6 @@
|
||||
/target/
|
||||
/version/
|
||||
|
||||
# Local legacy-board packages retained when switching to the new-board branch.
|
||||
/artifacts/legacy-no-gnss/
|
||||
|
||||
# Extracted toolchain and local compiler license; keep compile_tools.zip in LFS.
|
||||
/ql-cross-tool/win32/owtoolchain/
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# 旧板专用分支:legacy-board
|
||||
|
||||
此分支用于没有 GNSS 芯片的旧板,后续旧板修改在此分支维护。
|
||||
|
||||
`ql-config/config/board_features.h` 中 `NFC_GNSS_BOARD_HAS_GNSS` 默认为 `0`,
|
||||
跳过内核 GNSS 型号识别,关闭应用定位任务和定位模式切换。
|
||||
切换到此版本时必须重新编译并更新内核和 APP。
|
||||
|
||||
修改说明、构建步骤和板上验收要求见 [旧板说明](artifacts/legacy-no-gnss/README.md)。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Local firmware exports, build logs, verification output and build-file backups.
|
||||
*.zip
|
||||
*.log
|
||||
*.disasm.txt
|
||||
/verification.json
|
||||
/build-backup/
|
||||
@@ -0,0 +1,53 @@
|
||||
# 无 GNSS 旧板调试版本
|
||||
|
||||
本分支名称为 `legacy-board`,用于后续无 GNSS 旧板开发。
|
||||
本目录的说明和验证脚本纳入版本控制;烧录包、日志、验证结果及备份仅保留在本地。
|
||||
|
||||
当前开关位于 `ql-config/config/board_features.h`:
|
||||
|
||||
```c
|
||||
#define NFC_GNSS_BOARD_HAS_GNSS 0
|
||||
```
|
||||
|
||||
- 内核不创建 `q_gocg` 型号识别任务,避免无定位芯片时触发 `gps_app.c` 的断言复位。
|
||||
- `app_gnss_init()` 不创建定位任务,包括联网成功后的调用。
|
||||
- `set_gnss_mode()` 忽略定位模式请求,避免无定位任务时无法退出 FAST 模式、阻止休眠。
|
||||
- 定位数据保持无效,不伪造坐标或定位成功状态。
|
||||
- 未保存音量时默认使用 100% 档位;已有保存音量时仍使用保存值。
|
||||
|
||||
在 SDK 根目录依次运行:
|
||||
|
||||
```bat
|
||||
build.bat kernel
|
||||
build.bat app
|
||||
build.bat firmware
|
||||
```
|
||||
|
||||
必须更新内核和 APP;只烧 APP 无法移除旧内核中的断言。
|
||||
烧录输入使用生成目录中的内层 ZIP,不使用包含 DBG 的外层归档。
|
||||
`original-EG800AKCN_11LG.zip` 是修改前外层归档的备份。
|
||||
|
||||
本次已导出可直接交给烧录工具的 `EG800AKCN_11LG-legacy-no-gnss.zip`
|
||||
(6,519,833 字节)。`EG800AKCN_11LG-legacy-no-gnss-debug.zip` 保存对应调试归档。
|
||||
仍沿用 EG800AKCN_11LG 的分区布局和 GNSS 资源文件;本修改关闭定位启动行为,
|
||||
不是更换模组型号或重做其他硬件的板级适配。
|
||||
|
||||
验证结果:内核、APP 和打包完成;内层 ZIP 的 CRC 检查通过;包内 `cp.bin`
|
||||
及 `customer_app.bin` 与本次构建输出逐字节一致;反汇编确认型号识别入口
|
||||
和应用定位初始化只输出日志,模式设置函数直接返回。
|
||||
详情和 SHA256 见 `verification.json`,构建日志及反汇编保存在同目录。
|
||||
本次未烧录设备,尚未完成板上功能验证。
|
||||
|
||||
重新构建后,将 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip` 复制到本目录,
|
||||
命名为 `EG800AKCN_11LG-legacy-no-gnss.zip`,在 SDK 根目录执行:
|
||||
|
||||
```bat
|
||||
ql-cross-tool\win32\owtoolchain\Python\python.exe artifacts\legacy-no-gnss\verify_package.py
|
||||
```
|
||||
|
||||
恢复带 GNSS 板时将开关改为 `1`,重新编译内核和 APP 并打包。
|
||||
两份构建都必须重新编译对应 GNSS 源文件;检查日志,不要只依赖批处理退出码。
|
||||
|
||||
板上验收:启动后不再出现 `No model was found for val` / `q_gocg` 断言;
|
||||
联网后继续运行至少一分钟,并实际检查网络、音频和 NFC 等所需功能。
|
||||
构建成功不能代替上述板上验证。
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Verify the exported legacy-board package against this SDK build."""
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import zipfile
|
||||
|
||||
output = Path(__file__).resolve().parent
|
||||
root = output.parent.parent
|
||||
package = output / "EG800AKCN_11LG-legacy-no-gnss.zip"
|
||||
objdump = root / "ql-cross-tool/win32/owtoolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objdump.exe"
|
||||
report = {"package": package.name, "sha256": hashlib.sha256(package.read_bytes()).hexdigest()}
|
||||
app_symbols = subprocess.check_output(
|
||||
[str(objdump.with_name("arm-none-eabi-nm.exe")), str(root / "ql-application/threadx/build/app.elf")], text=True
|
||||
)
|
||||
printf_address = int(re.search(r"^([0-9a-fA-F]+) T printf$", app_symbols, re.MULTILINE).group(1), 16)
|
||||
with zipfile.ZipFile(package) as archive:
|
||||
assert archive.testzip() is None, "ZIP CRC failure"
|
||||
for entry, source in {
|
||||
"cp.bin": "ql-kernel/threadx/build/release_kernel.bin",
|
||||
"customer_app.bin": "ql-application/threadx/build/app.bin",
|
||||
}.items():
|
||||
data = archive.read(entry)
|
||||
assert data == (root / source).read_bytes(), f"Stale image: {entry}"
|
||||
report[entry] = {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
report["download_manifest"] = json.loads(archive.read("download.json"))
|
||||
|
||||
for symbol, binary, required in [
|
||||
("quec_get_gnssid_task", "ql-kernel/threadx/build/kernel.axf", "<RTI_LOG>"),
|
||||
("app_gnss_init", "ql-application/threadx/build/app.elf", f"0x{printf_address | 1:08x}"),
|
||||
("set_gnss_mode", "ql-application/threadx/build/app.elf", "bx\tlr"),
|
||||
]:
|
||||
disassembly = subprocess.check_output(
|
||||
[str(objdump), "-d", f"--disassemble={symbol}", str(root / binary)], text=True
|
||||
)
|
||||
assert f"<{symbol}>:" in disassembly, f"Missing function: {symbol}"
|
||||
assert required in disassembly, f"Unexpected function body: {symbol}"
|
||||
assert "task_create" not in disassembly, f"Task still created: {symbol}"
|
||||
(output / f"{symbol}.disasm.txt").write_text(disassembly, encoding="utf-8")
|
||||
report["verification"] = "PASS: ZIP CRC, kernel and APP byte identity, GNSS entry disassembly"
|
||||
report["hardware_test"] = "Not performed"
|
||||
(output / "verification.json").write_text(json.dumps(report, indent=2), encoding="utf-8")
|
||||
print(report["verification"])
|
||||
print("SHA256:", report["sha256"])
|
||||
+7
-11
@@ -1,13 +1,9 @@
|
||||
# WSS 联调包
|
||||
# 旧板 WSS 联调包
|
||||
|
||||
最新域名试验包:new-board/mqtt-domain/EG800AKCN_11LG-new-board-wss-mqtt-domain.zip。MQTT 改为 banban.api.talkingq.com:1883,保留诊断日志;用户已确认改域名后新板连接成功,详细成功日志和其他业务验收尚未提供。
|
||||
最新优化包为 `full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip`,供无 GNSS 旧板联调,默认连接 `wss://banban.api.talkingq.com:443/ws`。默认音量仍为 100(已有保存值按原逻辑读取)。这是完整多分区包,沿用上一版 NIST 加速内核,APP 调整 WSS 的 ECDHE 曲线优先级为 X25519 优先,保留其他已启用曲线作为回退。每次仍执行完整握手,按键后建连。
|
||||
|
||||
最新网络诊断包:new-board/mqtt-diag/EG800AKCN_11LG-new-board-wss-mqtt-diag.zip。沿用新板 WSS 内核,APP 补充 MQTT 收发、CONNACK 和语音键网络准入日志,具体根因待新板日志确认。
|
||||
|
||||
当前 main 对应新板,下载包为 [EG800AKCN_11LG-new-board-wss-x25519.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip)。这是包含本次内核和 APP 的完整多分区包,默认连接 `wss://banban.api.talkingq.com:443/ws`,保留新板 GNSS。音量沿用原 main 逻辑,读取不到保存值时回退至 70。
|
||||
|
||||
新板已经完成内核、APP、固件打包和主机/静态验证,尚待新板上板测试。包哈希、镜像一致性、GNSS 任务入口、TLS 算法与时间检查结果见 `new-board/verification.json`;迁移说明见 [新板 WSS 文档](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。
|
||||
|
||||
旧板包 `full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip` 保持原样,属于无 GNSS、默认音量 100 的旧板配置。旧板 2026-09-16 日志确认 X25519 完整 TLS 握手 1455 ms;该数值不是新板实测结果。旧板 NIST 版本、接收修复版本及更早实验包保留用于历史追溯。完整旧板过程见 [旧板总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。
|
||||
|
||||
构建日志、测试报告、公共证书和包体在本目录本地保存并被 Git 忽略;提交源码与文档不会把这些本地包一并上传。
|
||||
2026-09-16 14:38 板端确认 NIST 加速后完整 TLS 握手为 1820 ms;15:52 补充日志进一步确认 X25519 实际协商为 group=9,客户端密钥交换从 445 ms 降至 135 ms,完整 TLS 握手降至 1455 ms,HTTP 101 正常。两份日志样本中 TLS 再缩短约 20%。包校验与 SHA-256 见 `full-handshake/verification.json`;其中待上板字段为生成时历史状态,新增运行证据见 [完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。
|
||||
|
||||
2026-09-16 11:32 复测:443 `/ws` 已返回 HTTP 101,TLS 和证书校验通过;8080 仍是明文 WS。`tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip`(1820 ms)和 `recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip` 均保留作已上板通过的回退版本。本目录原 `EG800AKCN_11LG-legacy-wss.zip` 缺少 ECDSA,`ecc-fix` 包存在接收误判,均仅用于历史对照。
|
||||
|
||||
原旧板包保留在 `../legacy-no-gnss/`。构建日志、探测 JSON、导出的公共证书和包体在此目录本地保存并被 Git 忽略。源码和详细步骤见 `../../docs/talkingq_wss.md`。
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
@echo off
|
||||
set CURDIR=%~dp0
|
||||
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMddHHmmss"') do set appbuildtime=%%i
|
||||
set time0=%time: =0%
|
||||
set hour=%time0:~0,2%
|
||||
set appbuildtime=%date:~0,4%%date:~5,2%%date:~8,2%%hour%%time:~3,2%%time:~6,2%
|
||||
set appbuildrev=NULL
|
||||
set appbuildmodel=EG800AKCN_11LG
|
||||
set appbuildver=beta1
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# 旧板 WSS 适配、故障修复与性能优化完整总结
|
||||
|
||||
> 新板 main 说明:本文保留旧板历史过程、配置与实测数据。新板已迁入 WSS,保留 GNSS 与原 main 音量行为;新板构建、下载包及验收请见 [新板 WSS 迁移说明](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。旧板实测耗时不代表新板实测。
|
||||
|
||||
> 整理日期:2026-09-16;设备日志时间按用户提供的串口时间记录。
|
||||
>
|
||||
> 工作区:`D:\banban\project\nfc_gnss`。分支:`legacy-board-wss`。本文覆盖的最新代码提交:`551a7bb1e657c12f45ac0a1f2ca851625723a63d`。
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
# 新板 main 分支 WSS 迁移说明
|
||||
|
||||
> 最新上板反馈(2026-09-16):用户确认将 MQTT 改为 banban.api.talkingq.com:1883 后已连接成功。该反馈支持域名访问方式适配新板受限 eSIM 的判断;未提供本次成功日志,本文不据此宣称 GNSS、全部业务及 WSS 性能均已验收。
|
||||
|
||||
更新日期:2026-09-16。适用工作区:`D:\banban\project\nfc_gnss`,分支:`main`。
|
||||
|
||||
## 1. 迁移范围
|
||||
|
||||
以原主分支 `2746cc3d4ebbd90f8fa0c1e68db93b199b297714` 的新板代码为基础,迁入旧板已经完成联调的 WSS 实现、故障修复、完整握手优化与测试工具。新板仍使用自身的 GNSS 检测和定位任务。
|
||||
|
||||
| 旧板来源提交 | 迁入内容 |
|
||||
| --- | --- |
|
||||
| `3e463f5` | WSS 地址解析、可信根 CA、服务器证书及域名验证、WebSocket Upgrade 校验 |
|
||||
| `5c3e7ee` | 内核 ECDSA/GCM 能力、APP 与内核共享配置、RTC 证书日期校验、TLS 接收重试修复 |
|
||||
| `3a0714c` | NIST 曲线模约减加速、握手阶段计时、连接循环日志修正 |
|
||||
| `551a7bb` | 完整握手优先协商 X25519,保留原有其他曲线作为回退 |
|
||||
| `54bf67c` | 旧板联调过程、算法说明与实测总结,作为历史资料保留 |
|
||||
|
||||
这些提交的修改迁入工作区,不直接合并旧板分支。旧板关闭 GNSS 的提交 `f283223` 和音量改为 100 的提交 `4b56595` 未迁入。
|
||||
|
||||
## 2. 新板硬件与业务行为
|
||||
|
||||
- 内核 `quec_get_gnssid_task()` 仍创建 GNSS 型号识别任务 `quec_get_ocname_by_gnss_task`。
|
||||
- APP `app_gnss_init()` 仍创建定位任务 `quec_gnss_test`,`set_gnss_mode()` 仍更新定位模式。
|
||||
- `audio.c` 与原主分支一致:音量优先读取设备保存值,读取失败时回退至 70。
|
||||
- 设备身份仍沿用 `device_id` / `serial_number`,不改后台鉴权格式。
|
||||
- 按键触发连接;每次新连接仍执行完整 TLS 握手,没有提前建连或新增会话恢复逻辑。
|
||||
|
||||
## 3. WSS 与密码算法
|
||||
|
||||
默认地址:`wss://banban.api.talkingq.com:443/ws`。
|
||||
|
||||
使用 TLS 1.2,配置 ISRG Root X2 公共根 CA,验证服务器证书链、有效期和主机名。单向 TLS 不需要设备客户端证书,但服务器证书校验仍必须通过。WebSocket 层继续验证 HTTP 101 与 `Sec-WebSocket-Accept`。
|
||||
|
||||
内核提供 ECDSA 签名验证、ECDHE 密钥交换及 AES-GCM 等能力。APP 和内核共用 `talkingq_mbedtls_config.h`,避免跨边界使用 mbedTLS 结构体时因宏配置不同导致布局不一致。证书时间检查使用真实 RTC 和 UTC 转换,不再依赖原空时间实现。
|
||||
|
||||
ECDHE 优先使用 X25519;证书签名算法不因此改变。NIST 优化针对证书链和回退曲线的有限域计算,X25519 优化针对临时密钥交换,二者可同时受益。服务器最终选择的曲线以设备协商日志为准。
|
||||
|
||||
TLS 非阻塞读写返回 `WANT_READ` / `WANT_WRITE` 时继续等待或重试,不把暂时没有数据误判为连接断开。工作线程使用 16 KiB 栈,并保留完整握手总耗时、各阶段耗时及协商信息日志。
|
||||
|
||||
更多算法和问题根因见 [旧板完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。其中的默认音量、关闭 GNSS 和实测性能均属于旧板历史。
|
||||
|
||||
## 4. 构建与切分支缓存
|
||||
|
||||
从原新板 main 升级至此版本,需要同时更新内核和 APP,因为原内核没有本次补齐的完整 TLS 算法与配置。不能只编译 APP 并沿用原 main 内核。
|
||||
|
||||
在仓库根目录执行:
|
||||
|
||||
```bat
|
||||
build.bat kernel
|
||||
build.bat app
|
||||
build.bat firmware
|
||||
```
|
||||
|
||||
首次切换发现旧 GNSS `.d` 文件仍引用旧板专用 `board_features.h`,导致内核构建提示依赖不存在。这是跨分支残留编译缓存,不是新板缺失硬件配置。处理方法为备份并移走内核 `gps_app` 和 APP `gnss` 的旧 `.o`、`.d` 文件,再重新构建;没有添加旧板配置头或跳过 GNSS 编译。
|
||||
|
||||
本次缓存备份和构建日志位于 `artifacts/wss/new-board/`。SDK 生成的诊断对象和链接反馈文件另行备份,包生成后恢复其原始受版本控制的内容,避免把构建副产物混入源码修改。
|
||||
|
||||
`build.bat firmware` 生成完整多分区固件包。提供的下载包来自 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip` 内层 ZIP;它不是仅含 APP 的更新包。
|
||||
|
||||
## 5. 产物与验证
|
||||
|
||||
新板包:[EG800AKCN_11LG-new-board-wss-x25519.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip)。
|
||||
|
||||
本地验证报告:[verification.json](D:/banban/project/nfc_gnss/artifacts/wss/new-board/verification.json)。该报告记录包 SHA-256、镜像大小和哈希、下载清单及检查结果。
|
||||
|
||||
验证覆盖:
|
||||
|
||||
- WSS URL、HTTP Upgrade、TLS 读写及接收回归测试。
|
||||
- APP/内核 mbedTLS ABI、实际 SDK 证书链验证及 ECDH 运算测试。
|
||||
- ZIP CRC,以及包内 `cp.bin`、`customer_app.bin` 与本次构建输出逐字节一致性。
|
||||
- 最终 ELF 中 ECDSA、GCM、NIST 优化、RTC 时间转换和 WSS 关键符号。
|
||||
- 反汇编确认 GNSS 识别及定位入口仍创建任务,且对应 worker 存在;新板镜像不包含旧板跳过 GNSS 的日志标记。
|
||||
|
||||
主机算法测试仅证明算法与配置可用,不能代替设备端速度测量。构建和静态校验也不代表已经完成新板上电、定位或语音业务测试。
|
||||
|
||||
## 6. 上板验收与影响
|
||||
|
||||
新板仍需验证正常启动、GNSS 识别、定位任务和语音业务。WSS 日志应出现证书/域名验证成功、HTTP 101,以及原有设备身份发送流程。关注 RTC 是否有效、网络信号、完整握手协商曲线与各阶段耗时。
|
||||
|
||||
旧板实测从最初约 9.6 秒 TLS 握手,经 NIST 优化降至 1820 ms,再经 X25519 优化降至 1455 ms。上述数据不能直接标注为新板测试结果;新板 GNSS 等任务可能影响调度和 CPU 负载,需要采集新日志比较。
|
||||
|
||||
证书校验继续要求正确时间、匹配域名和受信任证书链;未来后台更换证书链或根 CA 时需要复核。共享 TLS 配置改变了内核密码能力,其他使用相同内核接口的模块也需在新板回归时观察。
|
||||
|
||||
旧板分支和历史包保留,供旧板使用与回溯。新板使用本说明中的独立包目录,避免与 `artifacts/wss/full-handshake/` 的旧板包混淆。
|
||||
|
||||
|
||||
## 7. MQTT 连接诊断版本(2026-09-16)
|
||||
|
||||
新板 16:43 日志确认拨号获得 IP,但 MQTTConnect 返回 -1。语音键被识别后,因现有入口要求“拨号在线且 MQTT 在线”,播放网络检查提示,尚未发起 WSS。本轮增加连接诊断,GNSS 及网络准入、鉴权、重连策略保持原逻辑。
|
||||
|
||||
诊断包:[EG800AKCN_11LG-new-board-wss-mqtt-diag.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/mqtt-diag/EG800AKCN_11LG-new-board-wss-mqtt-diag.zip)。这是完整多分区包;内核与上一新板 WSS 包逐字节相同,APP 增加日志。已刷入上一新板 WSS 内核时,本轮只需 build.bat app 和 build.bat firmware,无需重编内核。报告和构建日志位于包的同一目录。
|
||||
|
||||
| 日志 | 含义 |
|
||||
| --- | --- |
|
||||
| [MQTT-DIAG] TCP begin/end | MQTT 服务地址、端口、拨号状态、连接返回值与耗时 |
|
||||
| CONNECT begin | 协议版本、会话选项、超时及 SDK 客户端状态 |
|
||||
| write request / rc | SDK 写回调请求及返回字节数;负值表示失败 |
|
||||
| read request / rc | SDK 读回调返回值及耗时;0 或负值不等于鉴权拒绝 |
|
||||
| CONNECT end | MQTT 最终返回值、总耗时、读写次数及字节数 |
|
||||
| CONNACK code=0 | 服务端接受连接 |
|
||||
| CONNACK code=1/2/3/4/5 | 协议版本不支持 / Client ID 被拒绝 / 服务不可用 / 用户名或密码错误 / 未授权 |
|
||||
| no response bytes returned by SDK read | SDK 读回调未返回有效字节,不能单凭此项断定服务端原因 |
|
||||
| no valid initial CONNACK | 已收到数据,但开头不构成完整合法 CONNACK |
|
||||
| failed before transport write | 尚未调用发送回调,优先检查客户端状态或 CONNECT 序列化 |
|
||||
| [NET-GATE] blocked_mqtt_offline | 按键已识别,但 MQTT 未在线,因此未尝试 WSS |
|
||||
| [NET-GATE] start_voice | 通过网络准入,进入原语音流程 |
|
||||
|
||||
SDK 读回调把 EOF 和部分 socket 错误统一映射为 -1。errno_snapshot 是即时读取的错误快照,可能残留旧值,不能单凭它断言服务端拒绝或关闭。只有完整合法 CONNACK 中的拒绝码才作为服务端拒绝证据。诊断只观察原回调,不替换底层 socket 实现。
|
||||
|
||||
诊断仅在同步 MQTT CONNECT 期间挂接原有读写回调,结束即恢复,随后才启动 MQTT 后台线程。代码不打印 CONNECT 原文、账号、密码或业务报文,仅暂存接收开头四字节用于识别 CONNACK。诊断状态按现有单一 MQTT 连接工作线程的串行使用约束实现。
|
||||
|
||||
在 Visual Studio 开发命令行运行 tools/test_mqtt_diag.py,可测试参数和返回值透传、成功与失败后回调恢复、分片 CONNACK、服务端拒绝、无效响应、读写错误、无发送提前返回、时钟回绕,以及凭据测试标记不出现在日志中。
|
||||
|
||||
APP 构建、诊断测试和固件镜像校验已通过。SDK 原有隐式声明等编译警告仍存在,本轮没有修改这些接口。具体 MQTT 根因仍需新板运行诊断包后取得日志。
|
||||
|
||||
## 8. MQTT 域名试验版本(2026-09-16)
|
||||
|
||||
根据新板使用受限 eSIM 的情况,将 mqtt.c 中 DEFAULT_SERVER_DOMAIN 从 101.35.224.118 改为 banban.api.talkingq.com,端口仍为 1883,沿用明文 MQTT 和原有鉴权、注册流程。保留 MQTT-DIAG 与 NET-GATE 日志。
|
||||
|
||||
电脑侧已验证该域名解析到 101.35.224.118,使用固件现有账号、临时 Client ID 可收到 CONNACK=0。该结果不能代替新板 eSIM 白名单验证:若卡商还限制端口或协议,仅改域名可能仍无法通过。
|
||||
|
||||
本次包:[EG800AKCN_11LG-new-board-wss-mqtt-domain.zip](D:/banban/project/nfc_gnss/artifacts/wss/new-board/mqtt-domain/EG800AKCN_11LG-new-board-wss-mqtt-domain.zip)。内核与上一诊断包相同,APP 编译、固件打包和镜像校验通过;用户随后确认改域名后已连接成功。已使用本轮新板 WSS 内核时,只需 build.bat app 和 build.bat firmware。本包仍是完整多分区包。
|
||||
|
||||
开机应看到 [MQTT-DIAG] TCP begin host=banban.api.talkingq.com port=1883。随后观察 CONNACK、设备注册和按语音键后的 WSS 连接结果。本轮不更改提示音下载地址。
|
||||
@@ -1,7 +1,5 @@
|
||||
# 旧板 WSS 适配与联调
|
||||
|
||||
> 新板 main 说明:本文保留旧板历史过程、配置与实测数据。新板已迁入 WSS,保留 GNSS 与原 main 音量行为;新板构建、下载包及验收请见 [新板 WSS 迁移说明](D:/banban/project/nfc_gnss/docs/new_board_wss.md)。旧板实测耗时不代表新板实测。
|
||||
|
||||
本次修改位于 `legacy-board-wss`(基于 `legacy-board`),默认地址为 `wss://banban.api.talkingq.com:443/ws`。沿用无 GNSS、默认音量 100 的旧板配置。上板暴露内核缺少 ECDSA 后,已补齐内核 TLS 能力并统一 APP/内核配置;从原明文旧板版本升级时必须更新内核和 APP。已有匹配 NIST 加速内核时,最新 X25519 调整只需更新 APP。
|
||||
|
||||
完整的问题根因、算法、提交、性能数据和影响说明见 [WSS 适配与性能优化完整总结](D:/banban/project/nfc_gnss/docs/WSS适配与性能优化完整总结.md)。2026-09-16 15:52 最新板端已确认 X25519 协商、完整 TLS 握手 1455 ms 和 HTTP 101。
|
||||
|
||||
@@ -81,7 +81,6 @@ SRC_FILES+= \
|
||||
nfc.c \
|
||||
DP1312EA.c \
|
||||
mqtt.c \
|
||||
mqtt_connect_diag.c \
|
||||
vad_det.c \
|
||||
sleep_wake.c \
|
||||
battery.c \
|
||||
|
||||
@@ -414,9 +414,9 @@ uint8_t app_get_volume(void)
|
||||
printf("get volume:%d, ret:%d\r\n", value, ret);
|
||||
//ret = read_volume_file(&value, 1);
|
||||
if(ret<0){
|
||||
g_cur_volume = 70;
|
||||
g_cur_volume = 100;
|
||||
hal_kv_set("volume", &g_cur_volume, 1);
|
||||
return VOLUME_70;
|
||||
return VOLUME_100;
|
||||
}
|
||||
|
||||
g_cur_volume = value;
|
||||
|
||||
@@ -19,11 +19,9 @@
|
||||
#include "ql_atcmd.h"
|
||||
#include "app_inc.h"
|
||||
#include "ql_uart.h"
|
||||
#include "ql_application.h"
|
||||
#include "ql_log.h"
|
||||
#include "ql_adc.h"
|
||||
#include "ql_power.h"
|
||||
#include "hal_api.h"
|
||||
#include "ql_application.h"
|
||||
#include "ql_log.h"
|
||||
#include "hal_api.h"
|
||||
|
||||
// 电量阈值定义
|
||||
#define BAT_VOLTAGE_100 4200
|
||||
@@ -38,24 +36,25 @@
|
||||
#define BAT_VOLTAGE_10 3550
|
||||
#define BAT_VOLTAGE_MIN 3500
|
||||
|
||||
#define BAT_LOW_WARN_MV 3550
|
||||
#define BAT_LOW_PROBE_EXIT_MV 3600
|
||||
#define BAT_SHUTDOWN_MV 3500
|
||||
#define BAT_VALID_MIN_MV 2500
|
||||
#define BAT_VALID_MAX_MV 5000
|
||||
#define BAT_SHUTDOWN_CONFIRM_COUNT 3
|
||||
|
||||
#define BAT_NORMAL_SAMPLE_CALLS 6
|
||||
// ========== 新增:10分钟电压历史记录用于判断充放电状态 ==========
|
||||
#define VOLTAGE_HISTORY_SIZE 60 // 10分钟 = 60个10秒采样点
|
||||
#define VOLTAGE_RISE_THRESHOLD 15 // 10分钟内上升超过15mV判定为充电
|
||||
#define VOLTAGE_DROP_THRESHOLD 15 // 10分钟内下降超过15mV判定为放电
|
||||
#define VOLTAGE_STABLE_RANGE 10 // 变化在±10mV内判定为稳定
|
||||
|
||||
unsigned short g_vbat_vol = 0;
|
||||
unsigned short vbat_sample[6] = {0}; // 修改:样本数改为6
|
||||
unsigned char battery_first_update_flag = 0;
|
||||
|
||||
static bat_level_t g_bat_level_last = BAT_LEVEL_100;
|
||||
static unsigned char g_bat_level_valid = 0;
|
||||
static unsigned char g_low_voltage_count = 0;
|
||||
static unsigned char g_low_probe_active = 0;
|
||||
static unsigned char g_low_power_warned = 0;
|
||||
static unsigned short g_vbat_vol_last = 0;
|
||||
static bat_level_t g_bat_level_last = BAT_LEVEL_100;
|
||||
|
||||
// ========== 新增:电压历史记录环形缓冲区 ==========
|
||||
static unsigned short g_voltage_history[VOLTAGE_HISTORY_SIZE] = {0};
|
||||
static unsigned char g_history_index = 0;
|
||||
static unsigned char g_history_count = 0; // 已记录的有效点数
|
||||
static int g_charging_state = 0; // 当前判定的充电状态:1-充电, 0-未充电
|
||||
static unsigned char g_charging_stable_cnt = 0; // 状态稳定计数
|
||||
|
||||
bat_level_t calc_battery_level(uint16_t voltage_mv)
|
||||
{
|
||||
@@ -72,64 +71,101 @@ bat_level_t calc_battery_level(uint16_t voltage_mv)
|
||||
else return BAT_LEVEL_0;
|
||||
}
|
||||
|
||||
bat_level_t battery_get_level(void)
|
||||
{
|
||||
return g_bat_level_valid ? g_bat_level_last : calc_battery_level(g_vbat_vol);
|
||||
}
|
||||
|
||||
static bat_level_t limit_level_jump(bat_level_t raw_level)
|
||||
{
|
||||
bat_level_t result = g_bat_level_last;
|
||||
|
||||
if (!g_bat_level_valid) {
|
||||
g_bat_level_valid = 1;
|
||||
g_bat_level_last = raw_level;
|
||||
printf("[BAT] first level initialized: %d\r\n", raw_level);
|
||||
return raw_level;
|
||||
}
|
||||
|
||||
if (raw_level > g_bat_level_last) {
|
||||
result = (raw_level >= g_bat_level_last + BAT_LEVEL_10)
|
||||
? (bat_level_t)(g_bat_level_last + BAT_LEVEL_10)
|
||||
: raw_level;
|
||||
} else if (raw_level < g_bat_level_last) {
|
||||
result = (raw_level <= g_bat_level_last - BAT_LEVEL_10)
|
||||
? (bat_level_t)(g_bat_level_last - BAT_LEVEL_10)
|
||||
: raw_level;
|
||||
}
|
||||
|
||||
printf("[BAT] level update: last=%d raw=%d result=%d\r\n",
|
||||
g_bat_level_last, raw_level, result);
|
||||
g_bat_level_last = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void battery_check_critical_voltage(unsigned short voltage_mv)
|
||||
{
|
||||
if (voltage_mv < BAT_LOW_WARN_MV) {
|
||||
g_low_probe_active = 1;
|
||||
} else if (voltage_mv >= BAT_LOW_PROBE_EXIT_MV) {
|
||||
g_low_probe_active = 0;
|
||||
}
|
||||
|
||||
if (voltage_mv < BAT_SHUTDOWN_MV) {
|
||||
if (g_low_voltage_count < BAT_SHUTDOWN_CONFIRM_COUNT) {
|
||||
g_low_voltage_count++;
|
||||
}
|
||||
|
||||
printf("[BAT] critical voltage:%d, confirm:%d/%d\r\n",
|
||||
voltage_mv, g_low_voltage_count, BAT_SHUTDOWN_CONFIRM_COUNT);
|
||||
|
||||
if (g_low_voltage_count >= BAT_SHUTDOWN_CONFIRM_COUNT) {
|
||||
printf("[BAT] critical voltage confirmed, poweroff now\r\n");
|
||||
app_play_tips_file(15);
|
||||
ql_rtos_task_sleep_s(5);
|
||||
ql_power_down(1);
|
||||
}
|
||||
} else {
|
||||
g_low_voltage_count = 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 基于10分钟电压历史判断充放电状态
|
||||
* @return 1-充电中, 0-未充电/放电
|
||||
*/
|
||||
static int detect_charging_by_trend(void)
|
||||
{
|
||||
if (g_history_count < VOLTAGE_HISTORY_SIZE) {
|
||||
printf("[BAT] history not full (%d/%d), keep state:%d\r\n",
|
||||
g_history_count, VOLTAGE_HISTORY_SIZE, g_charging_state);
|
||||
return g_charging_state;
|
||||
}
|
||||
|
||||
unsigned char old_index = (g_history_index + 1) % VOLTAGE_HISTORY_SIZE;
|
||||
unsigned short vbat_now = g_voltage_history[(g_history_index + VOLTAGE_HISTORY_SIZE - 1) % VOLTAGE_HISTORY_SIZE];
|
||||
unsigned short vbat_10min_ago = g_voltage_history[old_index];
|
||||
|
||||
short delta = (short)vbat_now - (short)vbat_10min_ago;
|
||||
|
||||
printf("[BAT] trend: now=%d, 10min_ago=%d, delta=%d\r\n",
|
||||
vbat_now, vbat_10min_ago, delta);
|
||||
|
||||
int new_state = g_charging_state;
|
||||
|
||||
if (delta > VOLTAGE_RISE_THRESHOLD) {
|
||||
new_state = 1;
|
||||
printf("[BAT] trend: CHARGING detected (rise %dmV)\r\n", delta);
|
||||
}
|
||||
else if (delta < -VOLTAGE_DROP_THRESHOLD) {
|
||||
new_state = 0;
|
||||
printf("[BAT] trend: DISCHARGING detected (drop %dmV)\r\n", -delta);
|
||||
}
|
||||
else {
|
||||
printf("[BAT] trend: STABLE (delta %dmV), keep state:%d\r\n", delta, g_charging_state);
|
||||
}
|
||||
|
||||
if (new_state == g_charging_state) {
|
||||
g_charging_stable_cnt = 0;
|
||||
} else {
|
||||
g_charging_stable_cnt++;
|
||||
if (g_charging_stable_cnt < 3) {
|
||||
printf("[BAT] state change pending: %d -> %d (%d/3)\r\n",
|
||||
g_charging_state, new_state, g_charging_stable_cnt);
|
||||
return g_charging_state;
|
||||
}
|
||||
g_charging_stable_cnt = 0;
|
||||
printf("[BAT] state changed: %d -> %d (confirmed)\r\n", g_charging_state, new_state);
|
||||
}
|
||||
|
||||
return new_state;
|
||||
}
|
||||
|
||||
static void update_voltage_history(unsigned short voltage)
|
||||
{
|
||||
g_voltage_history[g_history_index] = voltage;
|
||||
g_history_index = (g_history_index + 1) % VOLTAGE_HISTORY_SIZE;
|
||||
if (g_history_count < VOLTAGE_HISTORY_SIZE) {
|
||||
g_history_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_charging(void)
|
||||
{
|
||||
// 如有硬件检测引脚,可优先使用
|
||||
return detect_charging_by_trend();
|
||||
}
|
||||
|
||||
static bat_level_t limit_level_jump(bat_level_t raw_level, bat_level_t last_level, int charging)
|
||||
{
|
||||
bat_level_t result = last_level;
|
||||
|
||||
if (charging) {
|
||||
if (raw_level > last_level) {
|
||||
if (raw_level - last_level == 1) {
|
||||
result = raw_level;
|
||||
printf("[BAT] level up (charging): %d -> %d\r\n", last_level, raw_level);
|
||||
} else if (raw_level - last_level > 1) {
|
||||
result = (bat_level_t)(last_level + 1);
|
||||
printf("[BAT] level up limited (charging): %d -> %d (raw:%d)\r\n",
|
||||
last_level, result, raw_level);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (raw_level < last_level) {
|
||||
if (last_level - raw_level == 1) {
|
||||
result = raw_level;
|
||||
printf("[BAT] level down (discharging): %d -> %d\r\n", last_level, raw_level);
|
||||
} else if (last_level - raw_level > 1) {
|
||||
result = (bat_level_t)(last_level - 1);
|
||||
printf("[BAT] level down limited (discharging): %d -> %d (raw:%d)\r\n",
|
||||
last_level, result, raw_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 修改后的 battery_check:分阶段采样,采样窗口为6次 */
|
||||
void battery_check(void)
|
||||
@@ -138,17 +174,15 @@ void battery_check(void)
|
||||
static unsigned char sample_idx = 0; // 当前已采集样本数 (0~5)
|
||||
static unsigned short sample_buf[6] = {0}; // 本地缓冲(也可用全局 vbat_sample,此处直接用局部)
|
||||
|
||||
call_count++;
|
||||
call_count++;
|
||||
|
||||
/* 判断本次是否执行ADC采样 */
|
||||
unsigned char do_sample = 0;
|
||||
if (call_count <= 24) { // 开机2分钟内 (24次*5s=120s)
|
||||
do_sample = 1;
|
||||
} else if (g_low_probe_active) {
|
||||
do_sample = 1; // 低电风险时每5秒快速确认
|
||||
} else {
|
||||
// 2分钟后,每6次调用采样一次 (6*5s=30秒)
|
||||
if (call_count % BAT_NORMAL_SAMPLE_CALLS == 0) {
|
||||
if (call_count <= 24) { // 开机2分钟内 (24次*5s=120s)
|
||||
do_sample = 1;
|
||||
} else {
|
||||
// 2分钟后,每36次调用采样一次 (36*5s=180s=3分钟)
|
||||
if ((call_count - 25) % 36 == 0) {
|
||||
do_sample = 1;
|
||||
}
|
||||
}
|
||||
@@ -161,21 +195,9 @@ void battery_check(void)
|
||||
u16 vbat_vol = 0;
|
||||
ql_adc_onff(1);
|
||||
ql_rtos_task_sleep_ms(5);
|
||||
ql_adc_onff(0);
|
||||
vbat_vol = ql_get_battery_vol();
|
||||
|
||||
printf("[BAT][SAMPLE] voltage=%d, low_probe=%d\r\n",
|
||||
vbat_vol, g_low_probe_active);
|
||||
|
||||
if (vbat_vol < BAT_VALID_MIN_MV || vbat_vol > BAT_VALID_MAX_MV) {
|
||||
printf("[BAT] invalid voltage sample ignored:%d\r\n", vbat_vol);
|
||||
g_low_voltage_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
battery_check_critical_voltage(vbat_vol);
|
||||
|
||||
sample_buf[sample_idx] = vbat_vol;
|
||||
ql_adc_onff(0);
|
||||
vbat_vol = ql_get_battery_vol();
|
||||
sample_buf[sample_idx] = vbat_vol;
|
||||
sample_idx++;
|
||||
|
||||
/* 当收集满6个样本时进行平均处理 */
|
||||
@@ -193,29 +215,47 @@ void battery_check(void)
|
||||
vol_all = vol_all - min - max;
|
||||
vbat_vol = vol_all / 4; // 6个样本去掉最大最小,剩余4个平均
|
||||
|
||||
g_vbat_vol = vbat_vol;
|
||||
printf("vbat_vol:%d\r\n", g_vbat_vol);
|
||||
/* 更新电压历史(用于充电趋势判断) */
|
||||
update_voltage_history(vbat_vol);
|
||||
|
||||
/* 计算电量等级并限制跳变 */
|
||||
bat_level_t raw_level = calc_battery_level(g_vbat_vol);
|
||||
bat_level_t level = limit_level_jump(raw_level);
|
||||
|
||||
printf("[BAT] raw_level:%d, limited_level:%d\r\n",
|
||||
raw_level, level);
|
||||
/* 判断充电状态 */
|
||||
int charging = is_charging();
|
||||
g_charging_state = charging;
|
||||
|
||||
/* 非充电状态下,电压不允许上升 */
|
||||
if (!charging) {
|
||||
if (g_vbat_vol_last > 0 && vbat_vol > g_vbat_vol_last) {
|
||||
printf("[BAT] voltage rise blocked: %d -> %d (not charging)\r\n",
|
||||
g_vbat_vol_last, vbat_vol);
|
||||
vbat_vol = g_vbat_vol_last;
|
||||
}
|
||||
}
|
||||
|
||||
g_vbat_vol = vbat_vol;
|
||||
g_vbat_vol_last = vbat_vol;
|
||||
printf("vbat_vol:%d, charging:%d\r\n", g_vbat_vol, charging);
|
||||
|
||||
/* 计算电量等级并限制跳变 */
|
||||
bat_level_t raw_level = calc_battery_level(g_vbat_vol);
|
||||
bat_level_t level = limit_level_jump(raw_level, g_bat_level_last, charging);
|
||||
g_bat_level_last = level;
|
||||
|
||||
printf("[BAT] raw_level:%d, limited_level:%d, charging:%d\r\n",
|
||||
raw_level, level, charging);
|
||||
|
||||
if (battery_first_update_flag == 0) {
|
||||
battery_first_update_flag = 1;
|
||||
}
|
||||
|
||||
/* 极低电关机由原始电压独立处理,普通低电提示只播放一次 */
|
||||
if (level <= BAT_LEVEL_10) {
|
||||
if (!g_low_power_warned) {
|
||||
g_low_power_warned = 1;
|
||||
app_play_tips_file(14);
|
||||
}
|
||||
} else if (level >= BAT_LEVEL_20) {
|
||||
g_low_power_warned = 0;
|
||||
}
|
||||
/* 低电量处理 */
|
||||
if (level == BAT_LEVEL_0) {
|
||||
printf("poweroff now\r\n");
|
||||
app_play_tips_file(15);
|
||||
ql_rtos_task_sleep_s(5);
|
||||
ql_power_down(1);
|
||||
} else if (level <= BAT_LEVEL_10) {
|
||||
app_play_tips_file(14);
|
||||
}
|
||||
|
||||
sample_idx = 0; // 重置样本索引,准备下一轮采集
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "sockets.h"
|
||||
#include "netdb.h"
|
||||
#include "app_inc.h"
|
||||
#include "../../../../ql-config/config/board_features.h"
|
||||
|
||||
// 新增宏定义(原代码中未定义)
|
||||
#define GNSS_FAST_MODE_HOLD_TIME (5 * 60 * 1000) // FAST模式持续5分钟后切SLOW
|
||||
@@ -320,9 +321,14 @@ void quec_gnss_callback(char *data, int len)
|
||||
*===========================================================================*/
|
||||
void set_gnss_mode(gnss_mode_t mode)
|
||||
{
|
||||
#if !NFC_GNSS_BOARD_HAS_GNSS
|
||||
(void)mode;
|
||||
return; /* No GNSS task exists to leave FAST mode and allow sleep. */
|
||||
#else
|
||||
g_gnss_mode = mode;
|
||||
printf("[GNSSMode] Set to %s Mode\n",
|
||||
(mode == GNSS_MODE_FAST) ? "Fast (30s)" : "Slow (5min)");
|
||||
#endif
|
||||
}
|
||||
|
||||
gnss_mode_t get_gnss_mode(void)
|
||||
@@ -531,6 +537,11 @@ static void quec_gnss_test(void * argv)
|
||||
|
||||
void app_gnss_init(void)
|
||||
{
|
||||
#if !NFC_GNSS_BOARD_HAS_GNSS
|
||||
printf("[GNSS] Legacy board: positioning disabled\n");
|
||||
return;
|
||||
#else
|
||||
ql_task_t gnssTaskRef = NULL;
|
||||
ql_rtos_task_create(&gnssTaskRef, 1024*4, 100, "gnss_Task_Ref", quec_gnss_test, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -30,40 +30,9 @@ extern int fota_start;
|
||||
#define KEY_LONG_PRESS_1_5S 1500/5
|
||||
#define KEY_LONG_PRESS_3S 3000/5
|
||||
#define KEY_LONG_PRESS_5S 5000/5
|
||||
#define KEY_LONG_PRESS_10S 10000/5
|
||||
#define NFC_TX_CLICK_GAP 2000/5
|
||||
#define NFC_TX_CLICK_COUNT 5
|
||||
|
||||
static unsigned char key_down_event = 0;
|
||||
static unsigned char nfc_tx_click_count = 0;
|
||||
static u32 nfc_tx_last_click_time = 0;
|
||||
|
||||
extern void app_nfc_tx_mode_start(void);
|
||||
|
||||
static int key_mic_register_short_click(void)
|
||||
{
|
||||
u32 now = ql_rtos_get_systicks();
|
||||
|
||||
if (nfc_tx_click_count == 0 ||
|
||||
(u32)(now - nfc_tx_last_click_time) > NFC_TX_CLICK_GAP) {
|
||||
nfc_tx_click_count = 1;
|
||||
} else {
|
||||
nfc_tx_click_count++;
|
||||
}
|
||||
nfc_tx_last_click_time = now;
|
||||
|
||||
printf("NFC TX key clicks:%u/%u\r\n",
|
||||
nfc_tx_click_count, NFC_TX_CLICK_COUNT);
|
||||
if (nfc_tx_click_count >= NFC_TX_CLICK_COUNT) {
|
||||
nfc_tx_click_count = NFC_TX_CLICK_COUNT;
|
||||
app_nfc_tx_mode_start();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Keep the first click's existing voice action. Suppress clicks 2-4 so
|
||||
* a rapid NFC gesture does not repeatedly start/interrupt voice work. */
|
||||
return nfc_tx_click_count > 1;
|
||||
}
|
||||
#define KEY_LONG_PRESS_10S 10000/5
|
||||
|
||||
static unsigned char key_down_event = 0;
|
||||
|
||||
static void key_press_mic(void *args)
|
||||
{
|
||||
@@ -145,36 +114,26 @@ static void key_thread_entry(void *parameter)
|
||||
if(level!=0){ //up
|
||||
key_press_mic_status = 0;
|
||||
|
||||
if(ql_rtos_get_systicks() - key_press_mic_time > KEY_LONG_PRESS_3S){
|
||||
nfc_tx_click_count = 0;
|
||||
//app_play_tips_file(10);
|
||||
if(ql_rtos_get_systicks() - key_press_mic_time > KEY_LONG_PRESS_3S){
|
||||
//app_play_tips_file(10);
|
||||
// power_down_status = 1;
|
||||
ql_rtos_task_sleep_s(3);
|
||||
ql_power_down(1);
|
||||
}
|
||||
else if(ql_rtos_get_systicks() - key_press_mic_time > KEY_WAIT_TIME){
|
||||
int nfc_click_handled = key_mic_register_short_click();
|
||||
|
||||
//休眠唤醒其它线程
|
||||
if(app_get_app_sleep_flag()==0){
|
||||
app_set_app_operation(1);
|
||||
}
|
||||
|
||||
if (!nfc_click_handled) {
|
||||
int mic_pdp = app_get_data_call_net_status();
|
||||
int mic_mqtt = get_mqtt_connect_status();
|
||||
printf("[NET-GATE] mic pdp=%d mqtt=%d action=%s\r\n",
|
||||
mic_pdp, mic_mqtt,
|
||||
!mic_pdp ? "blocked_no_pdp" : (mic_mqtt != 1 ? "blocked_mqtt_offline" : "start_voice"));
|
||||
if(mic_pdp && mic_mqtt == 1){
|
||||
if(tts_play_interrupt_func()==-2){
|
||||
app_play_tips_file(2);
|
||||
}
|
||||
}else{
|
||||
app_play_tips_file(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ql_rtos_get_systicks() - key_press_mic_time > KEY_WAIT_TIME){
|
||||
//休眠唤醒其它线程
|
||||
if(app_get_app_sleep_flag()==0){
|
||||
app_set_app_operation(1);
|
||||
}
|
||||
|
||||
if(app_get_data_call_net_status()&&get_mqtt_connect_status()==1){
|
||||
if(tts_play_interrupt_func()==-2){
|
||||
app_play_tips_file(2);
|
||||
}
|
||||
}else{
|
||||
app_play_tips_file(2);
|
||||
}
|
||||
}
|
||||
|
||||
key_press_mic_time = ql_rtos_get_systicks();
|
||||
key_press_mic_counter = 0;
|
||||
@@ -184,9 +143,8 @@ static void key_thread_entry(void *parameter)
|
||||
key_press_mic_status = 1;//表示已经按下了
|
||||
}
|
||||
|
||||
if(ql_rtos_get_systicks() - key_press_mic_time > KEY_LONG_PRESS_3S && power_down_status==0){
|
||||
nfc_tx_click_count = 0;
|
||||
power_down_status = 1;
|
||||
if(ql_rtos_get_systicks() - key_press_mic_time > KEY_LONG_PRESS_3S && power_down_status==0){
|
||||
power_down_status = 1;
|
||||
app_play_tips_file(10);
|
||||
power_down_status = 2;
|
||||
//ql_rtos_task_sleep_s(3);
|
||||
|
||||
@@ -36,7 +36,7 @@ const char *mp3_file[] ={
|
||||
"ai_active_zh.mp3",//18
|
||||
};
|
||||
|
||||
//http://banban.api.talkingq.com:8080/assets/roles/banban/tts_error.mp3
|
||||
//http://175.24.73.253/assets/roles/zhuli/zh/tts_error.mp3
|
||||
|
||||
//#define PCM_FILE "http://47.113.99.138:7050/output_16k.pcm"
|
||||
|
||||
@@ -194,7 +194,7 @@ void app_file_check_download(void)
|
||||
if(ql_access(filename_buff, 0)!=0){
|
||||
printf("downing mp3 file %d,%s\r\n", index,filename_buff);
|
||||
down_loop:
|
||||
sprintf(url_buff, "http://banban.api.talkingq.com:8080/assets/roles/banban/%s", mp3_file[index]);
|
||||
sprintf(url_buff, "http://101.35.224.118:8080/assets/roles/banban/%s", mp3_file[index]);
|
||||
|
||||
ret = http_file_download(url_buff);
|
||||
if(ret!=0&&cnt++<3) goto down_loop;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef TALKINGQ_MQTT_CONNECT_DIAG_H
|
||||
#define TALKINGQ_MQTT_CONNECT_DIAG_H
|
||||
|
||||
#include "MQTTClient.h"
|
||||
|
||||
/* Called by the single MQTT connection worker, before MQTTStartTask(). */
|
||||
int talkingq_mqtt_connect_diagnosed(MQTTClient *client, MQTTPacket_connectData *options);
|
||||
|
||||
#endif
|
||||
@@ -280,11 +280,10 @@ u32 local_mp3_data_fetch(const char *uri, uint8_t *buffer, u32 length)
|
||||
|
||||
size_t url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length)
|
||||
{
|
||||
int resp_status = -1;
|
||||
uint32_t nw = length;
|
||||
uint32_t page_pos = 0;
|
||||
uint32_t content_length = 0;
|
||||
int error_cnt = 0;
|
||||
int resp_status = -1;
|
||||
uint32_t nw = length;
|
||||
uint32_t page_pos = 0;
|
||||
int error_cnt = 0;
|
||||
|
||||
//printf("fd_fetch start\n");
|
||||
session = webclient_session_create(1024);
|
||||
@@ -307,12 +306,11 @@ size_t url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length)
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (session->content_length == 0)
|
||||
{
|
||||
printf("Request file size is 0!");
|
||||
goto __exit;
|
||||
}
|
||||
content_length = session->content_length;
|
||||
if (session->content_length == 0)
|
||||
{
|
||||
printf("Request file size is 0!");
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
printf("start read\n");
|
||||
if(total_length < session->content_length)
|
||||
@@ -327,7 +325,7 @@ size_t url_mp3_data_fetch(const char *uri, uint8_t *buffer, size_t length)
|
||||
page_pos = 0;
|
||||
do
|
||||
{
|
||||
if(vad_audio_is_recording() || tts_playing_ret()==2) {
|
||||
if(vad_audio_is_recording()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -363,7 +361,7 @@ __exit:
|
||||
session = NULL;
|
||||
}
|
||||
|
||||
if(total_length<content_length){
|
||||
if(total_length<session->content_length){
|
||||
total_length = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
=================================================================*/
|
||||
|
||||
#include "MQTTClient.h"
|
||||
#include "mqtt_connect_diag.h"
|
||||
#include "ql_data_call.h"
|
||||
#include "ql_application.h"
|
||||
#include "sockets.h"
|
||||
@@ -26,7 +25,7 @@
|
||||
|
||||
#define PROFILE_IDX 1
|
||||
//#define DEFAULT_SERVER_DOMAIN "175.24.73.253"
|
||||
#define DEFAULT_SERVER_DOMAIN "banban.api.talkingq.com"
|
||||
#define DEFAULT_SERVER_DOMAIN "101.35.224.118"
|
||||
#define DEFAULT_SERVER_CLIENT_USER "talkingq"
|
||||
#define DEFAULT_SERVER_CLIENT_PASS "talkingq@2026"
|
||||
#define DEFAULT_SERVER_CLIENT_ID "9376265c059e474e92c2e46208ae1dc5"
|
||||
@@ -38,14 +37,6 @@
|
||||
|
||||
|
||||
#define DEFAULT_MQTT_EVENT_TIME 5000
|
||||
|
||||
#define OTA_STATE_FILE "U:/ota_state.bin"
|
||||
#define OTA_STATE_TMP_FILE "U:/ota_state.tmp"
|
||||
#define OTA_STATE_MAGIC 0x4F544131U
|
||||
#define OTA_STATE_FORMAT_VERSION 1U
|
||||
#define OTA_PROMPT_PLAY_TIMEOUT_MS 10000
|
||||
#define OTA_PROMPT_POLL_MS 50
|
||||
#define OTA_ALLOWED_URL_PREFIX "http://101.35.224.118:8080/assets/firmware/"
|
||||
|
||||
static ql_timer_t mqtt_event_task_timer = NULL;
|
||||
|
||||
@@ -80,8 +71,7 @@ int g_nfc_bind_status = 0;
|
||||
|
||||
extern u8 g_cur_volume;
|
||||
extern u8 g_pre_volume;
|
||||
extern unsigned short g_vbat_vol;
|
||||
extern bat_level_t battery_get_level(void);
|
||||
extern unsigned short g_vbat_vol;
|
||||
extern sleep_config_t g_sleep_cfg;
|
||||
extern uint8_t g_sleep_config_valid;
|
||||
|
||||
@@ -102,23 +92,8 @@ int app_mqtt_connect_sem(char flag);
|
||||
#define MQTT_PAYLOAD_SIZE 2048
|
||||
static char payload[MQTT_PAYLOAD_SIZE] = {0};
|
||||
|
||||
extern int fota_start;
|
||||
int app_play_ota_tips_file(void);
|
||||
int xiaoice_get_play_counter(void);
|
||||
extern int fota_start;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint16_t format_version;
|
||||
uint16_t reserved;
|
||||
char previous_version[16];
|
||||
char target_version[16];
|
||||
uint32_t checksum;
|
||||
} ota_state_t;
|
||||
|
||||
int app_mqtt_publish(char *buf, int len, int type);
|
||||
void build_register(void);
|
||||
static void ota_report_pending_success(void);
|
||||
|
||||
enum{
|
||||
MQTT_WORK_CONNECT,
|
||||
MQTT_WORK_RECV,
|
||||
@@ -131,177 +106,6 @@ int get_mqtt_connect_status(void)
|
||||
status = mqtt_connect;
|
||||
MQTT_CONN_UNLOCK();
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t ota_state_checksum(const ota_state_t *state)
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *)state;
|
||||
uint32_t hash = 2166136261U;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(*state) - sizeof(state->checksum); i++) {
|
||||
hash ^= data[i];
|
||||
hash *= 16777619U;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int ota_state_save(const char *previous_version, const char *target_version)
|
||||
{
|
||||
ota_state_t state;
|
||||
QFILE *fp;
|
||||
int ret;
|
||||
|
||||
if (previous_version == NULL || target_version == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.magic = OTA_STATE_MAGIC;
|
||||
state.format_version = OTA_STATE_FORMAT_VERSION;
|
||||
strncpy(state.previous_version, previous_version, sizeof(state.previous_version) - 1);
|
||||
strncpy(state.target_version, target_version, sizeof(state.target_version) - 1);
|
||||
state.checksum = ota_state_checksum(&state);
|
||||
|
||||
fp = ql_fopen(OTA_STATE_TMP_FILE, "wb+");
|
||||
if (fp == NULL) {
|
||||
RLOGE("OTA state open failed: %s", OTA_STATE_TMP_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ql_fwrite(&state, sizeof(state), 1, fp);
|
||||
if (ret != (int)sizeof(state)) {
|
||||
RLOGE("OTA state write failed: %d", ret);
|
||||
ql_fclose(fp);
|
||||
ql_remove(OTA_STATE_TMP_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ql_fsync(fp);
|
||||
ql_fclose(fp);
|
||||
if (ret != 0) {
|
||||
RLOGE("OTA state sync failed: %d", ret);
|
||||
ql_remove(OTA_STATE_TMP_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ql_rename(OTA_STATE_TMP_FILE, OTA_STATE_FILE);
|
||||
if (ret != 0) {
|
||||
RLOGE("OTA state rename failed: %d", ret);
|
||||
ql_remove(OTA_STATE_TMP_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ota_state_load(ota_state_t *state)
|
||||
{
|
||||
QFILE *fp;
|
||||
int ret;
|
||||
uint32_t checksum;
|
||||
|
||||
if (state == NULL || ql_access(OTA_STATE_FILE, 0) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = ql_fopen(OTA_STATE_FILE, "rb");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
ret = ql_fread(state, sizeof(*state), 1, fp);
|
||||
ql_fclose(fp);
|
||||
if (ret != (int)sizeof(*state)) {
|
||||
RLOGE("OTA state read failed: %d", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
checksum = state->checksum;
|
||||
state->checksum = 0;
|
||||
if (state->magic != OTA_STATE_MAGIC ||
|
||||
state->format_version != OTA_STATE_FORMAT_VERSION ||
|
||||
checksum != ota_state_checksum(state)) {
|
||||
RLOGE("OTA state validation failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->checksum = checksum;
|
||||
state->previous_version[sizeof(state->previous_version) - 1] = '\0';
|
||||
state->target_version[sizeof(state->target_version) - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ota_state_clear(void)
|
||||
{
|
||||
ql_remove(OTA_STATE_TMP_FILE);
|
||||
if (ql_access(OTA_STATE_FILE, 0) == 0) {
|
||||
ql_remove(OTA_STATE_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
static int ota_parse_version(const char *version, unsigned int parts[3])
|
||||
{
|
||||
char tail;
|
||||
|
||||
if (version == NULL || version[0] == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sscanf(version, "%u.%u.%u%c",
|
||||
&parts[0], &parts[1], &parts[2], &tail) != 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ota_compare_version(const char *left, const char *right, int *result)
|
||||
{
|
||||
unsigned int left_parts[3];
|
||||
unsigned int right_parts[3];
|
||||
int i;
|
||||
|
||||
if (result == NULL ||
|
||||
ota_parse_version(left, left_parts) != 0 ||
|
||||
ota_parse_version(right, right_parts) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*result = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (left_parts[i] > right_parts[i]) {
|
||||
*result = 1;
|
||||
break;
|
||||
}
|
||||
if (left_parts[i] < right_parts[i]) {
|
||||
*result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ota_url_is_allowed(const char *url)
|
||||
{
|
||||
size_t prefix_len = strlen(OTA_ALLOWED_URL_PREFIX);
|
||||
|
||||
if (url == NULL || url[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncmp(url, OTA_ALLOWED_URL_PREFIX, prefix_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (url[prefix_len] == '\0' || strstr(url + prefix_len, "..") != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strstr(url + prefix_len, ".bin") != NULL;
|
||||
}
|
||||
/* ==================== 解析函数 ==================== */
|
||||
|
||||
@@ -357,11 +161,6 @@ int parse_command(const char *json_str, command_t *cmd)
|
||||
|
||||
// 解析 params
|
||||
cJSON *params = cJSON_GetObjectItem(root, "params");
|
||||
if (cmd->type == MSG_OTA_UPGRADE && params == NULL) {
|
||||
RLOGE("OTA command has no params");
|
||||
cJSON_Delete(root);
|
||||
return -1;
|
||||
}
|
||||
if (params != NULL) {
|
||||
switch (cmd->type) {
|
||||
case MSG_GPS_QUERY:
|
||||
@@ -380,27 +179,35 @@ int parse_command(const char *json_str, command_t *cmd)
|
||||
}
|
||||
|
||||
case MSG_OTA_UPGRADE: {
|
||||
/*判断当前的固件版本与下发的是否一致,不一致,则触发升级*/
|
||||
printf("MSG_OTA_UPGRADE\r\n");
|
||||
cJSON *url = cJSON_GetObjectItem(params, "url");
|
||||
cJSON *version = cJSON_GetObjectItem(params, "version");
|
||||
|
||||
if (!cJSON_IsString(url) || url->valuestring == NULL ||
|
||||
!cJSON_IsString(version) || version->valuestring == NULL ||
|
||||
url->valuestring[0] == '\0' || version->valuestring[0] == '\0' ||
|
||||
strlen(url->valuestring) >= sizeof(cmd->params.ota.url) ||
|
||||
strlen(version->valuestring) >= sizeof(cmd->params.ota.version)) {
|
||||
RLOGE("invalid OTA command parameters");
|
||||
cJSON_Delete(root);
|
||||
return -1;
|
||||
if (cJSON_IsString(url)) {
|
||||
strncpy(cmd->params.ota.url, url->valuestring,
|
||||
sizeof(cmd->params.ota.url) - 1);
|
||||
}
|
||||
if (cJSON_IsString(version)) {
|
||||
strncpy(cmd->params.ota.version, version->valuestring,
|
||||
sizeof(cmd->params.ota.version) - 1);
|
||||
}
|
||||
|
||||
strncpy(cmd->params.ota.url, url->valuestring,
|
||||
sizeof(cmd->params.ota.url) - 1);
|
||||
strncpy(cmd->params.ota.version, version->valuestring,
|
||||
sizeof(cmd->params.ota.version) - 1);
|
||||
|
||||
printf("ota.url:%s\r\n", cmd->params.ota.url);
|
||||
printf("ota.version:%s\r\n", cmd->params.ota.version);
|
||||
|
||||
app_play_tips_file(3);
|
||||
|
||||
fota_start = 1;
|
||||
|
||||
ql_rtos_task_sleep_ms(6000);
|
||||
|
||||
app_file_check_del();
|
||||
|
||||
ql_fullfota_app_start(cmd->params.ota.url);
|
||||
/*
|
||||
{'msg_id': '003', 'params': {'url': 'http://175.24.73.253/assets/firmware/20260425203347-fota.bin', 'version': '2.0.1'}}
|
||||
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -560,7 +367,7 @@ int parse_event_response(const char *json_str, response_t *resp)
|
||||
}
|
||||
else {
|
||||
// ===== 原有逻辑: NFC事件 - 动态查找 url_x 字段 =====
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "url_%d", i);
|
||||
cJSON *url = cJSON_GetObjectItem(params, key);
|
||||
@@ -583,12 +390,11 @@ int parse_event_response(const char *json_str, response_t *resp)
|
||||
|
||||
|
||||
/* [FIX] 安全处理 topic 名称,避免越界访问 */
|
||||
static void messageArrived(MessageData* data)
|
||||
{
|
||||
int ret;
|
||||
int payload_log_len;
|
||||
uint32 queue_count = 0;
|
||||
app_work_item *work_item;
|
||||
static void messageArrived(MessageData* data)
|
||||
{
|
||||
int ret;
|
||||
uint32 queue_count = 0;
|
||||
app_work_item *work_item;
|
||||
|
||||
// 安全获取 topic 字符串(复制到本地缓冲区)
|
||||
char topic_buf[128] = {0};
|
||||
@@ -596,16 +402,10 @@ static void messageArrived(MessageData* data)
|
||||
if (topic_len >= sizeof(topic_buf)) {
|
||||
topic_len = sizeof(topic_buf) - 1;
|
||||
}
|
||||
memcpy(topic_buf, data->topicName->lenstring.data, topic_len);
|
||||
topic_buf[topic_len] = '\0';
|
||||
|
||||
payload_log_len = data->message->payloadlen > 512 ? 512 : (int)data->message->payloadlen;
|
||||
printf("[MQTT-RX] topic=%s len=%d payload=%.*s%s\r\n",
|
||||
topic_buf, (int)data->message->payloadlen, payload_log_len,
|
||||
(const char *)data->message->payload,
|
||||
data->message->payloadlen > (unsigned int)payload_log_len ? "..." : "");
|
||||
|
||||
// 比较 topic 类型
|
||||
memcpy(topic_buf, data->topicName->lenstring.data, topic_len);
|
||||
topic_buf[topic_len] = '\0';
|
||||
|
||||
// 比较 topic 类型
|
||||
int is_command = (strstr(topic_buf, "command") != NULL);
|
||||
int is_event_resp = (strstr(topic_buf, "event_resp") != NULL) || (strstr(topic_buf, imei_buf) != NULL);
|
||||
|
||||
@@ -648,8 +448,6 @@ int app_mqtt_connect()
|
||||
|
||||
MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
|
||||
int mqtt_server_port=DEFAULT_SERVER_PORT;
|
||||
unsigned int tcp_started;
|
||||
int tcp_errno;
|
||||
|
||||
MQTT_CONN_LOCK();
|
||||
if(mqtt_connect==1){
|
||||
@@ -664,14 +462,7 @@ int app_mqtt_connect()
|
||||
NetworkInit(&network, NULL, PROFILE_IDX);
|
||||
MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));
|
||||
char* address = DEFAULT_SERVER_DOMAIN;
|
||||
printf("[MQTT-DIAG] TCP begin host=%s port=%d pdp=%d\r\n",
|
||||
address, mqtt_server_port, app_get_data_call_net_status());
|
||||
tcp_started = ql_rtos_get_systicks();
|
||||
rc = NetworkConnect(&network, address, mqtt_server_port);
|
||||
tcp_errno = rc != 0 ? ql_get_soc_errno() : 0;
|
||||
printf("[MQTT-DIAG] TCP end rc=%d socket=%d elapsed_ms=%u errno_snapshot=%d\r\n",
|
||||
rc, network.my_socket, (unsigned int)(ql_rtos_get_systicks() - tcp_started) * 5U, tcp_errno);
|
||||
if (rc != 0)
|
||||
if ((rc = NetworkConnect(&network, address, mqtt_server_port)) != 0)
|
||||
{
|
||||
RLOGE("Return code from network connect is %d", rc);
|
||||
MQTTClientDeinit(&client);
|
||||
@@ -695,8 +486,7 @@ int app_mqtt_connect()
|
||||
connectData.cleansession = 0;
|
||||
connectData.keepAliveInterval = 60;
|
||||
|
||||
printf("[MQTT-DIAG] saved_device_id=%d (registration needed when 0)\r\n", ret == 0);
|
||||
if ((rc = talkingq_mqtt_connect_diagnosed(&client, &connectData)) != 0)
|
||||
if ((rc = MQTTConnect(&client, &connectData)) != 0)
|
||||
{
|
||||
RLOGE("Return code from MQTT connect is %d", rc);
|
||||
NetworkDisconnect(&network);
|
||||
@@ -747,8 +537,6 @@ int app_mqtt_connect()
|
||||
|
||||
if(ret!=0){
|
||||
build_register();
|
||||
} else {
|
||||
ota_report_pending_success();
|
||||
}
|
||||
|
||||
RLOGD("========== app_mqtt_connect okay ==========");
|
||||
@@ -819,33 +607,15 @@ sub_exit:
|
||||
//0:代表respone command
|
||||
//1:代表send event
|
||||
/* [FIX] 将连接状态检查和发送放在同一锁保护区内,避免竞争 */
|
||||
int app_mqtt_publish(char *buf, int len, int type)
|
||||
{
|
||||
static uint32_t mqtt_pub_cnt_err=0;
|
||||
int rc = -1;
|
||||
int payload_log_len;
|
||||
MQTTMessage message;
|
||||
int cnt = 0;
|
||||
static uint8_t publish_error = 0;
|
||||
const char *publish_topic = NULL;
|
||||
|
||||
if (type == 0) {
|
||||
publish_topic = respone_topicName;
|
||||
} else if (type == 1) {
|
||||
publish_topic = event_topicName;
|
||||
} else if (type == 2) {
|
||||
publish_topic = event_register;
|
||||
} else {
|
||||
RLOGE("invalid MQTT publish type=%d", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload_log_len = len > 512 ? 512 : len;
|
||||
printf("[MQTT-TX] topic=%s len=%d payload=%.*s%s\r\n",
|
||||
publish_topic, len, payload_log_len, buf,
|
||||
len > payload_log_len ? "..." : "");
|
||||
|
||||
// 锁保护:检查连接状态并发送
|
||||
int app_mqtt_publish(char *buf, int len, int type)
|
||||
{
|
||||
static uint32_t mqtt_pub_cnt_err=0;
|
||||
int rc = -1;
|
||||
MQTTMessage message;
|
||||
int cnt = 0;
|
||||
static uint8_t publish_error = 0;
|
||||
|
||||
// 锁保护:检查连接状态并发送
|
||||
int lock_ret = ql_rtos_mutex_lock(g_mqtt_public_mutex, 5000);
|
||||
if (lock_ret != 0) {
|
||||
RLOGE("MQTT mutex lock timeout");
|
||||
@@ -862,13 +632,18 @@ int app_mqtt_publish(char *buf, int len, int type)
|
||||
message.payload = buf;
|
||||
message.payloadlen = len;
|
||||
|
||||
while (cnt++ < 3) {
|
||||
u32 start = ql_rtos_get_systicks();
|
||||
rc = MQTTPublish(&client, publish_topic, &message);
|
||||
|
||||
RLOGD("Pulish time:%d len: %d\n", (ql_rtos_get_systicks()-start)*5, len);
|
||||
printf("[MQTT-TX] topic=%s attempt=%d rc=%d\r\n", publish_topic, cnt, rc);
|
||||
if (rc != 0) {
|
||||
while (cnt++ < 3) {
|
||||
u32 start = ql_rtos_get_systicks();
|
||||
if (type == 0) {
|
||||
rc = MQTTPublish(&client, respone_topicName, &message);
|
||||
} else if (type == 1) {
|
||||
rc = MQTTPublish(&client, event_topicName, &message);
|
||||
} else if (type == 2) {
|
||||
rc = MQTTPublish(&client, event_register, &message);
|
||||
}
|
||||
|
||||
RLOGD("Pulish time:%d len: %d\n", (ql_rtos_get_systicks()-start)*5, len);
|
||||
if (rc != 0) {
|
||||
mqtt_pub_cnt_err++;
|
||||
mqtt_public_error = 1;
|
||||
RLOGE("ERROR Return code from MQTT publish is %d, cnt %d", rc, mqtt_pub_cnt_err);
|
||||
@@ -1183,18 +958,12 @@ char *build_wake_response(const char *msg_id, const char *switch_state)
|
||||
/**
|
||||
* @brief 构建OTA接受响应
|
||||
*/
|
||||
char *build_ota_accepted(const char *msg_id, const char *cur_ver, const char *tgt_ver)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
|
||||
if (root == NULL || data == NULL) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(root, "msg_id", msg_id);
|
||||
char *build_ota_accepted(const char *msg_id, const char *cur_ver, const char *tgt_ver)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "msg_id", msg_id);
|
||||
cJSON_AddStringToObject(root, "status", "accepted");
|
||||
cJSON_AddStringToObject(data, "current_version", cur_ver);
|
||||
cJSON_AddStringToObject(data, "target_version", tgt_ver);
|
||||
@@ -1206,66 +975,6 @@ char *build_ota_accepted(const char *msg_id, const char *cur_ver, const char *tg
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构建OTA升级成功响应
|
||||
*/
|
||||
static char *build_ota_success(const char *new_ver)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
|
||||
if (root == NULL || data == NULL) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(root, "msg_id", "003");
|
||||
cJSON_AddStringToObject(root, "status", "success");
|
||||
cJSON_AddStringToObject(data, "new_version", new_ver);
|
||||
cJSON_AddItemToObject(root, "data", data);
|
||||
|
||||
char *json_str = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
return json_str;
|
||||
}
|
||||
|
||||
static void ota_report_pending_success(void)
|
||||
{
|
||||
ota_state_t state;
|
||||
char current_version[64] = {0};
|
||||
char *resp;
|
||||
int compare_result;
|
||||
int ret;
|
||||
|
||||
if (ota_state_load(&state) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hal_os_get_build_ver(current_version);
|
||||
if (ota_compare_version(current_version, state.target_version, &compare_result) != 0 ||
|
||||
compare_result != 0) {
|
||||
RLOGE("OTA pending state version mismatch: current=%s target=%s",
|
||||
current_version, state.target_version);
|
||||
return;
|
||||
}
|
||||
|
||||
resp = build_ota_success(current_version);
|
||||
if (resp == NULL) {
|
||||
RLOGE("build OTA success response failed");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = app_mqtt_publish(resp, strlen(resp), 0);
|
||||
free(resp);
|
||||
if (ret == 0) {
|
||||
printf("OTA success response published, version=%s\r\n", current_version);
|
||||
ota_state_clear();
|
||||
} else {
|
||||
RLOGE("OTA success response publish failed: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构建音量调节响应
|
||||
*/
|
||||
char *build_volume_response(const char *msg_id, int current_level, const char *time)
|
||||
@@ -1290,95 +999,6 @@ char *build_volume_response(const char *msg_id, int current_level, const char *t
|
||||
}
|
||||
|
||||
/* ==================== 业务处理函数 ==================== */
|
||||
|
||||
static void handle_ota_upgrade(command_t *cmd)
|
||||
{
|
||||
char current_version[64] = {0};
|
||||
char *resp;
|
||||
int compare_result;
|
||||
int ret;
|
||||
uint32_t prompt_start_time;
|
||||
|
||||
if (cmd == NULL || fota_start == 1) {
|
||||
RLOGE("OTA request ignored: invalid command or OTA already running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ota_url_is_allowed(cmd->params.ota.url)) {
|
||||
RLOGE("OTA URL is not allowed: %s", cmd->params.ota.url);
|
||||
return;
|
||||
}
|
||||
|
||||
hal_os_get_build_ver(current_version);
|
||||
if (ota_compare_version(current_version, cmd->params.ota.version, &compare_result) != 0) {
|
||||
RLOGE("invalid OTA version: current=%s target=%s",
|
||||
current_version, cmd->params.ota.version);
|
||||
return;
|
||||
}
|
||||
|
||||
if (compare_result > 0) {
|
||||
RLOGE("OTA downgrade rejected: current=%s target=%s",
|
||||
current_version, cmd->params.ota.version);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ota_state_save(current_version, cmd->params.ota.version) != 0) {
|
||||
RLOGE("OTA state save failed");
|
||||
return;
|
||||
}
|
||||
|
||||
resp = build_ota_accepted(cmd->msg_id,
|
||||
current_version,
|
||||
cmd->params.ota.version);
|
||||
if (resp == NULL) {
|
||||
RLOGE("build OTA accepted response failed");
|
||||
ota_state_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
ret = app_mqtt_publish(resp, strlen(resp), 0);
|
||||
free(resp);
|
||||
if (ret != 0) {
|
||||
RLOGE("OTA accepted response publish failed: %d", ret);
|
||||
ota_state_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (compare_result == 0) {
|
||||
printf("OTA target version is already installed: %s\r\n", current_version);
|
||||
ota_report_pending_success();
|
||||
return;
|
||||
}
|
||||
|
||||
fota_start = 1;
|
||||
ret = app_play_ota_tips_file();
|
||||
if (ret != 0) {
|
||||
RLOGE("OTA prompt could not start: %d", ret);
|
||||
} else {
|
||||
prompt_start_time = hal_os_up_time_ms();
|
||||
while (xiaoice_get_play_counter() > 0) {
|
||||
ql_rtos_task_sleep_ms(OTA_PROMPT_POLL_MS);
|
||||
if (hal_os_up_time_ms() - prompt_start_time > OTA_PROMPT_PLAY_TIMEOUT_MS) {
|
||||
RLOGE("OTA prompt playback timeout, counter=%d",
|
||||
xiaoice_get_play_counter());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (xiaoice_get_play_counter() == 0) {
|
||||
printf("OTA prompt playback completed\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Product requirement: keep deleting the configured audio files before OTA. */
|
||||
app_file_check_del();
|
||||
|
||||
ret = ql_fullfota_app_start(cmd->params.ota.url);
|
||||
if (ret != 0) {
|
||||
RLOGE("start OTA failed: %d", ret);
|
||||
fota_start = 0;
|
||||
ota_state_clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 命令分发处理
|
||||
@@ -1454,7 +1074,26 @@ void handle_command(command_t *cmd)
|
||||
}
|
||||
|
||||
case MSG_OTA_UPGRADE: {
|
||||
handle_ota_upgrade(cmd);
|
||||
// 立即响应accepted
|
||||
char *resp = build_ota_accepted(cmd->msg_id,
|
||||
hal_os_get_current_version(),
|
||||
cmd->params.ota.version);
|
||||
|
||||
/*while(get_mqtt_public_mutex()!=0){
|
||||
hal_os_task_sleep_ms(100);
|
||||
}*/
|
||||
/*
|
||||
添加删除所有本地音频动作
|
||||
*/
|
||||
app_mqtt_publish(resp, strlen(resp), 0);
|
||||
free(resp);
|
||||
|
||||
// 启动OTA任务
|
||||
fota_start = 1;
|
||||
|
||||
ql_rtos_task_sleep_ms(5000);
|
||||
|
||||
ql_fullfota_app_start(cmd->params.ota.url);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1600,34 +1239,23 @@ void handle_nfc_trigger(const char *uuid)
|
||||
free(json_str);
|
||||
}
|
||||
|
||||
void handle_nfc_bind(const char *uuid)
|
||||
{
|
||||
char iccid[QL_SIM_ICCID_LEN * 2 + 1] = {0};
|
||||
QL_SIM_ERROR_CODE sim_ret;
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "msg_id", "006");
|
||||
cJSON_AddStringToObject(root, "status", "success");
|
||||
|
||||
cJSON_AddStringToObject(data, "uuid", uuid);
|
||||
|
||||
sim_ret = ql_sim_get_iccid(iccid, sizeof(iccid));
|
||||
if (sim_ret == QL_SIM_SUCCESS && iccid[0] != '\0') {
|
||||
printf("NFC bind ICCID:%s\r\n", iccid);
|
||||
cJSON_AddStringToObject(data, "iccid", iccid);
|
||||
} else {
|
||||
printf("NFC bind get ICCID failed, ret:%d\r\n", sim_ret);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "data", data);
|
||||
|
||||
char *json_str = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
|
||||
app_mqtt_publish(json_str, strlen(json_str), 0);
|
||||
free(json_str);
|
||||
}
|
||||
void handle_nfc_bind(const char *uuid)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "msg_id", "006");
|
||||
cJSON_AddStringToObject(root, "status", "success");
|
||||
|
||||
cJSON_AddStringToObject(data, "uuid", uuid);
|
||||
cJSON_AddItemToObject(root, "data", data);
|
||||
|
||||
char *json_str = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
|
||||
app_mqtt_publish(json_str, strlen(json_str), 0);
|
||||
free(json_str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1807,7 +1435,7 @@ static void app_mqtt_task_event_thread(void *arvg)
|
||||
app_heartbeat_time_record = hal_os_up_time_ms();
|
||||
|
||||
// 采集设备信息
|
||||
info.power = battery_get_level(); // 使用与设备内部一致的最终电量档位
|
||||
info.power = calc_battery_level(g_vbat_vol); // 10,20,30...100
|
||||
|
||||
info.voice = g_cur_volume; // 40,60,80,100
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mqtt_connect_diag.h"
|
||||
#include "sockets.h"
|
||||
|
||||
/* Only installed during synchronous CONNECT. Never dump the CONNECT payload:
|
||||
* it contains credentials. Keep only the first four incoming CONNACK bytes. */
|
||||
static struct {
|
||||
int (*read)(Network *, unsigned char *, int, int);
|
||||
int (*write)(Network *, unsigned char *, int, int);
|
||||
unsigned int read_calls, write_calls, received, sent;
|
||||
unsigned char prefix[4];
|
||||
unsigned int prefix_size;
|
||||
} mqtt_diag;
|
||||
|
||||
static unsigned int mqtt_diag_elapsed(unsigned int start)
|
||||
{
|
||||
return (unsigned int)(ql_rtos_get_systicks() - start) * 5U;
|
||||
}
|
||||
|
||||
static int mqtt_diag_read(Network *network, unsigned char *buffer, int size, int timeout)
|
||||
{
|
||||
unsigned int start = ql_rtos_get_systicks();
|
||||
int result = mqtt_diag.read(network, buffer, size, timeout);
|
||||
int socket_error = result <= 0 ? ql_get_soc_errno() : 0;
|
||||
int i;
|
||||
++mqtt_diag.read_calls;
|
||||
if (result > 0) {
|
||||
mqtt_diag.received += (unsigned int)result;
|
||||
for (i = 0; i < result && i < size && mqtt_diag.prefix_size < sizeof(mqtt_diag.prefix); ++i)
|
||||
mqtt_diag.prefix[mqtt_diag.prefix_size++] = buffer[i];
|
||||
}
|
||||
printf("[MQTT-DIAG] read request=%d rc=%d timeout_ms=%d elapsed_ms=%u errno_snapshot=%d\r\n",
|
||||
size, result, timeout, mqtt_diag_elapsed(start), socket_error);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int mqtt_diag_write(Network *network, unsigned char *buffer, int size, int timeout)
|
||||
{
|
||||
unsigned int start = ql_rtos_get_systicks();
|
||||
int result = mqtt_diag.write(network, buffer, size, timeout);
|
||||
int socket_error = result <= 0 ? ql_get_soc_errno() : 0;
|
||||
++mqtt_diag.write_calls;
|
||||
if (result > 0) mqtt_diag.sent += (unsigned int)result;
|
||||
printf("[MQTT-DIAG] write request=%d rc=%d timeout_ms=%d elapsed_ms=%u errno_snapshot=%d\r\n",
|
||||
size, result, timeout, mqtt_diag_elapsed(start), socket_error);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *mqtt_diag_connack_reason(unsigned char code)
|
||||
{
|
||||
switch (code) {
|
||||
case 0: return "accepted";
|
||||
case 1: return "protocol_version_rejected";
|
||||
case 2: return "client_id_rejected";
|
||||
case 3: return "server_unavailable";
|
||||
case 4: return "bad_username_or_password";
|
||||
case 5: return "not_authorized";
|
||||
default: return "reserved_code";
|
||||
}
|
||||
}
|
||||
|
||||
int talkingq_mqtt_connect_diagnosed(MQTTClient *client, MQTTPacket_connectData *options)
|
||||
{
|
||||
Network *network = client->ipstack;
|
||||
MQTTConnackData connack = {255, 255};
|
||||
unsigned int start;
|
||||
int result;
|
||||
int valid_connack;
|
||||
memset(&mqtt_diag, 0, sizeof(mqtt_diag));
|
||||
mqtt_diag.read = network->mqttread;
|
||||
mqtt_diag.write = network->mqttwrite;
|
||||
if (!mqtt_diag.read || !mqtt_diag.write) {
|
||||
printf("[MQTT-DIAG] missing transport callbacks; calling SDK unchanged\r\n");
|
||||
return MQTTConnectWithResults(client, options, &connack);
|
||||
}
|
||||
printf("[MQTT-DIAG] CONNECT begin version=%u clean=%u keepalive_s=%u timeout_ms=%u already_connected=%d\r\n",
|
||||
(unsigned int)options->MQTTVersion, (unsigned int)options->cleansession,
|
||||
(unsigned int)options->keepAliveInterval, client->command_timeout_ms, client->isconnected);
|
||||
network->mqttread = mqtt_diag_read;
|
||||
network->mqttwrite = mqtt_diag_write;
|
||||
start = ql_rtos_get_systicks();
|
||||
result = MQTTConnectWithResults(client, options, &connack);
|
||||
network->mqttread = mqtt_diag.read;
|
||||
network->mqttwrite = mqtt_diag.write;
|
||||
printf("[MQTT-DIAG] CONNECT end rc=%d elapsed_ms=%u write_calls=%u tx_bytes=%u read_calls=%u rx_bytes=%u\r\n",
|
||||
result, mqtt_diag_elapsed(start), mqtt_diag.write_calls, mqtt_diag.sent,
|
||||
mqtt_diag.read_calls, mqtt_diag.received);
|
||||
valid_connack = mqtt_diag.prefix_size == 4 && mqtt_diag.prefix[0] == 0x20 &&
|
||||
mqtt_diag.prefix[1] == 2 && (mqtt_diag.prefix[2] & 0xfe) == 0 &&
|
||||
mqtt_diag.prefix[3] <= 5 &&
|
||||
(mqtt_diag.prefix[3] == 0 || mqtt_diag.prefix[2] == 0);
|
||||
if (valid_connack) {
|
||||
printf("[MQTT-DIAG] CONNACK code=%u reason=%s session_present=%u\r\n",
|
||||
(unsigned int)mqtt_diag.prefix[3], mqtt_diag_connack_reason(mqtt_diag.prefix[3]),
|
||||
(unsigned int)mqtt_diag.prefix[2]);
|
||||
} else if (mqtt_diag.received) {
|
||||
printf("[MQTT-DIAG] no valid initial CONNACK: received_prefix_bytes=%u\r\n", mqtt_diag.prefix_size);
|
||||
} else {
|
||||
printf("[MQTT-DIAG] no response bytes returned by SDK read\r\n");
|
||||
}
|
||||
if (result != 0 && !mqtt_diag.write_calls)
|
||||
printf("[MQTT-DIAG] failed before transport write; inspect client state/CONNECT serialization\r\n");
|
||||
/* SDK read maps both EOF and some socket errors to -1. The errno snapshot
|
||||
* can be stale; do not claim a broker rejection without a valid CONNACK. */
|
||||
return result;
|
||||
}
|
||||
@@ -1,488 +1,166 @@
|
||||
#if 1
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ql_rtos.h"
|
||||
#if 1
|
||||
#include <stdio.h>
|
||||
#include "ql_rtos.h"
|
||||
#include "ql_application.h"
|
||||
#include "ql_audio.h"
|
||||
#include "ql_iic.h"
|
||||
#include "ql_gpio.h"
|
||||
#include "app_inc.h"
|
||||
#include "ql_fs.h"
|
||||
#include "DP1312EA.h"
|
||||
#include "TypeA.h"
|
||||
#include "LPCD.h"
|
||||
#include "hal_api.h"
|
||||
|
||||
#define DP1312EA_PIN_IRQ GPIO_PIN_NO_85
|
||||
#define NFC_TX_MODE_DURATION_MS 30000U
|
||||
#define NFC_TICKS_PER_MS_DIVISOR 5U
|
||||
|
||||
extern int fota_start;
|
||||
extern int audio_packet_type;
|
||||
extern volatile int tts_playing;
|
||||
extern int g_testmode;
|
||||
extern uint8_t get_sleep_mode(void);
|
||||
extern int get_mqtt_connect_status(void);
|
||||
extern bool vad_audio_is_recording(void);
|
||||
extern int xiaoice_get_play_counter(void);
|
||||
|
||||
static ql_sem_t nfc_irq_sem = NULL;
|
||||
|
||||
// NFC 任务状态
|
||||
#include "app_inc.h"
|
||||
#include "ql_fs.h"
|
||||
|
||||
extern int fota_start;
|
||||
extern int audio_packet_type;
|
||||
extern volatile int tts_playing;
|
||||
extern int g_testmode;
|
||||
|
||||
static ql_sem_t nfc_irq_sem = NULL;
|
||||
#define DP1312EA_PIN_IRQ GPIO_PIN_NO_85
|
||||
|
||||
// NFC 任务状态
|
||||
typedef enum {
|
||||
NFC_STATE_LPCD_WAIT, // 低功耗等待中断
|
||||
NFC_STATE_POLLING, // 500ms 轮询读卡中
|
||||
} nfc_state_t;
|
||||
|
||||
static volatile nfc_state_t g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
static volatile int g_poll_fail_count = 0;
|
||||
static volatile int g_nfc_irq_trigger = 0;
|
||||
static volatile u8 g_nfc_tx_mode_requested = 0;
|
||||
static volatile u32 g_nfc_tx_mode_deadline = 0;
|
||||
|
||||
#define NFC_CARD_FRAME_MAX 64
|
||||
#define NFC_CARD_TX_POLLS 20
|
||||
#define NFC_CARD_RX_POLLS 200
|
||||
#define NFC_CARD_SESSION_POLL_MS 1
|
||||
#define NFC_TYPE2_PAGE_SIZE 4
|
||||
#define NFC_TYPE2_READ_SIZE 16
|
||||
#define NFC_TYPE2_MEMORY_SIZE 160
|
||||
#define NFC_TYPE2_NDEF_OFFSET 16
|
||||
|
||||
#define NFC_IRQ_TX 0x40
|
||||
#define NFC_IRQ_RX 0x20
|
||||
#define NFC_IRQ_ERR 0x02
|
||||
|
||||
extern int hal_os_get_device_sn(char *pdevice_sn);
|
||||
extern s8 Test_NTAG213(void);
|
||||
|
||||
static u8 nfc_type2_memory[NFC_TYPE2_MEMORY_SIZE];
|
||||
|
||||
static u8 nfc_tx_mode_is_active(void)
|
||||
{
|
||||
u32 now;
|
||||
|
||||
if (!g_nfc_tx_mode_requested) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
now = ql_rtos_get_systicks();
|
||||
if ((s32)(now - g_nfc_tx_mode_deadline) >= 0) {
|
||||
g_nfc_tx_mode_requested = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void app_nfc_tx_mode_start(void)
|
||||
{
|
||||
g_nfc_tx_mode_deadline = ql_rtos_get_systicks() +
|
||||
(NFC_TX_MODE_DURATION_MS / NFC_TICKS_PER_MS_DIVISOR);
|
||||
g_nfc_tx_mode_requested = 1;
|
||||
|
||||
if (nfc_irq_sem != NULL) {
|
||||
ql_rtos_semaphore_release(nfc_irq_sem);
|
||||
}
|
||||
|
||||
printf("NFC TX mode requested for 30 seconds\r\n");
|
||||
}
|
||||
|
||||
static void nfc_card_emulation_log_status(const char *reason)
|
||||
{
|
||||
printf("NFC card emulation %s: cmd=%02X irq=%02X err=%02X status1=%02X status2=%02X fifo=%02X txm=%02X rxm=%02X framing=%02X\r\n",
|
||||
reason,
|
||||
DP1312EA_ReadRawRC(CommandReg),
|
||||
DP1312EA_ReadRawRC(ComIrqReg),
|
||||
DP1312EA_ReadRawRC(ErrorReg),
|
||||
DP1312EA_ReadRawRC(Status1Reg),
|
||||
DP1312EA_ReadRawRC(Status2Reg),
|
||||
DP1312EA_ReadRawRC(FIFOLevelReg),
|
||||
DP1312EA_ReadRawRC(TxModeReg),
|
||||
DP1312EA_ReadRawRC(RxModeReg),
|
||||
DP1312EA_ReadRawRC(BitFramingReg));
|
||||
}
|
||||
|
||||
static u8 nfc_card_emulation_read_frame(u8 *frame, u8 capacity)
|
||||
{
|
||||
u8 fifo_len = DP1312EA_ReadRawRC(FIFOLevelReg) & 0x7F;
|
||||
u8 read_len = fifo_len;
|
||||
|
||||
if (read_len > capacity) {
|
||||
read_len = capacity;
|
||||
}
|
||||
|
||||
if (read_len > 0) {
|
||||
DP1312EA_ReadFIFO(frame, read_len);
|
||||
}
|
||||
|
||||
while (read_len < fifo_len) {
|
||||
(void)DP1312EA_ReadRawRC(FIFODataReg);
|
||||
read_len++;
|
||||
}
|
||||
|
||||
return fifo_len > capacity ? capacity : fifo_len;
|
||||
}
|
||||
|
||||
static void nfc_card_emulation_log_frame(const char *prefix,
|
||||
const u8 *frame,
|
||||
u8 length)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
printf("NFC %s (%u):", prefix, length);
|
||||
for (i = 0; i < length; i++) {
|
||||
printf(" %02X", frame[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
static u8 nfc_card_emulation_send_frame(const u8 *frame, u8 length)
|
||||
{
|
||||
u16 poll_count;
|
||||
u8 irq;
|
||||
|
||||
/* AutoColl has activated target mode and changed to Transceive. Keep it
|
||||
* active: writing Idle here clears TargetActivated in Status2Reg. */
|
||||
DP1312EA_WriteRawRC(ComIrqReg, 0x7F);
|
||||
DP1312EA_SetBitMask(FIFOLevelReg, 0x80);
|
||||
DP1312EA_SetBitMask(TxModeReg, 0x80);
|
||||
DP1312EA_SetBitMask(RxModeReg, 0x80);
|
||||
DP1312EA_WriteRawRC(BitFramingReg, 0x00);
|
||||
DP1312EA_WriteFIFO((u8 *)frame, length);
|
||||
DP1312EA_SetBitMask(BitFramingReg, 0x80);
|
||||
|
||||
for (poll_count = 0; poll_count < NFC_CARD_TX_POLLS; poll_count++) {
|
||||
irq = DP1312EA_ReadRawRC(ComIrqReg);
|
||||
if ((irq & NFC_IRQ_TX) != 0) {
|
||||
DP1312EA_WriteRawRC(BitFramingReg, 0x00);
|
||||
return 1;
|
||||
}
|
||||
if ((irq & NFC_IRQ_ERR) != 0) {
|
||||
break;
|
||||
}
|
||||
ql_rtos_task_sleep_ms(NFC_CARD_SESSION_POLL_MS);
|
||||
}
|
||||
|
||||
DP1312EA_WriteRawRC(BitFramingReg, 0x00);
|
||||
nfc_card_emulation_log_status("frame transmit failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfc_type2_build_ndef(void)
|
||||
{
|
||||
char device_id[32] = {0};
|
||||
char serial_number[32] = {0};
|
||||
char json[128];
|
||||
int json_len;
|
||||
u8 *ndef;
|
||||
u8 record_len;
|
||||
|
||||
if (hal_os_get_device_id(device_id) != 0) {
|
||||
strcpy(device_id, "unknown");
|
||||
}
|
||||
if (hal_os_get_device_sn(serial_number) != 0) {
|
||||
strcpy(serial_number, "unknown");
|
||||
}
|
||||
|
||||
json_len = snprintf(json, sizeof(json),
|
||||
"{\"device_id\":\"%s\",\"serial_number\":\"%s\"}",
|
||||
device_id, serial_number);
|
||||
if (json_len < 0) {
|
||||
json_len = 0;
|
||||
} else if (json_len >= (int)sizeof(json)) {
|
||||
json_len = sizeof(json) - 1;
|
||||
}
|
||||
|
||||
memset(nfc_type2_memory, 0, sizeof(nfc_type2_memory));
|
||||
|
||||
/* Static UID/manufacturer pages followed by a read-only Type 2 CC. */
|
||||
nfc_type2_memory[0] = 0x08;
|
||||
nfc_type2_memory[1] = 0xAA;
|
||||
nfc_type2_memory[2] = 0xBB;
|
||||
nfc_type2_memory[3] = 0xCC;
|
||||
nfc_type2_memory[12] = 0xE1;
|
||||
nfc_type2_memory[13] = 0x10;
|
||||
nfc_type2_memory[14] = 0x12; /* 144 data bytes (18 x 8). */
|
||||
nfc_type2_memory[15] = 0x0F; /* Read-only access. */
|
||||
|
||||
ndef = &nfc_type2_memory[NFC_TYPE2_NDEF_OFFSET];
|
||||
record_len = (u8)(7 + json_len);
|
||||
ndef[0] = 0x03; /* NDEF Message TLV. */
|
||||
ndef[1] = record_len;
|
||||
ndef[2] = 0xD1; /* MB, ME, SR, TNF well-known. */
|
||||
ndef[3] = 0x01; /* Type length. */
|
||||
ndef[4] = (u8)(3 + json_len); /* Text status/lang plus JSON. */
|
||||
ndef[5] = 'T';
|
||||
ndef[6] = 0x02; /* UTF-8, language length 2. */
|
||||
ndef[7] = 'e';
|
||||
ndef[8] = 'n';
|
||||
memcpy(&ndef[9], json, json_len);
|
||||
ndef[9 + json_len] = 0xFE; /* Terminator TLV. */
|
||||
|
||||
printf("NFC Type 2 NDEF: %s\r\n", json);
|
||||
}
|
||||
|
||||
static void nfc_type2_read_pages(u8 start_page, u8 *response)
|
||||
{
|
||||
u16 start = (u16)start_page * NFC_TYPE2_PAGE_SIZE;
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < NFC_TYPE2_READ_SIZE; i++) {
|
||||
u16 offset = start + i;
|
||||
response[i] = offset < NFC_TYPE2_MEMORY_SIZE
|
||||
? nfc_type2_memory[offset]
|
||||
: 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_card_emulation_configure_type2(void)
|
||||
{
|
||||
static const u8 card_config[] = {
|
||||
0x04, 0x00, /* ATQA: single-size (4-byte) UID */
|
||||
0xAA, 0xBB, 0xCC, /* NFCID1; first byte 08 is added by PN512 */
|
||||
0x00, /* SEL_RES / SAK: Type 2, no ISO-DEP */
|
||||
0x01, 0xFE, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
|
||||
0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0xFF, 0xFF,
|
||||
0x88
|
||||
};
|
||||
|
||||
DP1312EA_PcdConfigISOType('K');
|
||||
ql_rtos_task_sleep_ms(2);
|
||||
DP1312EA_WriteFIFO((u8 *)card_config, sizeof(card_config));
|
||||
ql_rtos_task_sleep_ms(2);
|
||||
DP1312EA_WriteRawRC(CommandReg, DP1312EA_MEM);
|
||||
DP1312EA_WriteRawRC(CommandReg, DP1312EA_AUTOCOLL);
|
||||
}
|
||||
|
||||
static void nfc_card_emulation_start(void)
|
||||
{
|
||||
DP1312EA_WriteRawRC(CommandReg, DP1312EA_IDLE);
|
||||
DP1312EA_WriteRawRC(ComIrqReg, 0x7F);
|
||||
DP1312EA_WriteRawRC(DivIrqReg, 0x7F);
|
||||
DP1312EA_SetBitMask(FIFOLevelReg, 0x80);
|
||||
|
||||
nfc_type2_build_ndef();
|
||||
nfc_card_emulation_configure_type2();
|
||||
nfc_card_emulation_log_status("started");
|
||||
printf("NFC Type 2 tag: ATQA=04 00, UID=08 AA BB CC, SAK=00\r\n");
|
||||
}
|
||||
|
||||
static void nfc_card_emulation_task(void)
|
||||
{
|
||||
u8 frame[NFC_CARD_FRAME_MAX];
|
||||
u8 response[NFC_TYPE2_READ_SIZE];
|
||||
u8 frame_len;
|
||||
u16 poll_count;
|
||||
u8 last_command = 0xFF;
|
||||
u8 command;
|
||||
|
||||
nfc_card_emulation_start();
|
||||
|
||||
while (nfc_tx_mode_is_active()) {
|
||||
command = DP1312EA_ReadRawRC(CommandReg) & 0x0F;
|
||||
|
||||
if (command != last_command) {
|
||||
nfc_card_emulation_log_status("state changed");
|
||||
last_command = command;
|
||||
}
|
||||
|
||||
if (command == DP1312EA_TRANSCEIVE) {
|
||||
frame_len = nfc_card_emulation_read_frame(frame, sizeof(frame));
|
||||
while (nfc_tx_mode_is_active() &&
|
||||
frame_len >= 2 && frame[0] == 0x30) {
|
||||
nfc_card_emulation_log_frame("Type 2 READ RX", frame,
|
||||
frame_len);
|
||||
nfc_type2_read_pages(frame[1], response);
|
||||
if (!nfc_card_emulation_send_frame(response,
|
||||
sizeof(response))) {
|
||||
break;
|
||||
}
|
||||
printf("NFC Type 2 READ page %u TX complete\r\n", frame[1]);
|
||||
nfc_card_emulation_log_frame("Type 2 READ TX", response,
|
||||
sizeof(response));
|
||||
|
||||
DP1312EA_WriteRawRC(ComIrqReg, NFC_IRQ_TX);
|
||||
for (poll_count = 0;
|
||||
poll_count < NFC_CARD_RX_POLLS &&
|
||||
nfc_tx_mode_is_active();
|
||||
poll_count++) {
|
||||
u8 irq = DP1312EA_ReadRawRC(ComIrqReg);
|
||||
|
||||
if ((irq & NFC_IRQ_ERR) != 0) {
|
||||
nfc_card_emulation_log_status("Type 2 receive error");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((irq & NFC_IRQ_RX) != 0 &&
|
||||
(DP1312EA_ReadRawRC(FIFOLevelReg) & 0x7F) != 0) {
|
||||
frame_len = nfc_card_emulation_read_frame(
|
||||
frame, sizeof(frame));
|
||||
break;
|
||||
}
|
||||
|
||||
ql_rtos_task_sleep_ms(NFC_CARD_SESSION_POLL_MS);
|
||||
}
|
||||
|
||||
if (poll_count == NFC_CARD_RX_POLLS) {
|
||||
nfc_card_emulation_log_status("Type 2 receive timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((DP1312EA_ReadRawRC(ComIrqReg) & NFC_IRQ_ERR) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_len > 0 && !(frame_len >= 2 && frame[0] == 0x30)) {
|
||||
nfc_card_emulation_log_frame("Type 2 session end RX", frame,
|
||||
frame_len);
|
||||
}
|
||||
|
||||
nfc_card_emulation_log_status("session complete");
|
||||
nfc_card_emulation_start();
|
||||
last_command = DP1312EA_AUTOCOLL;
|
||||
} else if (command != DP1312EA_AUTOCOLL) {
|
||||
nfc_card_emulation_log_status("unexpected command");
|
||||
nfc_card_emulation_start();
|
||||
last_command = DP1312EA_AUTOCOLL;
|
||||
}
|
||||
|
||||
ql_rtos_task_sleep_ms(1);
|
||||
}
|
||||
|
||||
DP1312EA_WriteRawRC(CommandReg, DP1312EA_IDLE);
|
||||
DP1312EA_WriteRawRC(ComIrqReg, 0x7F);
|
||||
DP1312EA_SetBitMask(FIFOLevelReg, 0x80);
|
||||
printf("NFC TX mode expired, restoring reader mode\r\n");
|
||||
}
|
||||
|
||||
static void nfc_irq_wake(void *args)
|
||||
{
|
||||
PIN_LEVEL_E level = PIN_LEVEL_LOW;
|
||||
|
||||
ql_eint_disable_wakeup(DP1312EA_PIN_IRQ);
|
||||
ql_gpio_get_level(DP1312EA_PIN_IRQ, &level);
|
||||
printf("DP1312EA_PIN_IRQ level:%d\n", level);
|
||||
|
||||
ql_eint_enable_wakeup(DP1312EA_PIN_IRQ, PIN_RISING_EDGE);
|
||||
|
||||
if (g_nfc_tx_mode_requested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_nfc_irq_trigger==0){
|
||||
g_nfc_irq_trigger = 1;
|
||||
ql_rtos_semaphore_release(nfc_irq_sem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void nfc_reader_prepare(void)
|
||||
{
|
||||
DP1312EA_Reset();
|
||||
Check_Chip();
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
g_nfc_irq_trigger = 0;
|
||||
}
|
||||
|
||||
static void nfc_reader_task_step(void)
|
||||
{
|
||||
s8 read_status;
|
||||
int can_read = 0;
|
||||
|
||||
if (g_nfc_state == NFC_STATE_LPCD_WAIT) {
|
||||
DP1312EA_LPCD_Start();
|
||||
printf("NFC enter LPCD mode, waiting for IRQ...\r\n");
|
||||
ql_rtos_semaphore_wait(nfc_irq_sem, 0xFFFFFFFF);
|
||||
|
||||
if (g_nfc_tx_mode_requested) {
|
||||
return;
|
||||
}
|
||||
|
||||
DP1312EA_CLEAR_IRQ();
|
||||
ql_rtos_task_sleep_ms(10);
|
||||
printf("NFC IRQ wake up, switch to polling mode\r\n");
|
||||
g_nfc_state = NFC_STATE_POLLING;
|
||||
g_poll_fail_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fota_start == 0 &&
|
||||
get_sleep_mode() != 1 &&
|
||||
get_mqtt_connect_status() == 1) {
|
||||
if ((vad_audio_is_recording() != 1 &&
|
||||
audio_packet_type == 0 &&
|
||||
tts_playing == 0 &&
|
||||
xiaoice_get_play_counter() == 0) ||
|
||||
g_testmode == 1) {
|
||||
can_read = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_read) {
|
||||
ql_rtos_task_sleep_ms(300);
|
||||
return;
|
||||
}
|
||||
|
||||
read_status = Test_NTAG213();
|
||||
if (read_status == 0) {
|
||||
printf("NFC read OK, back to LPCD\r\n");
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
g_nfc_irq_trigger = 0;
|
||||
ql_rtos_task_sleep_ms(10);
|
||||
return;
|
||||
}
|
||||
|
||||
g_poll_fail_count++;
|
||||
printf("NFC read failed, fail_count=%d\r\n", g_poll_fail_count);
|
||||
if (g_poll_fail_count >= 3) {
|
||||
printf("NFC fail 3 times, card removed, back to LPCD\r\n");
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
g_nfc_irq_trigger = 0;
|
||||
ql_rtos_task_sleep_ms(10);
|
||||
} else {
|
||||
ql_rtos_task_sleep_ms(300);
|
||||
}
|
||||
}
|
||||
|
||||
void ql_nfc_task_thread(void *arvg)
|
||||
{
|
||||
printf("NFC work mode: READER (5 clicks enable Type 2 TX)\r\n");
|
||||
|
||||
while (1) {
|
||||
if (nfc_tx_mode_is_active()) {
|
||||
printf("NFC TX mode enabled\r\n");
|
||||
DP1312EA_Reset();
|
||||
Check_Chip();
|
||||
nfc_card_emulation_task();
|
||||
nfc_reader_prepare();
|
||||
continue;
|
||||
}
|
||||
|
||||
nfc_reader_task_step();
|
||||
}
|
||||
}
|
||||
static volatile nfc_state_t g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
static volatile int g_poll_fail_count = 0;
|
||||
static volatile int g_nfc_irq_trigger = 0;
|
||||
|
||||
static void nfc_irq_wake(void *args)
|
||||
{
|
||||
unsigned char level = 0;
|
||||
|
||||
ql_eint_disable_wakeup(DP1312EA_PIN_IRQ);
|
||||
ql_gpio_get_level(DP1312EA_PIN_IRQ, &level);
|
||||
printf("DP1312EA_PIN_IRQ level:%d\n", level);
|
||||
|
||||
ql_eint_enable_wakeup(DP1312EA_PIN_IRQ, PIN_RISING_EDGE);
|
||||
|
||||
if(g_nfc_irq_trigger==0){
|
||||
g_nfc_irq_trigger = 1;
|
||||
ql_rtos_semaphore_release(nfc_irq_sem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DP1312EA_Init(void)
|
||||
{
|
||||
ql_rtos_semaphore_create(&nfc_irq_sem, 0);
|
||||
|
||||
ql_gpio_init(DP1312EA_PIN_IRQ, PIN_DIRECTION_IN, PIN_PULL_PD, PIN_LEVEL_LOW);
|
||||
ql_eint_register(DP1312EA_PIN_IRQ, PIN_RISING_EDGE, PIN_PULL_PD, NULL, nfc_irq_wake);
|
||||
ql_eint_enable_wakeup(DP1312EA_PIN_IRQ, PIN_RISING_EDGE);
|
||||
|
||||
DP1312EA_Reset();
|
||||
Check_Chip();
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
|
||||
ql_task_t nfcTaskRef = NULL;
|
||||
void ql_nfc_task_thread(void *arvg)
|
||||
{
|
||||
s8 read_status;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// ========== 状态1: 低功耗等待中断 ==========
|
||||
if (g_nfc_state == NFC_STATE_LPCD_WAIT) {
|
||||
|
||||
DP1312EA_LPCD_Start(); // 进入低功耗检卡,等待中断
|
||||
|
||||
printf("NFC enter LPCD mode, waiting for IRQ...\r\n");
|
||||
|
||||
// 阻塞等待中断唤醒
|
||||
ql_rtos_semaphore_wait(nfc_irq_sem, 0xFFFFFFFF);
|
||||
|
||||
DP1312EA_CLEAR_IRQ();
|
||||
ql_rtos_task_sleep_ms(10); // 防抖延迟
|
||||
|
||||
printf("NFC IRQ wake up, switch to polling mode\r\n");
|
||||
|
||||
// 切换到轮询模式,重置失败计数
|
||||
g_nfc_state = NFC_STATE_POLLING;
|
||||
g_poll_fail_count = 0;
|
||||
|
||||
// 立即执行第一次读卡(不等待200ms)
|
||||
continue;
|
||||
}
|
||||
|
||||
// ========== 状态2: 200ms 轮询读卡 ==========
|
||||
if (g_nfc_state == NFC_STATE_POLLING) {
|
||||
|
||||
// 检查是否允许读卡(业务条件)
|
||||
int can_read = 0;
|
||||
|
||||
if (fota_start == 0 &&
|
||||
get_sleep_mode() != 1 &&
|
||||
get_mqtt_connect_status() == 1) {
|
||||
|
||||
if ((get_mqtt_connect_status() == 1 &&
|
||||
vad_audio_is_recording() != 1 &&
|
||||
audio_packet_type == 0 &&
|
||||
tts_playing == 0 &&
|
||||
xiaoice_get_play_counter() == 0) ||
|
||||
g_testmode == 1) {
|
||||
|
||||
can_read = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_read) {
|
||||
// 当前业务状态不允许读卡,继续等待下次轮询
|
||||
// printf("NFC business condition not met, keep polling\r\n");
|
||||
ql_rtos_task_sleep_ms(300);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 执行读卡
|
||||
// printf("NFC polling read, fail_count=%d\r\n", g_poll_fail_count);
|
||||
read_status = Test_NTAG213();
|
||||
|
||||
if (read_status == 0) {
|
||||
// ===== 读卡成功 =====
|
||||
printf("NFC read OK, continue polling\r\n");
|
||||
|
||||
// 直接回到 LPCD,不再轮询
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
g_nfc_irq_trigger = 0;
|
||||
|
||||
ql_rtos_task_sleep_ms(10);
|
||||
continue; // 回到 LPCD 等待
|
||||
} else {
|
||||
// ===== 读卡失败 =====
|
||||
g_poll_fail_count++;
|
||||
printf("NFC read failed, fail_count=%d\r\n", g_poll_fail_count);
|
||||
|
||||
if (g_poll_fail_count >= 3) {
|
||||
// 连续失败 3 次,认为卡已离开
|
||||
printf("NFC fail 3 times, card removed, back to LPCD\r\n");
|
||||
|
||||
// 清理状态,回到低功耗
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
g_nfc_irq_trigger = 0;
|
||||
// 短暂延迟确保硬件稳定
|
||||
ql_rtos_task_sleep_ms(10);
|
||||
|
||||
} else {
|
||||
// 继续轮询,可能卡只是位置偏移
|
||||
ql_rtos_task_sleep_ms(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DP1312EA_Init(void)
|
||||
{
|
||||
// 创建信号量
|
||||
ql_rtos_semaphore_create(&nfc_irq_sem, 0);
|
||||
|
||||
// 配置中断引脚
|
||||
ql_gpio_init(DP1312EA_PIN_IRQ, PIN_DIRECTION_IN, PIN_PULL_PD, PIN_LEVEL_LOW);
|
||||
ql_eint_register(DP1312EA_PIN_IRQ, PIN_RISING_EDGE, PIN_PULL_PD, NULL, nfc_irq_wake);
|
||||
ql_eint_enable_wakeup(DP1312EA_PIN_IRQ, PIN_RISING_EDGE);
|
||||
|
||||
// I2C 和芯片初始化
|
||||
DP1312EA_Reset();
|
||||
Check_Chip();
|
||||
|
||||
// 初始状态为低功耗等待
|
||||
g_nfc_state = NFC_STATE_LPCD_WAIT;
|
||||
g_poll_fail_count = 0;
|
||||
|
||||
// 创建任务
|
||||
ql_task_t nfcTaskRef = NULL;
|
||||
ql_rtos_task_create(&nfcTaskRef, 1024*6, 100, "nfc_Task_Ref", ql_nfc_task_thread, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -327,14 +327,14 @@ void vad_audio_process(void) {
|
||||
ql_audio_record_read(record_hd, mic_buf, &record_len);
|
||||
|
||||
//如果检测到声音再去传输
|
||||
// if(first_sound_detected){
|
||||
//实际上每次编码需要输入1152字节的PCM数据
|
||||
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
|
||||
if(first_sound_detected){
|
||||
//实际上每次编码需要输入1152字节的PCM数据
|
||||
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
|
||||
|
||||
//quec_usbcdc_write((uint8*)mic_buf, record_len);
|
||||
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
|
||||
//quec_usbcdc_write((uint8*)mic_buf, record_len);
|
||||
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
|
||||
}
|
||||
}
|
||||
// }
|
||||
#if 1
|
||||
for(index=0; index<320; index++)
|
||||
{
|
||||
@@ -354,14 +354,12 @@ void vad_audio_process(void) {
|
||||
// 连续达到阈值才确认检测到声音
|
||||
if (speech_detect_count >= SPEECH_DETECT_THRESHOLD) {
|
||||
|
||||
#if 0
|
||||
//实际上每次编码需要输入1152字节的PCM数据
|
||||
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
|
||||
|
||||
//quec_usbcdc_write((uint8*)mic_buf, record_len);
|
||||
ql_rtos_semaphore_release(sender_sem); //告诉线程去获取录音数据
|
||||
}
|
||||
#endif
|
||||
|
||||
first_sound_detected = true;
|
||||
set_state(STATE_RECORDING_ACTIVE);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "ql_ping.h"
|
||||
#include "ql_nw.h"
|
||||
#include "ql_atcmd.h"
|
||||
#include "ql_platform.h"
|
||||
#include "layer3.h"
|
||||
#include "ql_uart.h"
|
||||
#include "hal_api.h"
|
||||
@@ -30,8 +29,6 @@
|
||||
|
||||
|
||||
#define PCM_FRAME_LENGTH 1152
|
||||
#define CLOUD_TTS_ERROR_URL_FORMAT \
|
||||
"http://101.35.224.118:8080/assets/roles/banban/tts_error/tts_error%02u.mp3"
|
||||
|
||||
static int g_sim_ready = 0;
|
||||
|
||||
@@ -52,7 +49,7 @@ static int16 *ppbuff=mp3_buff;
|
||||
static int onmessage_status = -1;
|
||||
volatile int tts_playing = 0;
|
||||
unsigned char wss_connect_start = 0;
|
||||
static volatile int player_mq_couter = 0;
|
||||
static int player_mq_couter = 0;
|
||||
static unsigned char audio_in_task_cancel = 0;
|
||||
|
||||
static unsigned char xiaoice_wss_connect_status = 0;
|
||||
@@ -108,11 +105,6 @@ static librws_app_t app = &_app;
|
||||
static ql_sem_t ws_connect_sem = NULL;
|
||||
extern int fota_start;
|
||||
|
||||
#define OTA_AUDIO_STOP_TIMEOUT_MS 3000
|
||||
#define OTA_AUDIO_STOP_POLL_MS 20
|
||||
|
||||
void xiaoice_clear_player_mq(void);
|
||||
|
||||
|
||||
static int tts_play_status = 0;//1播放中 2播放完成
|
||||
|
||||
@@ -195,56 +187,6 @@ static void remove_underscores(char *str) {
|
||||
*dst = '\0'; // 添加字符串结束符
|
||||
}
|
||||
|
||||
/*
|
||||
* 将后台下发的任意角色对话超时音频 URL 改为 banban 角色的 HTTP 地址,
|
||||
* 并在 tts_error00~tts_error09 中随机选择一条。
|
||||
*/
|
||||
static int randomize_tts_error_url(char *url, size_t url_size)
|
||||
{
|
||||
static uint32_t random_sequence = 0;
|
||||
static unsigned int last_random_index = 10;
|
||||
char *file_name;
|
||||
uint32_t random_value;
|
||||
unsigned int random_index;
|
||||
int written;
|
||||
|
||||
if(url == NULL || url_size == 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
file_name = strrchr(url, '/');
|
||||
file_name = (file_name == NULL) ? url : file_name + 1;
|
||||
|
||||
if(strncmp(file_name, "tts_error", strlen("tts_error")) != 0 &&
|
||||
strncmp(file_name, "tts_err", strlen("tts_err")) != 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 某些设备上硬件随机接口可能持续返回 0,因此混合伪随机数、系统 tick
|
||||
* 和递增序列;若仍与上一次相同,则向后偏移,避免连续重复播放。
|
||||
*/
|
||||
random_sequence++;
|
||||
random_value = hal_os_get_rand();
|
||||
random_value ^= ql_pseudo_random_number();
|
||||
random_value ^= ql_rtos_get_systicks();
|
||||
random_value ^= random_sequence * 0x9E3779B9U;
|
||||
random_index = random_value % 10;
|
||||
if(random_index == last_random_index){
|
||||
random_index = (random_index + 1 + (random_sequence % 9)) % 10;
|
||||
}
|
||||
last_random_index = random_index;
|
||||
|
||||
written = snprintf(url, url_size, CLOUD_TTS_ERROR_URL_FORMAT, random_index);
|
||||
if(written < 0 || (size_t)written >= url_size){
|
||||
printf("random tts error url buffer too small\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("random cloud tts error index:%02u, url:%s\n", random_index, url);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void add_file_to_play_player_queue(char *path, int type)
|
||||
{
|
||||
ice_work_item *item = NULL;
|
||||
@@ -292,13 +234,8 @@ void add_file_to_play_player_queue(char *path, int type)
|
||||
|
||||
|
||||
//如果录音后 超过10s 还没有收到信息 提示本轮对话结束
|
||||
static void onmessage_text(rws_socket socket, const char *text, const unsigned int len)
|
||||
{
|
||||
if (fota_start == 1) {
|
||||
printf("ignore websocket audio during OTA\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void onmessage_text(rws_socket socket, const char *text, const unsigned int len)
|
||||
{
|
||||
// printf("message(txt), %d(byte): %s ", len, text);
|
||||
|
||||
char url_buff[128]={0x00};
|
||||
@@ -319,32 +256,14 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i
|
||||
|
||||
}else{//TTS_URL:audio_url
|
||||
wss_connect_start = 0;
|
||||
char *ret_str1 = strstr(text, "https://");
|
||||
char *ret_str2 = NULL;
|
||||
size_t url_len = 0;
|
||||
int is_tts_error_url = 0;
|
||||
if(ret_str1 == NULL){
|
||||
ret_str1 = strstr(text, "http://");
|
||||
}
|
||||
if(ret_str1 != NULL){
|
||||
ret_str2 = strstr(ret_str1, ".mp3");
|
||||
}
|
||||
char *ret_str1= strstr(text, "http:");
|
||||
char *ret_str2= strstr(text, ".mp3");
|
||||
int len = 0;
|
||||
if(ret_str1!=NULL && ret_str2!=NULL){
|
||||
|
||||
/* 提取完整的 HTTP/HTTPS MP3 URL,结尾额外包含“.mp3”四个字符 */
|
||||
url_len = (size_t)(ret_str2 - ret_str1) + strlen(".mp3");
|
||||
if(url_len >= sizeof(url_buff)){
|
||||
printf("audio url too long:%d\n", (int)url_len);
|
||||
return;
|
||||
}
|
||||
memcpy(url_buff, ret_str1, url_len);
|
||||
url_buff[url_len] = '\0';
|
||||
|
||||
/* 两个角色统一使用 banban 的 HTTP 随机超时提示音 */
|
||||
is_tts_error_url = randomize_tts_error_url(url_buff, sizeof(url_buff));
|
||||
if(is_tts_error_url < 0){
|
||||
return;
|
||||
}
|
||||
|
||||
len = strlen(ret_str1)-strlen(ret_str2);
|
||||
strncpy(url_buff, ret_str1, len);
|
||||
sprintf(url_buff+len, "%s", ".mp3");
|
||||
|
||||
memcpy(url_buff_bak, url_buff, strlen(url_buff)+1);
|
||||
|
||||
@@ -363,7 +282,7 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i
|
||||
tts_play_status = 2;
|
||||
}
|
||||
|
||||
if(is_tts_error_url == 1 || strstr(url_buff, "message_ok")!=NULL){
|
||||
if(strstr(url_buff, "tts_error")!=NULL || strstr(url_buff, "message_ok")!=NULL){
|
||||
printf("tts_error or message_ok\n");
|
||||
vad_set_speech_timeout_flag(1);
|
||||
}
|
||||
@@ -901,49 +820,6 @@ int app_play_tips_file(int index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int app_play_ota_tips_file(void)
|
||||
{
|
||||
char filename_buff[64] = {0};
|
||||
uint32_t timeout;
|
||||
|
||||
/* OTA owns the audio path from this point on. Stop the active conversation
|
||||
* before queuing the prompt, otherwise recording/TTS rejects or masks it. */
|
||||
if (vad_audio_is_recording()) {
|
||||
vad_audio_stop_record();
|
||||
}
|
||||
|
||||
if (get_xiaoice_connected_status() == 2) {
|
||||
talkingq_ws_disconnect();
|
||||
}
|
||||
|
||||
tts_playing = 2;
|
||||
xiaoice_clear_player_mq();
|
||||
|
||||
timeout = hal_os_up_time_ms();
|
||||
while (player_mq_couter > 0) {
|
||||
ql_rtos_task_sleep_ms(OTA_AUDIO_STOP_POLL_MS);
|
||||
if (hal_os_up_time_ms() - timeout > OTA_AUDIO_STOP_TIMEOUT_MS) {
|
||||
printf("OTA wait previous audio timeout, counter:%d\r\n",
|
||||
player_mq_couter);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
tts_play_status = 0;
|
||||
tts_playing = 0;
|
||||
|
||||
/* Normal prompts are blocked while fota_start is set. Queue the dedicated
|
||||
* OTA prompt directly after the old conversation has fully stopped. */
|
||||
snprintf(filename_buff, sizeof(filename_buff), "U:/%s", mp3_file[3]);
|
||||
printf("xiaoice_play_index:3\n");
|
||||
if (ql_access(filename_buff, 0) != 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
add_file_to_play_player_queue(filename_buff, 0);
|
||||
return player_mq_couter > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
void app_player_thread(void *arvg)
|
||||
{
|
||||
ice_work_item *player_item=NULL;
|
||||
@@ -1168,12 +1044,12 @@ void xiaoice_clear_player_mq(void)
|
||||
free(player_item->data);
|
||||
player_item->data = NULL;
|
||||
}
|
||||
if(player_mq_couter>0) player_mq_couter--;
|
||||
free(player_item);
|
||||
player_item = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
player_mq_couter=0;
|
||||
}
|
||||
|
||||
void xiaoice_close_all_audio(void)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef NFC_GNSS_BOARD_FEATURES_H
|
||||
#define NFC_GNSS_BOARD_FEATURES_H
|
||||
|
||||
/* Legacy board has no GNSS. Set to 1 and rebuild kernel + app for GNSS boards. */
|
||||
#define NFC_GNSS_BOARD_HAS_GNSS 0
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
============================================================================*/
|
||||
|
||||
#include "gps_app.h"
|
||||
#include "../../../../ql-config/config/board_features.h"
|
||||
#include <string.h>
|
||||
#include "gps_debug.h"
|
||||
#include "quec_hx_agps.h"
|
||||
@@ -840,6 +841,10 @@ void quec_get_ocname_by_gnss_task(void * argv)
|
||||
|
||||
void quec_get_gnssid_task(void)
|
||||
{
|
||||
#if !NFC_GNSS_BOARD_HAS_GNSS
|
||||
RTI_LOG("[GNSS] Legacy board: skip GNSS ID detection");
|
||||
return;
|
||||
#else
|
||||
QuecOSStatus err = kNoErr;
|
||||
quec_task_t quec_ocname_get = NULL;
|
||||
err = quec_rtos_task_create(&quec_ocname_get, 2048, QUEC_APP_TASK_PRIORITY,"q_gocg", quec_get_ocname_by_gnss_task, NULL);
|
||||
@@ -849,6 +854,7 @@ void quec_get_gnssid_task(void)
|
||||
RTI_LOG("quec_get_ocname_by_gnss_task create failed!");
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import struct
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss/new-board'
|
||||
output = root / 'artifacts/wss/ecc-fix'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
toolchain = root / 'ql-cross-tool/win32/owtoolchain'
|
||||
gcc = toolchain / 'gcc-arm-none-eabi/bin/arm-none-eabi-gcc.exe'
|
||||
|
||||
@@ -11,7 +11,7 @@ parser.add_argument('chain', nargs='?', default=str(root / 'artifacts/wss/retest
|
||||
parser.add_argument('--compare-nist', action='store_true')
|
||||
parser.add_argument('--compare-windows', action='store_true')
|
||||
args = parser.parse_args()
|
||||
output = root / 'artifacts/wss/new-board/host-cert'
|
||||
output = root / 'artifacts/wss/tls-speed/host-cert'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
chain = Path(args.chain).resolve()
|
||||
sources = '''asn1parse asn1write bignum base64 pem oid pk pkparse pk_wrap rsa rsa_internal
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
"""Exercise actual MQTT diagnostic callbacks without a broker or credentials."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss/new-board/mqtt-diag/tests'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
(output / 'MQTTClient.h').write_text(r'''
|
||||
#ifndef TEST_MQTT_CLIENT_H
|
||||
#define TEST_MQTT_CLIENT_H
|
||||
typedef struct Network Network;
|
||||
struct Network {
|
||||
int (*mqttread)(Network *, unsigned char *, int, int);
|
||||
int (*mqttwrite)(Network *, unsigned char *, int, int);
|
||||
};
|
||||
typedef struct { Network *ipstack; unsigned int command_timeout_ms; int isconnected; } MQTTClient;
|
||||
typedef struct { unsigned char MQTTVersion, cleansession; unsigned short keepAliveInterval; } MQTTPacket_connectData;
|
||||
typedef struct { unsigned char rc, sessionPresent; } MQTTConnackData;
|
||||
int MQTTConnectWithResults(MQTTClient *, MQTTPacket_connectData *, MQTTConnackData *);
|
||||
unsigned int ql_rtos_get_systicks(void);
|
||||
#endif
|
||||
''')
|
||||
(output / 'sockets.h').write_text('int ql_get_soc_errno(void);\n')
|
||||
harness = r'''
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mqtt_connect_diag.h"
|
||||
static unsigned int ticks;
|
||||
static int mode, offset, writes, reads;
|
||||
unsigned int ql_rtos_get_systicks(void) { return ticks; }
|
||||
int ql_get_soc_errno(void) { return 104; }
|
||||
static int fake_write(Network *n, unsigned char *buf, int size, int timeout) {
|
||||
(void)n;
|
||||
assert(timeout == 30000 && size == 19);
|
||||
assert(memcmp(buf, "PRIVATE_TEST_SECRET", 19) == 0);
|
||||
++writes; ticks += 2;
|
||||
return mode == 2 ? -1 : size;
|
||||
}
|
||||
static int fake_read(Network *n, unsigned char *buf, int size, int timeout) {
|
||||
unsigned char response[] = {0x20, 2, 0, 0};
|
||||
(void)n;
|
||||
assert(timeout == 30000 && (size == 1 || size == 2));
|
||||
++reads; ticks += 3;
|
||||
if (mode == 3) return -1;
|
||||
if (mode == 4) return 0;
|
||||
if (mode == 1) response[3] = 5;
|
||||
if (mode == 5) response[0] = 0x30;
|
||||
if (mode == 7) { response[2] = 1; response[3] = 5; }
|
||||
memcpy(buf, response + offset, size); offset += size;
|
||||
return size;
|
||||
}
|
||||
int MQTTConnectWithResults(MQTTClient *client, MQTTPacket_connectData *options, MQTTConnackData *ack) {
|
||||
unsigned char packet[] = "PRIVATE_TEST_SECRET";
|
||||
unsigned char response[4] = {0};
|
||||
int result;
|
||||
assert(options->MQTTVersion == 3);
|
||||
assert(ack->rc == 255);
|
||||
if (mode == 6) return -1;
|
||||
result = client->ipstack->mqttwrite(client->ipstack, packet, 19, 30000);
|
||||
if (result != 19) { assert(result == -1); return -1; }
|
||||
/* SDK reads fixed header, remaining length, then the two-byte body. */
|
||||
result = client->ipstack->mqttread(client->ipstack, response, 1, 30000);
|
||||
if (mode == 3 || mode == 4) { assert(result == (mode == 3 ? -1 : 0)); return -1; }
|
||||
assert(result == 1);
|
||||
assert(client->ipstack->mqttread(client->ipstack, response + 1, 1, 30000) == 1);
|
||||
assert(client->ipstack->mqttread(client->ipstack, response + 2, 2, 30000) == 2);
|
||||
assert(response[1] == 2);
|
||||
return mode == 0 ? 0 : (mode == 1 ? 5 : -1);
|
||||
}
|
||||
int main(void) {
|
||||
Network network = {fake_read, fake_write};
|
||||
MQTTClient client = {&network, 30000, 0};
|
||||
MQTTPacket_connectData options = {3, 0, 60};
|
||||
for (mode = 0; mode < 8; ++mode) {
|
||||
int expected = mode == 0 ? 0 : (mode == 1 ? 5 : -1);
|
||||
ticks = mode == 0 ? 0xfffffffeU : 0; offset = writes = reads = 0;
|
||||
printf("CASE %d\n", mode);
|
||||
assert(talkingq_mqtt_connect_diagnosed(&client, &options) == expected);
|
||||
assert(network.mqttread == fake_read && network.mqttwrite == fake_write);
|
||||
assert(writes == (mode == 6 ? 0 : 1));
|
||||
if (mode == 6 || mode == 2) assert(reads == 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
(output / 'test.c').write_text(harness)
|
||||
include = root / 'ql-application/threadx/interface/custom_app/inc'
|
||||
source = root / 'ql-application/threadx/interface/custom_app/mqtt_connect_diag.c'
|
||||
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', '/I' + str(output), '/I' + str(include),
|
||||
str(source), str(output / 'test.c'), '/Fe' + str(output / 'test.exe')], cwd=output, check=True)
|
||||
result = subprocess.check_output([str(output / 'test.exe')], text=True)
|
||||
assert 'PRIVATE_TEST_SECRET' not in result
|
||||
cases = [part for part in result.split('CASE ')[1:]]
|
||||
assert len(cases) == 8
|
||||
assert 'CONNACK code=0 reason=accepted' in cases[0] and 'elapsed_ms=55' in cases[0]
|
||||
assert 'CONNACK code=5 reason=not_authorized' in cases[1]
|
||||
assert 'write request=19 rc=-1' in cases[2]
|
||||
assert 'read request=1 rc=-1' in cases[3]
|
||||
assert 'read request=1 rc=0' in cases[4]
|
||||
assert 'no valid initial CONNACK' in cases[5]
|
||||
assert 'failed before transport write' in cases[6]
|
||||
assert 'no valid initial CONNACK' in cases[7]
|
||||
for case in cases[2:]:
|
||||
assert '[MQTT-DIAG] CONNACK code=' not in case
|
||||
(output / 'test-output.log').write_text(result)
|
||||
print('PASS: transport arguments/return values, callback restoration, split CONNACK, rejection, invalid response, errors, no-I/O failure, tick wrap and credential redaction')
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Verify the new-board WSS package, including enabled GNSS task entry points."""
|
||||
"""Verify the WSS flash package against the current legacy-board build."""
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
@@ -8,7 +8,7 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/new-board/EG800AKCN_11LG-new-board-wss-x25519.zip'
|
||||
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip'
|
||||
output = package.parent
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
objdump = root / 'ql-cross-tool/win32/owtoolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objdump.exe'
|
||||
@@ -33,9 +33,8 @@ clock_body = subprocess.check_output([str(objdump), '-d', '--disassemble=x509_ge
|
||||
assert '<mbedtls_platform_gmtime_r>' in clock_body and '<GetCurrentCompTime>' not in clock_body
|
||||
(output / 'x509-clock.disasm.txt').write_text(clock_body)
|
||||
report = {'package': package.name, 'sha256': hashlib.sha256(package.read_bytes()).hexdigest(),
|
||||
'board': 'new board with GNSS enabled',
|
||||
'linked_symbols': required, 'kernel_symbols': kernel_required,
|
||||
'hardware_test': 'New-board package awaits device testing; legacy board verified X25519 full handshake at 1455 ms on 2026-09-16',
|
||||
'hardware_test': '2026-09-16 14:38 device log: NIST TLS handshake 1820 ms and HTTP 101 passed; X25519 preference awaits device test',
|
||||
'backend': '2026-09-16 11:32 CST: WSS 443 /ws HTTP 101; 8080 plaintext WS'}
|
||||
with zipfile.ZipFile(package) as archive:
|
||||
assert archive.testzip() is None, 'ZIP CRC failure'
|
||||
@@ -47,21 +46,16 @@ with zipfile.ZipFile(package) as archive:
|
||||
assert b'wss://banban.api.talkingq.com:443/ws' in archive.read('customer_app.bin')
|
||||
assert b'WSS TLS handshake total=' in archive.read('customer_app.bin')
|
||||
assert b'WSS TLS full handshake: prefer X25519' in archive.read('customer_app.bin')
|
||||
assert b'Legacy board:' not in archive.read('customer_app.bin')
|
||||
assert b'Legacy board:' not in archive.read('cp.bin')
|
||||
assert b'gnss_Task_Ref' in archive.read('customer_app.bin')
|
||||
report['download_manifest'] = json.loads(archive.read('download.json'))
|
||||
task_create_address = int(re.search(r'^([0-9a-fA-F]+) T ql_rtos_task_create$', symbols, re.M).group(1), 16)
|
||||
printf_address = int(re.search(r'^([0-9a-fA-F]+) T printf$', symbols, re.M).group(1), 16)
|
||||
for symbol, binary, expected in [
|
||||
('quec_get_gnssid_task', root / 'ql-kernel/threadx/build/kernel.axf', '<quec_rtos_task_create>'),
|
||||
('app_gnss_init', app, '0x%08x' % (task_create_address | 1)),
|
||||
('set_gnss_mode', app, 'strb')]:
|
||||
('quec_get_gnssid_task', root / 'ql-kernel/threadx/build/kernel.axf', '<RTI_LOG>'),
|
||||
('app_gnss_init', app, '0x%08x' % (printf_address | 1)),
|
||||
('set_gnss_mode', app, 'bx\tlr')]:
|
||||
body = subprocess.check_output([str(objdump), '-d', '--disassemble=' + symbol, str(binary)], text=True)
|
||||
assert '<' + symbol + '>:' in body and expected in body, 'GNSS entry point disabled or unexpected code: ' + symbol
|
||||
assert '<' + symbol + '>:' in body and expected in body and 'task_create' not in body
|
||||
(output / (symbol + '.disasm.txt')).write_text(body)
|
||||
for symbol, table in [('quec_get_ocname_by_gnss_task', kernel_symbols), ('quec_gnss_test', symbols)]:
|
||||
assert re.search(r'\b' + symbol + r'$', table, re.M), 'Missing GNSS worker: ' + symbol
|
||||
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS task creation, WSS URL, ECDSA/GCM/RTC/NIST kernel symbols and handshake timing logs'
|
||||
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS bypass, WSS URL, ECDSA/GCM/RTC/NIST kernel symbols and handshake timing logs'
|
||||
(output / 'verification.json').write_text(json.dumps(report, indent=2))
|
||||
print(report['verification'])
|
||||
print('SHA256:', report['sha256'])
|
||||
|
||||
Reference in New Issue
Block a user