迅鸣物联网文档

迅鸣物联网文档


蓝牙控制板底层通信协议V1版

蓝牙控制板相关协议

一.主服务

serviceId:0000fff0-0000-1000-8000-00805f9b34fb

蓝牙扫描时可通过指定serviceId过滤设备

二.广播数据

advertisData:0x010136630D040098

36630D040098为设备的mac地址,用于IOS系统识别设备的身份

三.应用服务

服务ID:00000001-0000-1000-8000-00805F9B34FB
characteristicId-write:000284CF-F7E3-55B4-6C4C-9FD140100A16
characteristicId-notify:000384CF-F7E3-55B4-6C4C-9FD140100A16

characteristicId-write:写指令及相关参数

characteristicId-notify:控制板响应通知

四.流程1-设备连接

  1. 扫描通过主服务过滤扫描设备
  2. mac地址/deviceId连接设备
  3. 发现应用服务
  4. 开启characteristicId-notify特征监听

当主机与设备连接成功后在开启监听characteristicId-notify的情况下会收到设备的认证参数

认证参数:0x31584d5f6c63685776363338314635434241

ASCII对照:31=字符1 58=字符X 依次类推可得结果:1XM_lock_v6381F5CBA

1流程ID:连接成功

XM_lock_v610个字符长度的设备名称

381F5CBA8个字符长度的随机串,每次连接和断开会重置

XM_lock_v6381F5CBA保存,用于后期交互验证【以下均称为认证串

五.流程2-指令发送

指令集:

 * id 响应次数  名称
 * 20   2次   读配置
 * 21   2次   开锁        V
 * 24   1次   改密码       V
 * 25   1次   改名字       V
 * 26   1次   查状态       V
 * 27   1次   写27        V
 * 28   1次   写28        V
 * 29   1次   读版本       V
 * 2a   1次   带参数的开锁  V
 * 2b   1次   带参数的关锁  V

8字符密码:默认00000000可通过指令更改

下述+均为字符串的拼接

读取锁的配置信息20:

指令id:20

密钥: 认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

写characteristicId-write:

// act 指令id
// md5p 密钥
const genValue = function (act, md5p) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length 
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  return buffer
}

第一次通知characteristicId-notify:3330xxxxxxxxxxxxxxxxxxxx

  • 33 => 字符3
  • 30 => 字符0

3330=>30 表示对20的回复

如果:xxxx....为0则密钥验证失败

结果解析

    let res = "3330xxxxxxxxxxxxxxxxxxxx".substring(4, 24)
    let a = res.substr(0, 2);
    let b = res.substr(2, 4);
    let c = res.substr(6, 2);
    let d = res.substr(8, 2);
    let e = res.substr(10, 4);
    let f = res.substr(14, 4);
    let g = res.substr(18, 2)

(图一)

各个结果对应内容如上图所示

第二次通知characteristicId-notify:3338xxxxxxxx

    let res = "3330xxxxxxxx".substring(4, 12)
    let a = res.substr(0, 2);
    let b = res.substr(2, 2);
    let c = res.substr(4, 2);
    let d = res.substr(6, 2);

(图二)

开指令21

指令id:21

密钥: 认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

写characteristicId-write:

// act 指令id
// md5p 密钥
const genValue = function (act, md5p) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length 
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  return buffer
}

第一次通知characteristicId-notify:转换为ASCII字符串310d120003000300

  • 31 对 21 指令的回复
  • 0D12 16进制电压
  • 0003 16进制开锁次数
  • 0003 16进制总开锁次数
  • 00 接收指令时的状态 [00关/10开]

第二次通知characteristicId-notify:转换为ASCII字符串310d120003000310

  • 31 对 21 指令的回复
  • 0D12 16进制电压
  • 0003 16进制开锁次数
  • 0003 16进制总开锁次数
  • 11 之心指令后状态 [00关/10开]

对比两次回复

  • 00->01 表示打开成功
  • 00->00 表示打开失败
  • 01->01 表示打开成功

改密码指令24

指令id:24

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

参数:8字符新密码

写characteristicId-write

  // act 指令
  // sendresB  8字符新密码
  // md5p  密钥
  function getValue(act,md5p,sendresB){
    let sendresA = act + md5p + sendresB;
    let l = sendresA.length
    let buffer = new ArrayBuffer(l)
    let dataView = new DataView(buffer)
    for (var i = 0; i < sendresA.length; i++) {
        dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
      }
    return buffer
  }

通知characteristicId-notify:转换为ASCII字符串340d120003000310

如果为3400000000000000 表示旧密码验证错误

