From 30d072defc8da01944b0a8ec29336e445834b53c Mon Sep 17 00:00:00 2001 From: "evan.liu" <583229071@qq.com> Date: Fri, 4 Sep 2026 08:53:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DASI=E5=AF=BC=E8=87=B4ConnectB?= =?UTF-8?q?le=E8=B0=83=E7=94=A8=E5=A4=B1=E6=95=88(setMac/pushMsg=E5=90=8E?= =?UTF-8?q?=E8=A1=A5=E5=88=86=E5=8F=B7)=EF=BC=9B=E6=96=B0=E5=A2=9EMCM=5FIN?= =?UTF-8?q?FO=E5=8F=AA=E8=AF=BB=E5=8F=82=E6=95=B0=E7=89=B9=E5=BE=81?= =?UTF-8?q?=E8=AF=BB=E5=8F=96(BluetoothManager.readInfoChar+=E8=AF=BB?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=8C=89UUID=E5=88=86=E5=8F=91,McmInfo.readD?= =?UTF-8?q?eviceInfo)=EF=BC=8C=E4=B8=BB=E9=A1=B5=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=BD=AE=E8=AF=A2=E4=BB=8EINFO=5FQUERY=E5=88=87=E5=88=B0read?= =?UTF-8?q?=E7=89=B9=E5=BE=81(...c72e0004)=EF=BC=9Bdocs=E8=A1=A5=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../修改记录_2026-09-04_BLE连接ASI分号修复.md | 18 ++++ docs/修改记录_2026-09-04_设备参数只读特征.md | 39 +++++++++ .../bluetoothOperation/bluetoothOperation.vue | 2 +- pages/tabbar/home.vue | 6 +- src/BluetoothManager.ts | 86 +++++++++++++++---- src/mcm/McmInfo.ts | 16 ++++ 6 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 docs/修改记录_2026-09-04_BLE连接ASI分号修复.md create mode 100644 docs/修改记录_2026-09-04_设备参数只读特征.md diff --git a/docs/修改记录_2026-09-04_BLE连接ASI分号修复.md b/docs/修改记录_2026-09-04_BLE连接ASI分号修复.md new file mode 100644 index 0000000..b8cb663 --- /dev/null +++ b/docs/修改记录_2026-09-04_BLE连接ASI分号修复.md @@ -0,0 +1,18 @@ +# 修改记录 2026-09-04:修复 ASI 导致 BLE 连接调用失效 + +## 问题现象 + +点击设备列表连接、CLI 页点 connect 均无反应,控制台报 +`TypeError: setMac(...) is not a function` / `pushMsg(...) is not a function`。 + +## 根因 + +`pages/tabbar/home.vue:230` 与 `pages/bleTest/bluetoothOperation/bluetoothOperation.vue:147` +两处,前一行语句末尾没有分号,下一行又以 `(` 开头(`(getApp().globalData as any).ConnectBle(...)`), +JavaScript 自动分号插入(ASI)对此不生效,编译产物把两行合并成 +`setMac(...)(getApp()...)` —— 拿上一行的返回值(undefined)当函数调用。 + +## 修改 + +两处行末补分号。排查技巧:源码函数明明存在却报 "is not a function" 时, +到 `unpackage/dist/dev/app-plus/app-service.js` 搜编译产物即可定位。 diff --git a/docs/修改记录_2026-09-04_设备参数只读特征.md b/docs/修改记录_2026-09-04_设备参数只读特征.md new file mode 100644 index 0000000..91feebf --- /dev/null +++ b/docs/修改记录_2026-09-04_设备参数只读特征.md @@ -0,0 +1,39 @@ +# 修改记录 2026-09-04:设备参数改为只读特征 + App 定时读 + +设备参数查看(温度/风扇/电压/版本/uptime/heap)从「INFO_QUERY 帧 + notify 应答」 +改为「只读特征 + App 定时 read」,不再依赖 notify 上行通道。 + +## GATT 变更(固件 mcm-ddc-ble V1.00.04) + +- 新增只读特征 `00002760-08c2-11e1-9073-0e8ac72e0004`(`MCM_INFO_UUID`): + 读出内容为全部信息项的 TLV 序列 `{id u8, len u8, value…}×n`(数值小端), + 与 INFO_RSP(all) 帧 payload 完全一致。信息项 id 不变: + 0x01 FW_VERSION(u32)、0x02 CHIP_TEMP(i16, 0.1℃, 0x7FFF=失败)、 + 0x03 FAN_RPM(u16, 0xFFFF=无硬件)、0x04 VDD_MV(u16, 0=失败)、 + 0x05 UPTIME_S(u32)、0x06 FREE_HEAP(u32)。 +- 既有特征不变:`...e0001`(帧协议下行,无响应写)、`...e0002`(notify 上行)、 + `...e0003`(CLI 读写特征:写原始命令行、读输出文本,V1.00.02 新增)。 +- 固件侧实现:`app_info.c` 抽出 `app_info_tlv_snapshot()`(INFO_QUERY 帧应答与其 + 共用同一组包逻辑);`app_rdtss.c` 读请求按 `att_idx` 分发(CLI_VAL → 命令输出, + INFO_VAL → TLV 快照,其余保持原 "NSING" 行为)。 +- 附带修复(V1.00.03):芯片温度采样前先给 TS 5ms 稳定时间并丢弃第一次转换, + 修复 `devinfo`/INFO_QUERY 温度显示 287.6°C 的问题(传感器未稳定即采样)。 + +## App 侧修改 + +- `src/BluetoothManager.ts`:新增 `MCM_INFO_UUID` 常量与 `readInfoChar(cb)`; + `onBLECharacteristicValueChange` 监听重构为 `ensureValueChangeListener()`, + notify 数据与 read 结果按特征 UUID 分发(uni 的读结果经此回调返回); + 读操作带 3s 超时。 +- `src/mcm/McmInfo.ts`:新增 `readDeviceInfo()`,读参数特征并复用 `parseTlv` 解析; + 原 `queryDeviceInfo()`(帧协议 INFO_QUERY)保留。 +- `pages/tabbar/home.vue`:参数轮询 `pollParams()` 从 `queryDeviceInfo` 切到 + `readDeviceInfo`,一次读出全部项取温度+风扇。 + +## 验证 + +- 固件 `UV4 -r` 全量重建 0 Error / 0 Warning;`tools/out/caiic_ble_full.hex` 出包, + OTA 清单 version=0x00010004。 +- PC 侧验证(烧录后):`tools\ble_cli.bat param` 读参数特征并解析 TLV; + `tools\ble_cli.bat -rw help` 验证 CLI 读写特征。 +- 真机回归:主页温度/风扇显示(轮询走 read),CLI 终端、OTA 流程不受影响。 diff --git a/pages/bleTest/bluetoothOperation/bluetoothOperation.vue b/pages/bleTest/bluetoothOperation/bluetoothOperation.vue index 60c8dfd..8557a04 100644 --- a/pages/bleTest/bluetoothOperation/bluetoothOperation.vue +++ b/pages/bleTest/bluetoothOperation/bluetoothOperation.vue @@ -144,7 +144,7 @@ function onConnect() { title: "Connecting...", mask: true }) - pushMsg("start", "connect: " + addr) + pushMsg("start", "connect: " + addr); (getApp().globalData as any).ConnectBle(addr, false) } diff --git a/pages/tabbar/home.vue b/pages/tabbar/home.vue index 7058c1f..eb770aa 100644 --- a/pages/tabbar/home.vue +++ b/pages/tabbar/home.vue @@ -103,8 +103,8 @@ let pageVisible = false async function pollParams() { if (!connected.value || !pageVisible) return - // 一次 INFO_QUERY 同时取温度+风扇(协议 V1.1 第 5 节) - const info = await McmInfo.queryDeviceInfo([McmInfo.INFO_CHIP_TEMP, McmInfo.INFO_FAN_RPM]) + // 读只读参数特征(MCM_INFO_UUID,TLV 同 INFO_RSP 全部项),取温度+风扇 + const info = await McmInfo.readDeviceInfo() const t = info.chipTemp temperature.value = t != null ? t.toFixed(1) : "--" const f = info.fanRpm @@ -227,7 +227,7 @@ function onLinkBluetooth(item: BleScanResult) { connecting.value = true deviceName.value = item.device.name ?? DEVICE_NAME_FILTER BleINfo.setName(deviceName.value) - BleINfo.setMac(item.device.address) + BleINfo.setMac(item.device.address); (getApp().globalData as any).ConnectBle(item.device.address, false) } diff --git a/src/BluetoothManager.ts b/src/BluetoothManager.ts index a6c107e..b7d29a8 100644 --- a/src/BluetoothManager.ts +++ b/src/BluetoothManager.ts @@ -14,6 +14,7 @@ export const MCM_SERVICE_UUID = "00002760-08c2-11e1-9073-0e8ac72e1001" export const MCM_WRITE_UUID = "00002760-08c2-11e1-9073-0e8ac72e0001" // Write Without Response export const MCM_NOTIFY_UUID = "00002760-08c2-11e1-9073-0e8ac72e0002" // Notify +export const MCM_INFO_UUID = "00002760-08c2-11e1-9073-0e8ac72e0004" // 只读:设备参数 TLV(同 INFO_RSP 全部项) export type MyApiResult = { type: number, @@ -51,6 +52,40 @@ let connListenerRegistered = false let scanListenerRegistered = false let notifyListenerRegistered = false let notifyDataCb: ((res: MyApiResult) => void) | null = null +let readPendingChar: string | null = null // 等待读结果的特征 UUID +let readCb: ((res: MyApiResult) => void) | null = null +let readTimer = 0 + +// 注册特征值变化监听(notify 数据与 read 结果都经此回调分发,幂等) +function ensureValueChangeListener() { + if (notifyListenerRegistered) return + notifyListenerRegistered = true + uni.onBLECharacteristicValueChange((res: any) => { + const charId = res.characteristicId as string + const bytes = new Uint8Array(res.value as ArrayBuffer) + const arr = [] as number[] + for (let i = 0; i < bytes.length; i++) { + arr.push(bytes[i]) + } + if (uuidEq(charId, MCM_NOTIFY_UUID)) { + const nd: NotityData = { data: arr, mac: res.deviceId } as NotityData + const c = notifyDataCb + if (c != null) { + c({ type: 0, message: "notify", data: nd } as MyApiResult) + } + return + } + if (readPendingChar != null && uuidEq(charId, readPendingChar)) { + const c = readCb + readCb = null + readPendingChar = null + clearTimeout(readTimer) + if (c != null) { + c({ type: 0, message: "read ok", data: arr } as MyApiResult) + } + } + }) +} function ok(message: string): MyApiResult { return { type: 0, message: message, data: null } as MyApiResult @@ -318,22 +353,7 @@ export function onNotityReadBleData(readNotification: boolean, cb: (res: MyApiRe return } notifyDataCb = cb - if (!notifyListenerRegistered) { - notifyListenerRegistered = true - uni.onBLECharacteristicValueChange((res: any) => { - if (!uuidEq(res.characteristicId as string, MCM_NOTIFY_UUID)) return - const bytes = new Uint8Array(res.value as ArrayBuffer) - const arr = [] as number[] - for (let i = 0; i < bytes.length; i++) { - arr.push(bytes[i]) - } - const nd: NotityData = { data: arr, mac: res.deviceId } as NotityData - const c = notifyDataCb - if (c != null) { - c({ type: 0, message: "notify", data: nd } as MyApiResult) - } - }) - } + ensureValueChangeListener() uni.notifyBLECharacteristicValueChange({ deviceId: id, serviceId: MCM_SERVICE_UUID, @@ -394,6 +414,40 @@ export function sendDataBytes(data: number[], writeType: number, cb: (res: MyApi } } +// ================= 读取 ================= + +// 读取设备参数特征(只读 MCM_INFO_UUID,内容为全部信息项 TLV,同 INFO_RSP(all))。 +// uni 的读结果经 onBLECharacteristicValueChange 回传;type==0 时 data 为 number[]。 +export function readInfoChar(cb: (res: MyApiResult) => void) { + const id = connectedDeviceId + if (id == null) { + cb({ type: 10001, message: "not connected", data: null } as MyApiResult) + return + } + ensureValueChangeListener() + readPendingChar = MCM_INFO_UUID + readCb = cb + readTimer = setTimeout(() => { + if (readCb != null) { + readCb = null + readPendingChar = null + cb({ type: 10008, message: "read timeout", data: null } as MyApiResult) + } + }, 3000) + uni.readBLECharacteristicValue({ + deviceId: id, + serviceId: MCM_SERVICE_UUID, + characteristicId: MCM_INFO_UUID, + success: () => {}, + fail: (err: any) => { + readPendingChar = null + readCb = null + clearTimeout(readTimer) + cb(failRes(10008, err)) + } + }) +} + // ================= 其他 ================= // 设置 mtu(仅 App 端有效;其他平台回调失败,调用方保持默认 20 字节切片) diff --git a/src/mcm/McmInfo.ts b/src/mcm/McmInfo.ts index 7bdbc71..352f9d4 100644 --- a/src/mcm/McmInfo.ts +++ b/src/mcm/McmInfo.ts @@ -5,6 +5,8 @@ */ import { queryInfo } from "./McmChannel" import { readU16LE, readI16LE, readU32LE } from "./FrameCodec" +import * as Ble from "../BluetoothManager" +import { MyApiResult } from "../BluetoothManager" // 信息项 id export const INFO_FW_VERSION = 0x01 // u32 固件版本号,如 0x00010001 = V1.00.01 @@ -74,6 +76,20 @@ export async function queryDeviceInfo(items: number[]): Promise { return parseTlv(payload) } +/** + * 读只读参数特征(MCM_INFO_UUID,内容为全部信息项 TLV,同 INFO_RSP(all))。 + * 不依赖 notify 通道;失败/超时返回各字段全 null 的 DeviceInfo。 + */ +export async function readDeviceInfo(): Promise { + const payload = await new Promise((resolve) => { + Ble.readInfoChar((res: MyApiResult) => { + resolve(res.type == 0 ? res.data as number[] : null) + }) + }) + if (payload == null) return emptyInfo() + return parseTlv(payload) +} + /** 固件版本号 u32 格式化,如 0x00010001 → "V1.00.01" */ export function formatVersion(v: number): string { const major = Math.floor(v / 65536) % 256