# Veepoo M2 BLE SDK 正确对接流程 > 日期: 2026-05-30 | 参与者: iven, Claude > 状态: 已验证通过 ## 背景 Veepoo M2 手环的 BLE SDK(372KB Webpack 打包)在微信小程序中的对接遇到了多层问题。 本文档记录**经过实际验证的正确对接流程**和踩过的坑,避免后续开发者重复踩坑。 ## SDK 架构 ``` Taro 页面 (veepoo-measure/index.tsx) ↓ navigateTo 原生分包页面 (pkg-veepoo/index.js + index.wxml) ↓ require('./libs/veepoo-sdk') Veepoo SDK (veepoo-sdk.js) ↓ wx.* BLE API 微信蓝牙底层 ``` ### 为什么用原生分包而非 Taro? 1. SDK 是纯 JS(372KB Webpack CommonJS2),不兼容 Taro 编译流程 2. SDK 使用全局变量 `veepooBle`/`veepooFeature`/`veepooLogger`,需要 `require()` 直接加载 3. 微信小程序 JS 引擎不支持 ES2020 语法(`??`、`?.`),原生页面可精确控制语法 4. 分包独立,不影响主包体积 ### 构建集成 ```js // config/index.ts — mini.copy.patterns { from: 'vendor/veepoo-sdk/libs/vp_sdk/index.js', to: 'dist/pkg-veepoo/libs/veepoo-sdk.js' }, { from: 'native/pkg-veepoo/', to: 'dist/pkg-veepoo/', ignore: ['*.ts'] }, ``` **注意**:`dev:weapp` 的 watch 模式不监听 `native/` 目录变化,修改原生页面后需清除 dist 重建: ```bash rm -rf apps/miniprogram/dist/pkg-veepoo pnpm run build:weapp # 或 pnpm run dev:weapp ``` ## 正确对接流程(已验证) ### 1. 页面加载(onLoad) ```js onLoad: function () { this._eventChannel = this.getOpenerEventChannel(); // ❌ 不要在这里注册 veepooWeiXinSDKNotifyMonitorValueChange! // 该函数内部会调用 wx.notifyBLECharacteristicValueChange // 此时蓝牙适配器未初始化 → 返回 "not init" 错误 } ``` ### 2. 扫描 ```js veepooBle.veepooWeiXinSDKStartScanDeviceAndReceiveScanningDevice(function (res) { var name = (device.localName || device.name || '').toUpperCase(); // 匹配条件要放宽:M2 / VPM / VEEPOO if (name.indexOf('M2') !== -1 || name.indexOf('VPM') !== -1 || name.indexOf('VEEPOO') !== -1) { // 找到设备 } }); ``` ### 3. 停止扫描 → 连接 ```js veepooBle.veepooWeiXinSDKStopSearchBleManager(function () { veepooBle.veepooWeiXinSDKBleConnectionServicesCharacteristicsNotifyManager(device, callback); }); ``` ### 4. 连接回调(关键!) **连接回调触发 4 次**,每个阶段一次: | # | 回调内容 | 含义 | |---|---------|------| | 1 | `{errno:0, errMsg:"createBLEConnection:ok"}` | BLE TCP 连接建立 | | 2 | `[{uuid:...}, ...]` | 服务发现完成 | | 3 | `{characteristics:[...], errno:0}` | 特征值发现完成 | | 4 | `{connection:true, deviceId:"..."}` | **特征值订阅完成,通道就绪** | **只响应第 4 次 `connection:true`**: ```js veepooBle.veepooWeiXinSDKBleConnectionServicesCharacteristicsNotifyManager(device, function (result) { // ❌ 不要用 errno===0 匹配!第 1 次回调就满足,但此时订阅未完成 // ✅ 只匹配 connection:true(第 4 次回调) if (result.connection === true) { // 此时 BLE 通道完全就绪 } }); ``` ### 5. 注册数据监听器(在 connection:true 回调内) ```js if (result.connection === true) { // ✅ 此时蓝牙适配器已初始化 + 连接已建立 + 特征值已订阅 veepooBle.veepooWeiXinSDKNotifyMonitorValueChange(function (data) { // data.type 对应不同事件:1=认证, 2=电量, 6=体温, 18=血压, 31=血氧, 51=心率, 58=压力 handleSdkEvent(data); }); // 同时注册连接状态变化监听 veepooBle.veepooWeiXinSDKBLEConnectionStateChangeManager(function (res) { if (!res.connected) { /* 断开处理 */ } }); } ``` ### 6. 认证(连接就绪后延迟 500ms) ```js setTimeout(function () { veepooFeature.veepooBlePasswordCheckManager(); }, 500); ``` ### 7. 认证结果判断(关键!) ```js // SDK 认证回调结构: // { // type: 1, // content: { // VPDevicepassword: "0000", ← 设备密码原始值 // VPDeviceAck: "successfulVerification", ← ✅ 认证结果 // VPDeviceVersion: "01.63.01.00-7466", // VPDeviceMAC: "BC:92:DC:9F:CA:6A", // ... // } // } // ❌ 错误:检查 VPDevicepassword(值是 "0000",永远不匹配) if (content.VPDevicepassword === 'successfulVerification') { ... } // ✅ 正确:检查 VPDeviceAck if (content.VPDeviceAck === 'successfulVerification' || content.VPDeviceAck === 'passTheVerification') { // 认证成功 } ``` **VPDeviceAck 可能的值**: - `successfulVerification` — 密码和时间校验成功 - `passTheVerification` — 核验通过 - `verifyNotPass` — 核验不通过 - `setupFailed` — 设置不成功 ### 8. Storage 轮询兜底 SDK 会写入 `deviceChipStatus` 到 Storage,但**可能是布尔值 `true` 而非字符串**: ```js var status = wx.getStorageSync('deviceChipStatus'); // ✅ 同时匹配字符串和布尔值 if (status === 'successfulVerification' || status === 'passTheVerification' || status === true) { // 认证成功 } ``` ## 完整流程图 ``` onLoad → 扫描 → 找到M2 → 停止扫描 ↓ 连接(veepooWeiXinSDKBleConnectionServicesCharacteristicsNotifyManager) → 回调1(errno:0) → 忽略 → 回调2(services) → 忽略 → 回调3(characteristics) → 忽略 → 回调4(connection:true) → ① 注册数据监听器(veepooWeiXinSDKNotifyMonitorValueChange) ② 注册连接状态监听器 ③ 延迟500ms → 调用认证(veepooBlePasswordCheckManager) ④ 启动 Storage 轮询(deviceChipStatus) + 8s 超时 ↓ 数据监听器收到 type=1 事件 → 检查 VPDeviceAck === "successfulVerification" ↓ 认证成功 → 设备就绪 → 可开始测量 ``` ## 踩坑清单 | # | 问题 | 根因 | 解决方案 | |---|------|------|----------| | 1 | 原生页面 `??` 报 SyntaxError | 微信小程序 JS 引擎不支持 ES2020 | 用 `!= null` 三元表达式替代 | | 2 | veepoo-measure 白屏 `useRef is not defined` | TSX import 未解构 `useRef` | `import React, { useRef } from 'react'` | | 3 | 扫描不到 M2 设备 | 过滤条件只匹配 `M2`,设备可能广播其他名 | 放宽匹配 M2/VPM/VEEPOO | | 4 | 认证超时 — 回调匹配过早 | `errno:0` 在第 1 次回调就匹配 | 只匹配 `connection:true` | | 5 | 认证超时 — 监听器注册过早 | `onLoad` 时适配器未初始化 → `not init` | 改到 `connection:true` 后注册 | | 6 | 认证超时 — 字段检查错误 | 检查 `VPDevicepassword`(值="0000")而非 `VPDeviceAck` | 改为检查 `VPDeviceAck` | | 7 | `deviceChipStatus` 轮询失败 | SDK 写入布尔值 `true` 而非字符串 | 同时匹配字符串和布尔值 | | 8 | `vibrateShort` promise rejection | DevTools 不支持 `type` 参数,try/catch 无法捕获异步 rejection | 改用 `.catch()` | | 9 | dist 不更新 | `dev:weapp` watch 不监听 `native/` 目录 | 修改原生页面后需 `rm -rf dist/pkg-veepoo` 重建 | ## dev:weapp 注意事项 - 原生页面修改后**不会自动热更新**,需手动清除 dist 重建 - DevTools 日志需要选中正确的页面上下文(`pkg-veepoo`)才能看到原生页面日志 - 关闭 DevTools 后重新打开,确保加载最新的 dist 文件