小程序配网流程
# 小程序蓝牙配网
蓝牙低功耗是从蓝牙 4.0 起支持的协议,与经典蓝牙相比,功耗极低、传输速度更快,但传输数据量较小。常用在对续航要求较高且只需小数据量传输的各种智能电子产品中,比如智能穿戴设备、智能家电、传感器等,应用场景广泛。
# 蓝牙名称
蓝牙名称规范参考 (opens new window),作用是用于搜索蓝牙设备时能够搜索固定产品的蓝牙设备,要求名称命名具有唯一性
- 命名规则
- 产品线:使用英文缩写或全称,首字母大写。例如:XYY(喜羊羊)、SW(超飞)、EGGY(蛋仔)、NALOONG(奶龙)
- 方案商:使用英文缩写或全称,首字母大写。例如:ES(ESP-32 乐鑫)、AN(Android)、AC(AC79XX 杰理)、STM(STM 意法半导体)
- 特性版本:使用简短的英文单词或缩写表示产品的主要特性。例如:S(Standard标准版)、U(Ultra专业版)、L(Lite轻量版)、P(Plus增强版)
- 蓝牙名称结构:
产品线
-方案商
-特性版本
-SN
, 例如:XYY-AC-S-AF1234766BQ103D
# Device UUID
Device UUID也可以被称作为DeviceID
Android 设备上扫描获取到的 deviceId 为外围设备的 MAC 地址,相对固定
iOS 设备上扫描获取到的 deviceId 是系统根据外围设备 MAC 地址及发现设备的时间生成的 UUID,是设备上的Core Bluetooth为该设备分配的标识符。对于已连接过的设备,UUID 会在一段时间内保持不变(正常是15-20分钟变化一次)。此外,UUID 也会在某些条件下可能会发生变化(如系统蓝牙模块重启、配对设备被忽略等),在不同的设备上获取到的 UUID 也是不同的。不同手机连接同一设备的uuid不是同一个。
# Service UUID
- 蓝牙设备的ID,通过此ID可以进行连接蓝牙设备,连接成功之后可以获取serviceUUID,serviceUUID可以有多个,每个serviceUUID可以看作是服务,每个serviceUUID中可以定义多个characteristics uuid(特征值)
- Service UUID已经有了很多特定的,也可以自己定义。特定的:{0000xxxx-0000-1000-8000-00805F9B34FB}
- xxxx = 0x0000 ~ 0xFFFE
# characteristics UUID
- characteristic特征值,ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容
# BLE蓝牙配网(蓝牙传输协议)
小程序与设备端蓝牙通讯协议以这个为标准:蓝牙传输协议 (opens new window)
# 小程序端->设备端
# 包结构
- 包头(1字节):固定位0x55
- 包类型(1字节):用于标识数据包的类型
- 包序号(1字节):用于标识数据包的序号,下标从0开始,用于在接收端重组数据包。
- 总包数(1字节):用于标识数据包总数,用于在接收端重组数据包。
- 数据包长度(1字节):当前包有效载荷长度。
- 数据包(可变长度,最大14字节):有效负载数据包。
- 校验位(1字节):计算包头及有效载荷的校验值,用于接收端验证数据包的完整性。
- 校验位计算:~(包类型+包序号+包总数+数据长度+数据包) & 0xFF
包头(1字节) | 包类型(1字节) | 包序号(1字节) | 包总数(1字节) | 数据长度(1字节) | 数据包(可变长度) | 校验位 |
---|---|---|---|---|---|---|
Data0 | Data1 | Data2 | Data3 | Data4 | Data5-N | DataN+1 |
固定为0x55 | 请查看蓝牙传输协议 (opens new window) | 下标从0开始 | 分包总数 | 负载包长度 | 1-14字节 | ~(Data1+Data2+Data3+Data4+Data5+DataN) & 0xFF |
# 配网步骤
- 初始化蓝牙适配器
- 通过DeviceID连接蓝牙设备
- 连接蓝牙后获取设备的Service UUID,需要延迟一下,防止连接成功立即获取服务号获取不到**(延迟1s)**
- Service UUID拿到后获取设备的characteristics UUID
- 先监听蓝牙发送的内容
notifyBLECharacteristicValueChange
- 蓝牙监听成功后再发送数据
writeBLECharacteristicValue
- 小程序不会对写入数据包大小做限制,但系统与蓝牙设备会限制蓝牙 4.0 单次传输的数据大小,超过最大字节数后会发生写入错误,建议每次写入不超过 20 字节
- 小程序需要发送数据
- ssid:Wi-Fi名称
- pwd:Wi-Fi密码
- user_uuid:用户的UUID
- 绑定成功后更新设备配置的deviceId才跳转到连接成功页面。作用: 用来存储当前绑定设备的deviceId,下次直接进入小程序首页已有蓝牙设备绑定直接通过deviceId建立连接
# 设备端->小程序端
# 包结构
- 包头(1字节):固定位0x56。
- 包类型(1字节) : 当前ACK包对应的包类型,参考包类型表
- 状态码(1字节):状态码,由通用状态码和定制状态码组成
- 包序号(1字节):发送端需要重发的数据包序号。
- 校验位(1字节):计算包头及有效载荷的校验值,用于接收端验证数据包的完整性。
包头(1字节) | 包类型(1字节) | 状态码(1字节) | 包序号(1字节) | 校验位 |
---|---|---|---|---|
Data0 | Data1 | Data2 | Data3 | Data4 |
固定为0x56 | 详情请查看下方通讯码表 | 请查看 通用状态码表 和 | 需要重发的数据包序号 | ~(Data1+Data2+Data3) & 0xFF |
# 配网步骤
- 设备端需要设置一个固定的Service UUID,为后续能够获取特定的characteristics UUID做准备
- Service UUID的属性isPrimary 需设置为true
- Service UUID拿到后获取设备的characteristics UUID
- 一个读的特征值UUID(readCharacteristic_UUID):属性值indicate、notify 、read 必须为true,小程序才能监听到设备的消息
- 一个写的特征值UUID(writeCharacteristic_UUID):属性值write必须为true
- 接收ssid、pwd、user_uuid,需要做以下表格相应信息的回复,小程序上相应的配网文案打勾交互才有反馈
- 绑定成功后还需要做OTA更新结果上报
注意:OTA更新结果上报成功后才返回绑定成功状态码给到小程序
包名称 | 包类型值 | 状态码 | 描述 |
---|---|---|---|
wifi_status | 0x03 | 0x30 完整设备接收wifi账号密码 0x31 wifi信息错误 报错连接失败、账号密码错误 0x32 wifi连接中 0x33 连接网络成功 0x34 连接网络失败 | wifi状态 |
uesr_binding_status | 0x05 | 0x35 用户绑定成功 0x36 用户绑定失败 0x37 用户绑定中 | 用户绑定状态 |
# BLE蓝牙配网(羊羊太空舱)
# 小程序端->设备端
传递给设备的具体参数信息:
ssid:输入的WiFi名称
openid:用户的openid
pwd:输入的WiFi密码
accountBind:联网绑定字段,0=>关闭 1=>打开
具体规则如下
第一包包头(1字节) | 中间包首包(1个字节) | 包尾首包(1个字节) | 数据包(可变长度) |
---|---|---|---|
Data0 | Data1 | Data2 | Data3 |
固定为0x01 | 固定为0x02 | 固定为0x00 | 不足长度的包固定为0x00 |
const START_BYTE = 0x01
const CONTINUE_BYTE = 0x02
const END_BYTE = 0x00
// 字符串转二进制编码(羊羊太空舱)
export function textToBinary(strData) {
try {
const originData = unescape(encodeURIComponent(strData))
.split('')
.map((val) => val.charCodeAt())
const MAX_SIZE = 18 // 分包最大发送数据包的长度
const size = Math.ceil(originData.length / MAX_SIZE)
const data = new Array(size)
let start = 0
let end = 0
let index = 0
while (index < size) {
index++
if (index === size) {
data[index - 1] = new Uint8Array(MAX_SIZE + 1)
data[index - 1][0] = END_BYTE
} else if (index === 1) {
data[index - 1] = new Uint8Array(MAX_SIZE + 1)
data[index - 1][0] = START_BYTE
} else {
data[index - 1] = new Uint8Array(MAX_SIZE + 1)
data[index - 1][0] = CONTINUE_BYTE
}
end = Math.min(start + MAX_SIZE, originData.length)
data[index - 1].set(originData.slice(start, end), 1)
start = end
}
return data
} catch (e) {
console.error(e)
}
return null
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 设备端->小程序端
设备端需要设置一个固定的Service UUID,为后续能够获取特定的characteristics UUID做准备
Service UUID的属性isPrimary 需设置为true
Service UUID拿到后获取设备的characteristics UUID
- 一个读的特征值UUID(readCharacteristic_UUID):属性值indicate、notify 、read 必须为true,小程序才能监听到设备的消息
- 一个写的特征值UUID(writeCharacteristic_UUID):属性值write必须为true
接收ssid、pwd、user_uuid,需要做以下表格相应信息的回复,小程序上相应的配网文案打勾交互才有反馈
绑定成功后还需要做OTA更新结果上报
注意:OTA更新结果上报成功后才返回绑定成功状态码给到小程序
配网流程中发送以下类型的单个状态码包给小程序
状态码 | 描述 |
---|---|
0x30 wifi信息错误 报错连接失败、账号密码错误 0x31 密码输入正确的时候需要进行校验 0x32 连接网络成功 0x33 连接网络失败 0x37 完整设备接收wifi账号密码 | wifi状态 |
0x34 用户绑定成功 0x35 用户绑定失败 0x36 用户绑定成功(已用同wifi配过一次网返回该状态码) | 用户绑定状态 |
# 小程序端蓝牙配网注意事项
- 等待响应:很多情况下需要等待设备响应,尤其在
IOS
环境下,比如- 监听到蓝牙开启后,不能马上开始搜索,需要等待2秒
- 连接蓝牙后获取设备的Service UUID,需要等待1秒,防止连接成功立即获取服务号获取不到
- 开启
notify
以后,不能马上发送消息,需要等待1秒
Mac
和UUID
:安卓的MAC
地址是可以获取到的所以设备的ID是固定的,但是IOS
是获取不到MAC
地址的,只能获取设备的UUID
,而且是动态的,所以需要使用其他方法来查询。IOS
下只有搜索可以省略,如果你知道了设备的ID,服务ID和各种特征值ID,在安卓下可以直接连接,然后发送消息,省去搜索设备,搜索服务和搜索特征值的过程,但是在IOS
下,只能指定设备ID
连接,后面的过程是不能省略的**(搜索服务和搜索特征值的过程,不然连接不上设备的蓝牙**)。- 监听到的消息要进行过滤处理,有些设备会抽风一样的发送同样的消息,需要在处理逻辑里面去重。
# 小程序声波配网
声波配网,即通过手机发出声波,将ssid、password等信息传给设备的一种配网方式。适用于没有触屏或触屏较小不易于信息输入,但是拥有麦克风的智能设备,如智能音箱、智能家庭助手等。其优点是配网速度快、可人耳感知,缺点是受环境干扰较大。
声波配网案例 (opens new window)该库仅用于生成和分析从音频设备(扬声器、麦克风等)播放和捕获的原始波形。只要您提供用于音频样本排队和出队的回调,您就可以自由使用任何音频后端(例如 PulseAudio、ALSA 等)。
实现声波配网,首先需要一套特定的算法库,算法库分小程序端和设备端两部分。小程序端算法库将ssid信息由字符串转化为声音信号,然后将声音信号通过音频模块播放出来。同时,设备端录下这一段声音,然后用同一套算法库将声音信息解析出来,还原成原来的ssid信息(字符串),最后用解析到的ssid信息用于连接wifi。
# 小程序->设备端
一段声波最大发送140个字符,节省字符使用
UTF-8
进行Base64
编码播放完一轮声波后查询设备绑定状态,声波播放结束查询用户是否绑定成功,未绑定成功,则继续播放声波
传递给设备的具体参数信息
s
为SSID
:wifi
名称p
为pwd
:wifi
密码si
为sessionId
:抢占式配网(随机生成的6为uuid字符串)u
为uuid
:小程序的unionId
(用户的uuid)
发送方式
- 使用 UTF-8 进行 Base64 编码
- 分三个包进行发送
s
p
、si
u
// 生成随机的唯一标识符
export function generateUniqueID(length) {
let result = ''
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length)
result += characters.charAt(randomIndex)
}
return result
}
// 使用 UTF-8 进行 Base64 编码
export function _utf8_encode(str) {
const utf8 = unescape(encodeURIComponent(str))
const buffer = new ArrayBuffer(utf8.length)
const view = new Uint8Array(buffer)
for (let i = 0; i < utf8.length; i++) {
view[i] = utf8.charCodeAt(i)
}
return uni.arrayBufferToBase64(buffer)
}
const deviceParams1 = {
s: wifiForm.wifiSSID
}
const deviceParams2 = {
p: wifiForm.wifiPWD,
si: generateUniqueID(6)
}
const deviceParams3 = {
u: uni.getStorageSync('userInfo').uuid
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 设备端->小程序
声波配网案例 (opens new window)该库仅用于生成和分析从音频设备(扬声器、麦克风等)播放和捕获的原始波形。
- 接收三个包的数据,都获取到后执行绑定接口
s
p
、si
u
- 绑定成功后还需要做OTA更新结果上报
注意:OTA更新结果上报成功后才返回绑定成功状态码给到小程序
配网流程中发送以下类型的单个状态码包给小程序
状态码 | 描述 |
---|---|
0x30 wifi信息错误 报错连接失败、账号密码错误 0x31 密码输入正确的时候需要进行校验 0x32 连接网络成功 0x33 连接网络失败 0x37 完整设备接收wifi账号密码 | wifi状态 |
0x34 用户绑定成功 0x35 用户绑定失败 0x36 用户绑定成功(已用同wifi配过一次网返回该状态码) | 用户绑定状态 |