APP设备标准交互协议
设备发现
对于那些需要与得力E+APP进行点对点连接的智能设备,例如通过蓝牙或者WIFI热点广播等方式直连的设备,APP需要设备的蓝牙名称或者WIFI热点名称(统称“广播名称”)符合以下规范:
-
对于不支持通过其他方式(例如扫二维码)获取到设备ID的设备,请命名设备广播名称为:DELI_{产品型号}。例如D2蓝牙考勤机的蓝牙名称可以为DELI_D2;
-
对于可以通过其他方式获取都设备ID的设备,请命名设备广播名称为:DELI_{产品型号}_{设备ID后6位},如果设备SN不足6位,请左补零。例如打印机型号为PT的设备ID为PT_12345678,则可以设置广播名称为DELI_PT_345678。
如果APP能够通过扫码等其他方式获取到设备ID,会优先通过第二种方式查找符合规范的设备热点。如果APP扫描到多个符合规范的设备列表,用户需要自行完成进一步的筛选。
一旦确定设备热点,APP会自动按照蓝牙或Wifi热点标准连接方式连接设备。
发现流程图如下所示:
st=>start: 开始
e=>end: 结束
sub0=>operation: 搜索DELI_{产品型号}
sub1=>operation: 搜索DELI_{产品型号}_{设备ID后6位}
sub2=>operation: 从设备列表选择
sub3=>operation: 连接设备
cond0=>condition: 扫码获得设备ID?
cond1=>condition: 发现多台设备?
st->cond0(right)
cond0(yes)->sub1->sub3
cond0(no)->sub0->cond1
cond1(yes,right)->sub2->sub3
cond1(no)->sub3
sub3->e
消息通信
APP与设备连接成功后,根据连接方式不同其通信方式和消息格式不同。考虑到APP版本兼容性的问题,推荐设备供应商按照以下标准通信方式接入。
蓝牙通信
对于蓝牙等点对点连接的设备,采用以下统一的消息报文格式进行后续通信,消息报文结构示例如下:
包头 | 消息指令 | 负载长度 | 负载数据 | 校验位 |
---|---|---|---|---|
0x40444cfa | 0x00 | 0x0004 | 0x68656c64 | 0x6b |
各部分数据说明如下:
参数 | 长度 | 说明 |
---|---|---|
包头 | 4字节 | 固定为0x40444cfa |
消息指令 | 1字节 | 具体的消息指令类型 |
负载长度 | 2字节 | 负载的内容长度N,单位为字节byte |
负载数据 | N字节 | 具体的负载内容,以UTF-8格式编码 |
校验位 | 1字节 | 报文完整性校验值。计算公式为报文内每个byte相加的和取低位字节。按照示例报文计算为:0x40+0x44+0x4c+0xfa+0x00+0x04+0x68+0x65+0x6c+0x64=0x36b, 校验位为0x6b |
后续所有按照该标准通信格式的交互指令均按照此规范格式进行通信。另外,对于某些存在单次发送固定报文长度需求的通信方式(例如蓝牙4.0要求每个包固定20个字节),负载数据可以通过填充0x00字节的方式来适配。
如果通信指令请求出现异常,设备可通过指令0x00向APP响应异常错误信息,响应负载内容如下:
错误代码 | 错误描述 |
---|---|
1个字节 | N-1字节 |
其中,预定义的错误代码有:
错误代码 | 描述 |
---|---|
0x01 | 报文解析出错 |
0x02 | 校验位计算错误 |
0x03 | 不能识别的指令 |
下面就具体的指令进行详细说明。
设备信息读取
APP可通过指令0x01向设备请求设备基本信息,请求负载为空。设备收到该指令后,应返回设备ID和产品型号信息,消息指令不变,负载内容如下:
设备ID长度 | 设备ID | 产品型号长度 | 产品型号 |
---|---|---|---|
1字节 | ... | 1字节 | ... |
该指令可用于在无法通过其他方式获取设备ID时使用。
设备验证
对于不支持联网的蓝牙设备,其合法性验证过程发生由APP端完成。整个验证流程如下:
-
APP生成随机字符串,并通过指令0x02请求签名校验。举例签名字符串为hello(16进制为0x68656c6c6f),则发送的完整报文内容为:0x40444cfa01000568656c6c6fe4。
-
设备收到指令,使用该随机签名字符串按照以下计算公式生成一个签名结果并返回APP,同时返回设备ID和产品型号。
sig = CRC32(${设备ID} + "-" + ${随机签名字符串} + "-" + ${产品秘钥})
CRC32校验算法请参考https://baike.baidu.com/item/CRC32/7460858。
返回负载数据内容的格式如下:
签名结果长度 签名结果 设备ID长度 设备ID 产品型号长度 产品型号 1字节 ... 1字节 ... 1字节 ... -
APP将该设备ID、产品型号、随机签名字符串以及签名结果信息发送给平台后端进行验证。如果平台后端按照相同的算法验证结果一致,则证明设备是合法的。
participant 设备
participant APP
participant 平台
APP->设备: 发送0x02签名指令
设备->APP: 返回签名结果
APP->平台: 验证设备签名结果
平台->APP: 返回验证结果
设备配网
通过消息指令0x03得力E+APP可以将WIFI账户、密码信息传递给蓝牙设备,实现设备联网配置。
由于明文广播的安全性没有保障,而且如果周围有多台处于配网状态的蓝牙设备可能造成误配网,因此需要对WIFI广播信息进行加密处理,只有对应产品型号的设备可以解密该配网信息进行网络连接。
配网信息采用XOR加密算法加密wifi密码信息,SID仍采用明文传输。具体加密密码的生成规则如下:
加密密码 = ('DELI@' + 明文WIFI密码) XOR 产品密钥
其中, 产品密钥
为该设备对应产品型号的接入密钥,由平台通过开发者平台生成并分配给设备厂商。设备收到该加密信息后,通过产品密钥再次XOR解密出明文密码,如果解密出来的密码以'DELI@'
开头(密码标示),则认为解密成功,设备可去掉该密码标示信息取得明文密码,进行网络连接,否则直接忽略。
需要注意的是,这里的异或算法规则是,用待加密的字符串的每个字节去和产品密钥的每个字节进行异或,产生最终的加密密码字节流。一段Java示例代码如下:
//src = ('DELI@' + 明文WIFI密码)
//key = 产品密钥
byte[] srcBytes = src.getBytes();
byte[] keyBytes = key.getBytes();
for (int i = 0, size = srcBytes.length; i < size; i++) {
for (byte keyByte : keyBytes) {
srcBytes[i] = (byte) (srcBytes[i] ^ keyByte);
}
}
发送数据的负载内容结构如下:
Wifi SID长度 | Wifi SID | Wifi加密密码长度 | Wifi加密密码 |
---|---|---|---|
1字节 | ... | 1字节 | ... |
注意,对于开放的WIFI热点,由于不需要密码认证,请直接将Wifi加密密码长度设置为0,不需要进行密码加密操作。
设备配网结果
为了及时获得设备的配网结果,在APP进行设备配网时,如果APP也连接到了相同的Wifi路由器,APP会在端口24333监听UDP广播。设备配网结束后,如果连接路由器成功(不论是否能连接平台),设备应发送UDP广播通知APP配网结果,通知消息指令也是0x03,负载信息包括: 设备ID、产品型号、设备状态、错误消息(可选)。结构如下:
设备ID长度 | 设备ID | 产品型号长度 | 产品型号 | 设备状态码 | 错误消息长度 | 错误代码 | 设备MAC | IP地址 | 子网掩码 | 网关地址 |
---|---|---|---|---|---|---|---|---|---|---|
1字节 | ... | 1字节 | ... | 1字节 | 1字节 | 1字节 | 6字节 | 4字节 | 4字节 | 4字节 |
注意,为了避免局域网内UDP丢包,建议设备配网结束之后至少发送3次以上UDP广播,每次发送间隔时间1 ~ 5s。
除了监听UDP数据包外,APP还会通过轮训平台查询设备接入的状态,整个轮训的周期长度为120s。另外,某些直连APP的通信设备,例如蓝牙设备可直接通过原蓝牙通道将配网结果返回给APP,该类设备可以不用发送UDP广播包。
其中,不同情况下返回的设备状态码不同,包括以下几种情况:
设备状态码 | 状态描述 | 处理建议 |
---|---|---|
-2 | 路由器连接失败 | APP引导重新发起配网流程 |
-1 | 无法访问外部网络 | 提示用户检查路由器设置后再重试 |
0 | 配网成功且接入云平台成功 | 无 |
1 | 配网成功,但接入云平台失败 | 建议用户与售后联系处理 |
如果设备状态码不等于0,则表示设备配置网络或连接云平台设备网关出现了故障,设备需要将故障原因通过错误代码反馈给APP。具体的错误代码定义如下:
错误代码 | 设备状态码 | 错误描述 | 提示语建议 |
---|---|---|---|
-6 | -2 | 配网超时 | 网络配置超时,请重试 |
-5 | -2 | 设备不支持连接5G Wifi热点 | 设备不支持5G网络,请切换到2.4G |
-4 | -2 | 其他配网错误 | 网络配置异常,请重试 |
-3 | -2 | Wifi SSID或密码错误 | 重新输入 |
-2 | -1 | 路由器连接成功,但DHCP失败 | IP地址获取异常,请检查路由器DHCP设置 |
-1 | -1 | 无法通过路由器访问外部网络 | 设备连接网络失败,请检查路由器是否支持外网访问 |
1 | -1 | DNS域名解析失败 | DNS解析失败,请检查路由器DNS设置 |
2 | 1 | 无法访问云平台 | 服务器无法访问,请检查网络 |
3 | 1 | 平台访问拒绝,接入认证失败 | 设备认证失败,请与售后联系 |
设备应用交互
在某些具体的业务场景下,如果蓝牙直连设备与绑定的应用之间需要直接的数据通信交互,可以通过支持APP消息指令0xff来完成。应用和设备具体的业务数据通过报文的负载数据部分来传输,具体负载数据内部的结构可以由设备和应用之间自定义。
包头 | 消息指令 | 负载长度 | 负载数据 | 校验位 |
---|---|---|---|---|
0x40444cfa | 0xff | 0x???? | 设备和应用定义消息 | 0x?? |
HTTP网络通信
对于wifi热点设备,推荐在wifi设备上启动HTTP服务器,并开放http请求接口进行通信。
HTTP服务器的固定IP地址是: 10.192.168.1
。
下面就具体的HTTP功能接口进行详细说明。
设备信息读取
APP可通过以下HTTP请求向设备查询设备基本信息,请求示例如下:
curl -X POST http://10.192.168.1/device/query
HTTP响应结果如下:
{
"code":"0",
"msg":"",
"data":{
"sn" : "DLX5_10000123456",
"model": "DLX5",
"version": "1.0"
}
}
响应结果包含了设备的所有基本参数,参数说明如下:
响应参数 | 类型 | 说明 |
---|---|---|
sn | 字符串 | 设备SN |
model | 字符串 | 产品型号 |
version | 字符串 | 固件版本号 |
设备配网
在APP已连接上Wifi设备的情况下,Wifi设备配网的标准流程如下:
- APP通过接口请求设备搜索周边的所有wifi热点列表,并返回APP;
- APP从设备搜索到的Wifi列表中选择需要配置的热点;
- APP输入Wifi密码信息,并通过接口写入设备;
- 设备退出热点模式,与APP断开连接,并连接配置的wifi热点;
- 设备通过wifi热点广播配网结果信息。
其中,第1步请求设备搜索周边wifi列表的接口定义如下:
- 接口URL:
http://10.192.168.1/wifi/list
- 请求类型:
POST
- 请求参数:
无
HTTP响应消息体结果如下:
{
"code":"0",
"msg":"",
"data":[
{
"ssid":"DELI",
"anthenc":"WPA2-PSK-AES",
"quality":"-70"
}
]
}
响应结果包括设备扫描到的所有可用wifi热点列表,每个wifi热点的参数说明如下:
响应参数 | 类型 | 说明 |
---|---|---|
ssid | 字符串 | WIFI热点ID |
anthenc | 字符串 | AP的加密模式。 -设备未加密 none -设备加密,以设备的加密类型为准。可参考 WEP64 ; WEP128 ; WPA-PSK-TKIP ;WPA-PSK-AES ;WPA2-PSK-TKIP ;WPA2-PSK-AES |
quality | 字符串 | 信号强度 |
第3步向设备写入配网信息的接口定义如下:
- 请求URL:
http://10.192.168.1/wifi/setting
- 请求类型:
POST
- 请求参数: (入参格式:application/json)
{
"ssid":"DELI",
"authenc":"WPA2-PSK-AES",
"key":"12345678"
}
请求参数 | 类型 | 说明 |
---|---|---|
ssid | 字符串 | WIFI热点ID |
authenc | 字符串 | AP的加密模式,以设备的加密类型为准 |
key | 字符串 | WIFI密码 |
HTTP响应消息体结果如下:
{
"code":"0",
"msg":""
}
为了及时获得设备的配网结果,Wifi设备可以在连接到Wifi热点之后通过UDP局域网广播返回配网结果信息,具体实现与蓝牙设备一致。