# 设备配网
对于有屏设备,一般选择自行入网即可,对于无屏设备,推荐使用小微提供的配网功能来完成配网和绑定。小微提供以下配网方式,以满足不同场景的需求。
- 腾讯小微小程序:蓝牙BLE配网,声波配网
- 腾讯小微APP:蓝牙BLE配网,声波配网,AP热点配网
# 蓝牙BLE配网
# 介绍
蓝牙BLE配网是最推荐的方式,该方式流程简单,用户体验好,若硬件设备支持蓝牙BLE,可采用该方式配网。简单来说,流程如下:
- 设备启动一个service并进行广播,声明自己的身份
- APP/小程序扫描到广播后与设备建立连接
- APP/小程序通过蓝牙将wifi信息传给设备
- 设备自行联网,此时配网流程已完成
- 配网成功后设备登录小微SDK,获取绑定ticket,并回传给APP/小程序
- APP/小程序根据ticket完成设备绑定
对于接入厂商来说,蓝牙配网需要厂商实现我们定义的配网协议,具体协议如下。
# BLE配网协议
# 1. 准备工作
使用小微蓝牙配网的设备,需要在小微官网注册设备时选择蓝牙配网方式,并且按照官网要求上传配网时的指示图片(该图片将在小程序/APP中显示,用于引导用户启动配网模式,如长按某按钮,等待语音或LED它提示等)。
# 2. 启动主Service
设备在进入正式配网流程前,需要先启动一个Service,该Service要包含下面三个特征:
# 2.1 ssid_info
内容 | 说明 |
---|---|
特证名 | ssid_info |
UUID | 需包含"A1CE",例如"0000A1CE-0000-1000-8000-00805f9b34fb" |
权限 | write(with response) |
说明 | 用于接收wifi信息 |
对应的数据结构如下代码所示:
typedef struct _SSID_Info{
unsigned short length; // 总长度 2byte
unsigned char ssidLength; // 无线名长度 1byte
unsigned char passwordLength;// 密码长度 1 byte
unsigned char ssid[1]; //无线名,UTF8编码 可变长
unsigned char password[1]; //无线密码,UTF8编码 可变长
}SSID_Info;
注意:如果此结构大于20字节,app/小程序端会分包发送,每个包最大20字节。为了保证分包发送正确,需要该特征有write(with response)权限。数据采用little endian的方式。例如以下接收wifiSSID信息的伪代码示例:
void onGetSsidInfoData(const char[] value, size_t length) {
if (length < 5 && !is_ssid_start) {
printf("data length error");
return;
}
if (!is_ssid_start) {
is_ssid_start = true;
ssid_total_len = value[0] + value[1]*256;
ssid_id_len = value[2];
ssid_password_len = value[3];
ssid = new char[ssid_id_len + 1];
memset(ssid, 0, ssid_id_len + 1);
ssid_password = new char[ssid_password_len + 1];
memset(ssid_password, 0, ssid_password_len + 1);
for (int i = 4; i < ssid_id_len + 4 && i<length; i++) {
ssid[ssid_id_offset++] = value[i];
}
if (ssid_id_len == ssid_id_offset) {
for (int i = 4 + ssid_id_len; i < ssid_id_len + 4 + ssid_password_len && i<value.length; i++) {
ssid_password[ssid_password_offset++] = value[i];
}
}
}
else {
int i = 0;
if (ssid_id_len > ssid_id_offset) {
int left = ssid_id_len - ssid_id_offset;
for(; i < left && i < length; i++) {
ssid[ssid_id_offset++] = value[i];
}
}
if (ssid_id_len == ssid_id_offset) {
for(; i < length; i++){
ssid_password[ssid_password_offset++] = value[i];
}
}
}
if (ssid_password_len == ssid_password_offset && ssid_id_len == ssid_id_offset) {
is_ssid_finish = true;
// todo, connect wifi and init xiaoweiSDK
}
}
# 2.2 xiaowei_specify
内容 | 说明 |
---|---|
特证名 | xiaowei_specify |
UUID | 需包含"6196" |
权限 | read |
说明 | 用于传输绑定信息,为了与老协议兼容,有一些无效字段,同样little endian |
对应的数据结构如下代码所示:
typedef struct _Xiaowei_Spec{
unsigned short length; // 总长度 2byte
unsigned char snLength; // sn长度 1byte,固定为0
unsigned char ticketLength;// ticket长度 1byte
unsigned char sn[1]; //sn,UTF8编码 可变长,固定为空
unsigned char ticket[1]; //token,UTF8编码 可变长
}Xiaowei_Spec;
注意,这里的ticket是需要联网成功之后,登录小微SDK,然后利用int device_get_bind_ticket(bind_ticket_callback callback);
接口来获取。获取成功后刷新这个Spec的值,同时通知APP/小程序来读取。
# 2.3 link_state
内容 | 说明 |
---|---|
特证名 | link_state |
UUID | 需包含"A007" |
权限 | notify |
说明 | 通知app/小程序,联网和ticket获取进度 |
对应的数据结构如下:
notifyState(1byte,值定义见下面枚举) + msg(可选, utf8 string)
enum XWLink_Notify{
XWLink_unkown = 0,
XWLink_SSID_Received = 1, //收到配网信息(可选)
XWLink_Linking = 2, //配网中(可选)
XWLink_LinkSuccess = 3, //success、failed和wifiErr必须有一个通知
XWLink_LinkFailed = 4, //综合原因的失败,如无法获取ticket等
XWLink_LinkWiFiErr = 5 //Wifi无法连接导致的失败
};
当配网成功后,不要立即通知XWLink_LinkSuccess
,应在成功获取到ticket后再进行通知,一旦发出通知,APP/小程序就会尝试读取上述的xiaowei_specify
,若读取失败,会提示配网失败。
为了提升用户体验,设备若能确定wifi无法连接,应notify XWLink_LinkWiFiErr
。此时APP/小程序会提示用户修改wifi信息。
# 3. 设备发起广播
设备进入配网流程后,首先需要发起广播,广播数据:
广播字段 | 值 |
---|---|
Service UUID | 包含FFB1,如"0000FFB1-0000-1000-8000-00805f9b34fb" |
Manufacture Data | 厂商ID(2byte)+pattern("xiaoweilink"字符串,11byte)+version(uchar,1byte,当前协议版本号为1)+设备pid(uint32,4byte,little endian) |
注意:根据蓝牙规范,厂商自定义的数据中,前两个字节表示厂商ID,剩下的是厂商自定义数据,厂商实现时不要漏掉厂商ID。
一个合法的Manufacture Data如:"xwxiaoweilink\1a010",在这里,厂商id是"xw",设备pid是'a' << 0 + '0' << 8 + '1' << 16 + '0' << 24
,注意这里显示的'0',其实是十进制的30。
假如productId 为 16909060 ,16进制为0x01020304。那么广播包最后4个字节应该是0x04 0x03 0x02 0x01,这些字符是显示不出来的。
APP/小程序在配网页面会扫描蓝牙广播,若匹配成功,则会向上述的ssid_info特证中写数据来完成wifi信息的传输。
# 4. 总结
配网时序如下:
- 小微设备->小微App: 广播数据
- 小微App->小微App: 检查Manufacture Data
- 小微App->小微设备: 写入配网信息
- 小微设备->小微设备: 自行联网,并登陆SDK,获取ticket
- 小微设备->小微App: 通知App联网成功
- 小微App->小微设备: 读取ticket
- 小微App->小微App: 尝试绑定设备
# 声波配网
# 介绍
声波配网是以特定频率的声音作为信息载体,由小微App/小程序将某个路由器的SSID和密码同步给设备。这个技术类似无线广播,在小微App/小程序端,会将ssid_info进行软调制,以高频声波作为载波,将信息广播出去;在设备端通过麦克风接收声波并软解调,从而获取wifi信息。
该方法对设备的收音性能有要求,需要在配网时保持安静,可作为蓝牙BLE配网失败时的备选方案。小微SDK内置了声波配网解调模块,设备端使用此功能时,初始化解调模块并将音频数据送入,即可获取解调后的信息。
与BLE配网的不同之处在于,设备端无法再向APP/小程序端传递数据,因此配网协议略有不同,总体可以概括为:在APP/小程序向设备端发送ssid_info的同时,还会携带一个token,用于标记这一次配网。设备配网并获取ticket成功后,不再直接通知APP/小程序,而是将token和ticket组成一个键值对,通过SDK的接口发送至小微后台,小微后台会完成绑定,并通知APP/小程序配网成功。
# 接口
# 1. 初始化SDK
int voicelink_init_decoder(VL_FUNC_NOTIFY func, int samplerate);
调用该接口初始化配网模块,设置通知回调和采样率。注意这里设置的采样率一定要和设备真实的录音采样率一致。
# 2. 持续采音并fill buffer。
void voicelink_fill_audio(signed short *audio, int nlen);
每当采集一定的音频数据后,调用此接口将数据传给SDK,建议每帧数据长度为20ms,数据过长会导致失败。这里的nlen是采样点数,而不是长度。例如采样率8000,20ms数据的nlen就是160。
持续调用此接口直到VL_FUNC_NOTIFY
到来。
# 3. 获得wifi信息和token并联网
初始化时设置的回调函数中的VL_FUNC_NOTIFY
包含了wifi信息和token。设备自行使用wifi信息联网,联网成功后登录小微SDK,登录成功后调用int device_get_bind_ticket(bind_ticket_callback callback);
获得ticket。
# 4. 通知小程序/App配网结果,并完成绑定
device_upload_voice_link_result(const char *ticket, const char *token);
调用此接口将上述ticket和token上传。
# 总结
配网时序如下:
- 设备进入声波配网模式,初始化SDK的decode接口,开始收音,并将音频数据持续输入SDK的decode接口。
- 小程序/App进入声波模式,播放声音。
- SDK decode成功,设备拿到
wifi_ssid
,wifi_password
和token
。 - 设备使用
wifi_ssid
和wifi_password
自行联网。 - 设备联网成功后登录SDK,成功之后获取ticket。
- 设备调用SDK接口将ticket和token上传后台,至此设备端工作结束。
- 小程序/App收到后台消息,完成绑定。
- 配网流程结束。
# 蓝牙和声波配网协同
在小微小程序/App的交互流程中,会优先使用蓝牙配网,配网失败后使用声波配网。在设备端若支持声波配网,应同时打开声波配网和蓝牙配网,二者有一个成功之后便退出配网流程即可。需要注意以下几点:
- 配网成功是指设备拿到wifi信息并且成功login,拿到ticket并且成功将ticket给到小程序/App(蓝牙)或后台(声波)。
- 两种方式同时打开时,只需要获取一次ticket,这个ticket对于两种方式均有效,重复get ticket会导致之前的ticket失效。