断开连接后重新连接生效

改名称25

指令id:24

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

参数:10字符新名称

写characteristicId-write

  // act 指令
  // sendresB  10字符新名称
  // md5p  密钥
  function getValue(act,md5p,sendresB){
    let sendresA = act + md5p + sendresB;
    let l = sendresA.length
    let buffer = new ArrayBuffer(l)
    let dataView = new DataView(buffer)
    for (var i = 0; i < sendresA.length; i++) {
        dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
      }
    return buffer
  }

通知characteristicId-notify:转换为ASCII字符串350d120003000310

如果为3500000000000000 表示密码验证错误

断开连接后重新连接生效

查询当前状态26

指令id:26

密钥:(认证串)toUpperCase().substr(10,8) // 此处不需要密码和md5 直接切

写characteristicId-write

// act 指令
// unp 直接切的认证串
function getValue(act,unp){
  let sendresA = act + unp;
  let l = sendresA.length;
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
    return buffer
}

通知characteristicId-notify:360d120003000310

解析规则与21指令回复一致

写配置27【业务配置】

指令id:27

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

参数:16进制的字符串规则为上述(图一)

写characteristicId-write

// act 指令id
// md5p 密钥
// params 参数
const genValue = function (act, md5p,params) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length + (sendresB.length / 2)
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  let mlist = []
  for (let index = 0; index < sendresB.length; index++) {
    if (index % 2 == 0) {
      mlist.push(sendresB.substr(index, 2))
    }
  }
  for (let index = 0; index < mlist.length; index++) {
    const element = parseInt(mlist[index], 16);
    dataView.setUint8(10 + index, element)
  }
  return buffer
}

通知characteristicId-notify:转换为ASCII字符串370d120003000310

如果为3700000000000000 表示密码验证错误

断开连接后重新连接生效

写配置28【系统配置】

指令id:28

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

参数:16进制的字符串规则为上述(图二)

写characteristicId-write

// act 指令id
// md5p 密钥
// params 参数
const genValue = function (act, md5p,params) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length + (sendresB.length / 2)
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  let mlist = []
  for (let index = 0; index < sendresB.length; index++) {
    if (index % 2 == 0) {
      mlist.push(sendresB.substr(index, 2))
    }
  }
  for (let index = 0; index < mlist.length; index++) {
    const element = parseInt(mlist[index], 16);
    dataView.setUint8(10 + index, element)
  }
  return buffer
}

通知characteristicId-notify:转换为ASCII字符串380d120003000310

如果为3800000000000000 表示密码验证错误

断开连接后重新连接生效

读取版本29

指令id:29

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

写characteristicId-write

// act 指令
// md5p 密钥
const genValue = function (act, md5p) {
  let sendresA = act + md5p;
  let l = sendresA.length
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  return buffer
}

通知characteristicId-notify:转换为ASCII字符串39xxxx0003000310

res.substr(2, 4)即为版本信息

如果为3900000000000000 表示密码验证错误

长时间开启2a

指令id:2a

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

参数:00000000 [16进制字符串 ]

​ 全0表示长开

​ 最大ffffffff 单位:秒 表示开多长时间

写characteristicId-write

// act 指令id
// md5p 密钥
// params 参数
const genValue = function (act, md5p,params) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length + (sendresB.length / 2)
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  let mlist = []
  for (let index = 0; index < sendresB.length; index++) {
    if (index % 2 == 0) {
      mlist.push(sendresB.substr(index, 2))
    }
  }
  for (let index = 0; index < mlist.length; index++) {
    const element = parseInt(mlist[index], 16);
    dataView.setUint8(10 + index, element)
  }
  return buffer
}

通知characteristicId-notify:转换为ASCII字符串3a0d120003000310

解析规则与21指令一致

关闭2b

指令id:2b

密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)

写characteristicId-write

// act 指令id
// md5p 密钥
const genValue = function (act, md5p) {
  let sendresA = act + md5p;
  let sendresB = params
  let l = sendresA.length 
  let buffer = new ArrayBuffer(l)
  let dataView = new DataView(buffer)
  for (var i = 0; i < sendresA.length; i++) {
    dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
  }
  return buffer
}

通知characteristicId-notify:转换为ASCII字符串3b0d120003000300

解析规则与21指令一致

六.Demo

以下为Uniapp Demo 仅供参考 [uniapp-android-demo.zip](https://www.showdoc.com.cn/server/api/attachment/visitfile/sign/55bb11d576a4632806fa3495ae2074f9 "[uniapp-android-demo.zip")

页面列表

ITEM_HTML