智慧硬件开发
智慧硬件开发
硬件直连接入
设备接入
蓝牙相关协议
蓝牙识别及配网协议
蓝牙识别及配网协议
最后更新:2021/02/01

目录

  • 蓝牙识别
  •       无需广播蓝牙信号打卡的设备
  •       支持广播蓝牙信号打卡的设备
  • 蓝牙协议
  • 模拟数据流
  • 调用流程
  •       设备绑定
  •       设备设置wifi
  • 业务协议
  • App推送
  •       设置wifi
  •       发起获取设备状态请求
  •       调取设备探测到的wifi信号列表
  • 设备请求
  •       发起握手
  •       确认握手
  •       上报设备状态
  •       上报设备wifi信号列表
  • 蓝牙发现SDK
  • 蓝牙协议更新日志
  • 蓝牙识别

    低功耗蓝牙(android&ios)设备需要广播:

    1. 企业微信规定的service uuid
    2. 厂商自定义字段里,包含设备deviceid(随secretNo一起生成并预先烧录)
    3. 包含指定的Characteristics

    参数说明:

    参数名描述
    ServiceUUID0xFCE7
    Write Characteristics UUID0xFCC7
    Indicate Characteristics UUID0xFCC8
    Read Characteristics UUID0xFCC9

    厂商自定义字段:
    广播包格式如下:(图片来自蓝牙官方文档)

    数据包长度为31字节, 考虑到部分芯片商预留字段不可修改,我们自定义数据段尽量简短。

    无需广播蓝牙信号打卡的设备

    仅定义21字节,以0xEE表示开始。
    格式参考:

    设备状态固定填0x02
    设备ID在服务商管理端,登记设备后获取
    sign预留字段,填0即可

    支持广播蓝牙信号打卡的设备

    需要在广播包中加入签名,为了防止员工抓取蓝牙信号伪造打卡,广播包中还需要增加一个自增的nonce。长度25字节,以0xED表示开始。
    格式参考:

    设备状态格式参考如下:

    5bit1bit2bit
    预留电量绑定状态
    -0:高 1:低1:已绑定 2:未绑定

    广播包每隔5分钟变化一次,nonce必须比前一次大(取值范围在0~60),sign由特定函数生成。

    sign生成算法:
    sign= Hi64Bit(Md5(seed|(nonce)));

    其中:
    1、seed为哈希种子,默认为预先烧录的secretNo,并且企业微信云端允许设备联网登陆后刷新seed。(建议一天更新一次seed,如果设备没有网络模块可以不刷新seed)
    2、nonce为随机数(uint32)
    3、|表示字符串拼接
    4、Hi64Bit表示取md5结果的高64位

    参考代码如下:

    uint64_t GetSign(const string & sSeed, uint32_t nonce)
    {
          string data = StrFormat("%s%u", sSeed.c_str(), nonce); //拼接
          MD5 md5;
          md5.update((unsigned char *)data.c_str(), data.size());
          md5.finalize();
          char sMd5[16];
          md5.raw_digest2((unsigned char *)sMd5);
          uint64_t res;
          memcpy(&res, sMd5, sizeof(res));
          return res;
    }
    

    StrFormat为字符串拼接函数,Md5为Md5类,厂商根据自己的开发情况替换。

    当设备电量低于10%应设置device_status

    Read Characteristics:
    为了支持手机给设备配网,该网络需要配置设备MAC地址白名单,设备需要在企业微信的service下面,暴露一个read character,内容包含6字节的MAC地址。
    格式说明:

    6字节2字节
    mac地址蓝牙协议版本号
    • 蓝牙协议版本号不填写则默认为0x01
    • 最新协议版本号为0x02,更新内容见“蓝牙协议更新日志”

    蓝牙协议

    目前假定设备都是BLE蓝牙设备,网络模块均为wifi。

    数据包使用私有的协议格式。透传数据包可以模拟数据流。

    模拟数据流

    企业微信规定了蓝牙BLE设备需要先模拟成流(即stream,输入输出流)。流具有的特性有:

    • 可以传输无限长度的数据
    • 双工,读写可以并发,互不干扰。

    显然,蓝牙BLE无法传输无限长度的数据,为了实现这个目的,需要定义一个规范。
    蓝牙设备需暴露两个特征值(Characteristics):Write特征值,Indication特征值。蓝牙设备从Write特征值接受数据,从Indication特征值发送数据。
    Indication特征值类型是bytes。
    这里我们约定,把一个特征值一次传输的数据,称为一帧(不同类型的特征值一次传输的数据长度是不一样的)。

    注意:应用层上的数据包(例如1k大小),会分散成许多帧来传输。

    蓝牙设备写过程:

    1. 分帧:假设设备上有1k数据,要发给企业微信。由于一个特征值长度有限(如20个字节),显然需要分多次才能传输完成。1k数据,要分成1024字节/ 20字节=51 个帧。剩下的4个字节,不足一帧(20个字节),需补齐为一帧并对剩下的16个字节赋0。总共是52帧。
    2. 发送第一个帧:把第一个帧的内容放入特征值里面。然后通知手机读取数据,通知有两种方式,Indication 和notify,这里使用Indication方式,即带响应的通知。当通知完成的时候,可以认为手机已经读完数据。这就完成了发送第一个帧。
    3. 按照2的步骤,依次发送剩下的帧。

    蓝牙设备读过程:
    当蓝牙设备发现读特征值收到数据的时候,就接收数据,并追加到设备的buf里。

    注意:蓝牙设备必须等企业微信app订阅了Characteristics之后,才能indicate数据,否者会造成设备发送数据丢失的问题。

    注意:企业微信收发取数据为大端字节序。

    调用流程

    设备绑定

    设备设置wifi

    业务协议

    业务层面的数据流采用定长包头+变长包体的形式

    定长包头:
    struct PkgFixHead
    {
        unsigned char bMagicNumber;  // 1 bytes
        unsigned char bVer;
        unsigned short nLength;
        unsigned short nCmdId;
        unsigned short nSeq;
        unsigned char nProtoType;
    };
    
    变长包体:
    二进制流数据

    参数说明:

    字段类型说明
    bMagicNumberunsigned char填0xFE
    bVerunsigned char包格式版本号,填1
    nLengthunsigned short为包头+包体的长度
    nCmdIdunsigned short命令号,表示要调用哪个接口
    nSequnsigned short递增。一个Req对应一个Resp,并且它们的nSeq相同,并且永不为0。Push的nSeq永远为0;
    nProtoTypeunsigned char表示数据类型,目前0表示json

    App推送

    设置wifi

    定长包头cmdid: cmd_push_set_wifi = 30003;
    请求数据格式:json

    数据示例:

    {
    	"ssid":"xxx",
    	"bssid":"xxxx",
    	"password":"xxxx"
    }

    参数说明:

    参数名是否必须类型描述
    ssidStringWi-Fi ssid
    bssidStringWi-Fi bssid
    passwordStringWi-Fi 密码
    protocolStringWi-Fi 协议,目前仅支持:
    None
    WEP
    WPA
    WPA2

    发起获取设备状态请求

    接口说明:
    如果蓝牙协议版本号为0x02时,企业微信进入查看设备详情页会发出该指令,设备接收后应调用“上报设备状态”接口上报对应数据。

    接口调用说明:
    定长包头cmdid: cmd_push_fetch_device_status = 30004;
    请求数据格式:空包


    调取设备探测到的wifi信号列表

    接口说明:
    App设置/重置wifi时,通过该指令调取设备探测到的wifi信号

    接口调用说明:
    定长包头cmdid: cmd_push_get_wifi_list = 30005;

    请求数据格式:

    {
    	"req_id":"xxx",
    	"limit":10
    }
    设备接收到该事件后应调用 上报wifi信号列表

    参数说明:

    参数名是否必须类型描述
    req_idString请求id,设备回包时也应该带上次id
    limituint32返回wifi信息条数,若探测到多个wifi信号,返回信号最强的前limit条即可

    设备请求

    发起握手

    接口说明:
    蓝牙匹配后调用,发起握手请求。

    接口调用说明:
    定长包头cmdid: cmd_req_handshake = 10001;
    请求数据格式:json
    数据示例:

    {
    	"client_nonce":"123451",
    	"sn":"JAS6007",
    	"scene":"handshake"
    }

    参数说明:

    参数名是否必须类型描述
    snString设备序列号
    client_nonceString64位整型随机数的字符串表示
    sceneString固定为handshake

    返回说明:
    定长包头cmdid:cmd_resp_handshake = 20001;
    JSON数据包示例:

    {
    	"errcode": 0,
    	"errmsg": "ok",
    	"server_nonce":"12354",
    	"signature":"7a83140e7bc7a4f75ac0bd094aca7474878e7f33"
    }
    参数名描述
    errcode错误码
    errmsg错误码描述
    signature服务端返回的签名
    server_nonce服务端产生的nonce, 64位整型随机数的字符串表示

    签名计算方法:
    HMAC_SHA1算法, signature= HMAC_SHA1(sort("wxwork", string(client_nonce), string(server_nonce), "handshake"))。sort的含义是将参数值按照字母字典排序,然后从小到大拼接成一个字符串。HMAC_SHA1指定key为预先烧录的secretNo。

    示例:
    client_nonce=123451
    server_nonce=12354
    secretNo=3b00147353d569ac9a4e21063d6xxxxx
    sort("wxwork", string(client_nonce), string(server_nonce), "handshake")=12345112354handshakewxwork
    signature= HMAC_SHA1(12345112354handshakewxwork)=7a83140e7bc7a4f75ac0bd094aca747487xxxxx


    确认握手

    接口说明:
    设备调用接口 发起握手 后,server端返回server签名,此处设备应计算server签名是否合法,不合法则断开链接。合法则确认握手

    接口调用说明:
    定长包头cmdid: cmd_req_confirm_handshake = 10002;
    请求数据格式:json
    数据示例:

    {
    	"signature":"e03d6219fb787764747967127a344deef85xxxxx"
    }

    参数说明:

    参数名是否必须类型描述
    signatureString设备签名

    签名计算方法:
    HMAC_SHA1算法, signature= HMAC_SHA1(sort(Sn,string(server_nonce),"handshake"))。sort的含义是将参数值按照字母字典排序,然后从小到大拼接成一个字符串。HMAC_SHA1指定key为预先烧录的secretNo。

    示例:
    sn=JAS6007
    server_nonce=12354
    secretNo=3b00147353d569ac9a4e21063d6xxxxx
    sort(Sn,string(server_nonce),"handshake")=12354JAS6007handshake
    HMAC_SHA1(12354JAS6007handshake)=e03d6219fb787764747967127a344deef85xxxxx

    返回说明:
    定长包头cmdid:cmd_resp_confirm_handshake = 20002;
    JSON数据包示例:

    {
      	"errcode": 0,
      	"errmsg": "ok",
    	"bind_status":1
    }
    参数名描述
    errcode错误码
    errmsg错误码描述
    bind_status设备是否已绑定,0:未绑定/已解绑 1:已绑定。当errcode非0时忽略该字段。

    注意:签名错误终端会断开连接。

    上报设备状态

    接口说明:
    设备连上网络成功或者失败后应主动调用该接口上报状态。

    接口调用说明:
    定长包头cmdid: cmd_req_report_device_status = 10004;
    请求数据格式:json
    数据示例:

    {
    	"errcode":0
    	"timestamp":1493913600,
    	"wifi_connected":true,
    	"ip_address": "10.9.248.30",
    	"mac_address": "B0:E5:ED:74:80:D1",
    	"wifi_name":"wtf"
    }

    参数说明:

    参数名是否必须类型描述
    errcodeInt32联网错误码。
    0: 成功
    1001: wifi不存在
    1002: 密码错误
    1003: 连接中
    timestampUint32时间戳
    wifi_connectedBool是否已连接Wi-Fi
    ip_addressString设备IP地址
    mac_addressString设备MAC地址,联网失败时也需要带上该值
    wifi_namestringwifi名
    • 注:wifi_name字段,当蓝牙协议版本号为0x02且wifi已经连接的情况下必须填写。

    返回说明:
    定长包头cmdid:cmd_resp_report_device_status = 20004;
    JSON数据包示例:

    {
      	"errcode": 0,
      	"errmsg": "ok",
    }
    参数名描述
    errcode错误码
    errmsg错误码描述

    上报设备wifi信号列表

    接口说明:
    当设备收到调取设备探测到的wifi信号列表推送(cmd:30005)后,应调用本接口上报wifi列表。

    接口调用说明:
    定长包头cmdid: cmd_req_report_wifi_list = 10005;
    请求数据格式:json
    数据示例:

    {
    	"req_id":"xxx"
    	"wifi_info":
    	[
    		{
    			"ssid":"xxx",
    			"rssi"0,
    			"need_password":false
    		},
    		{
    			"ssid":"xxx",
    			"rssi"-50,
    			"need_password":true
    		}
    	]
    }

    参数说明:

    参数名是否必须类型描述
    req_idstring请求id,透传即可
    ssidStringWi-Fi ssid
    rssiint32信号强度,单位dbm
    need_passwordbool是否需要密码

     

    返回说明:
    定长包头cmdid:cmd_resp_report_wifi_list = 20005;
    JSON数据包示例:

    {
      	"errcode": 0,
      	"errmsg": "ok",
    }
    参数名描述
    errcode错误码
    errmsg错误码描述

    蓝牙发现SDK

    对于Android设备,提供以SDK植入的方式,供企业微信发现该设备,并使其收到设置WIFI的指令。查看SDK文档

    蓝牙协议更新日志

    2020/01/13
    1.确认握手回包增加bind_status字段。
    2.增加30005指令,支持调取设备探测到的wifi信号列表。
    3.增加10005,20005指令,支持设备上报探测到的wifi信号列表。

    2019/12/16
    1.Read Characteristics增加蓝牙协议版本号字段。
    2.0x02蓝牙协议增加30004指令,支持企业微信app实时抓取设备信息。
    3.0x02蓝牙协议10004接口增加wifi_name字段。

    上一篇重启设备指令
    下一篇蓝牙识别及配网SDK
      本节内容
    硬件直连接入
    更新日志
    联系我们