docs(wiki): 记录 Veepoo M2 BLE SDK 对接踩坑和正确流程
- wiki/index.md 症状导航新增 5 条:??语法错误、useRef白屏、扫描匹配、认证超时三层根因 - wiki/index.md 关键数字更新:Git 提交 1061 次,小程序描述补充原生分包页面 - docs/discussions/ 新增 SDK 对接流程文档: - 连接回调 4 次触发机制(只响应 connection:true) - veepooWeiXinSDKNotifyMonitorValueChange 必须在连接后注册 - VPDeviceAck vs VPDevicepassword 字段区别 - deviceChipStatus 布尔值兼容 - 完整踩坑清单(9 项)
This commit is contained in:
206
docs/discussions/2026-05-30-veepoo-sdk-integration-flow.md
Normal file
206
docs/discussions/2026-05-30-veepoo-sdk-integration-flow.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# 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 文件
|
||||
Reference in New Issue
Block a user