蓝牙控制板底层通信协议V1版
<p>蓝牙控制板相关协议</p>
<h3>一.主服务</h3>
<p><code>serviceId:0000fff0-0000-1000-8000-00805f9b34fb</code></p>
<p>蓝牙扫描时可通过指定<strong>serviceId</strong>过滤设备</p>
<h3>二.广播数据</h3>
<p><code>advertisData:0x010136630D040098</code></p>
<p>36630D040098为设备的mac地址,用于IOS系统识别设备的身份</p>
<h3>三.应用服务</h3>
<pre><code>服务ID:00000001-0000-1000-8000-00805F9B34FB
characteristicId-write:000284CF-F7E3-55B4-6C4C-9FD140100A16
characteristicId-notify:000384CF-F7E3-55B4-6C4C-9FD140100A16</code></pre>
<p>characteristicId-write:写指令及相关参数</p>
<p>characteristicId-notify:控制板响应通知</p>
<h3>四.流程1-设备连接</h3>
<ol>
<li>扫描通过主服务过滤扫描设备</li>
<li>mac地址/deviceId连接设备</li>
<li>发现应用服务</li>
<li>开启characteristicId-notify特征监听</li>
</ol>
<p>当主机与设备连接成功后在开启监听characteristicId-notify的情况下会收到设备的<strong>认证参数</strong></p>
<p><code>认证参数:0x31584d5f6c63685776363338314635434241</code></p>
<p>ASCII对照:31=字符1 58=字符X 依次类推可得结果:<code>1XM_lock_v6381F5CBA</code></p>
<p><code>1</code>流程ID:连接成功</p>
<p><code>XM_lock_v6</code>10个字符长度的设备名称</p>
<p><code>381F5CBA</code>8个字符长度的随机串,每次连接和断开会重置</p>
<p>取<code>XM_lock_v6381F5CBA</code>保存,用于后期交互验证【以下均称为<strong>认证串</strong>】</p>
<h3>五.流程2-指令发送</h3>
<p>指令集:</p>
<pre><code> * 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</code></pre>
<p><code>8字符密码:默认00000000可通过指令更改</code></p>
<p><code>下述+均为字符串的拼接</code></p>
<h4>读取锁的配置信息20:</h4>
<p>指令id:20</p>
<p>密钥: 认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>写characteristicId-write: </p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}</code></pre>
<p>第一次通知characteristicId-notify:<code>3330xxxxxxxxxxxxxxxxxxxx</code></p>
<ul>
<li>33 => 字符3</li>
<li>30 => 字符0</li>
</ul>
<p>3330=>30 表示对20的回复</p>
<p>如果:xxxx....为0则密钥验证失败</p>
<p>结果解析</p>
<pre><code> let res = &quot;3330xxxxxxxxxxxxxxxxxxxx&quot;.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)</code></pre>
<p><img src="https://www.showdoc.com.cn/server/api/attachment/visitfile/sign/eaab17fa165448ad6d049a026e19f9ae" alt="" /></p>
<p>(图一)</p>
<p>各个结果对应内容如上图所示</p>
<p>第二次通知characteristicId-notify:<code>3338xxxxxxxx</code></p>
<pre><code> let res = &quot;3330xxxxxxxx&quot;.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);</code></pre>
<p><img src="https://www.showdoc.com.cn/server/api/attachment/visitfile/sign/da8a8fcbf23c79c803b96949f2e2ceab" alt="" /></p>
<p>(图二)</p>
<h4>开指令21</h4>
<p>指令id:21</p>
<p>密钥: 认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>写characteristicId-write: </p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}</code></pre>
<p>第一次通知characteristicId-notify:转换为ASCII字符串<code>310d120003000300</code></p>
<ul>
<li>31 对 21 指令的回复</li>
<li>0D12 16进制电压</li>
<li>0003 16进制开锁次数</li>
<li>0003 16进制总开锁次数</li>
<li>00 接收指令时的状态 [00关/10开]</li>
</ul>
<p>第二次通知characteristicId-notify:转换为ASCII字符串<code>310d120003000310</code></p>
<ul>
<li>31 对 21 指令的回复</li>
<li>0D12 16进制电压</li>
<li>0003 16进制开锁次数</li>
<li>0003 16进制总开锁次数</li>
<li>11 之心指令后状态 [00关/10开]</li>
</ul>
<p>对比两次回复 </p>
<ul>
<li>00->01 表示打开成功 </li>
<li>00->00 表示打开失败 </li>
<li>01->01 表示打开成功</li>
</ul>
<h4>改密码指令24</h4>
<p>指令id:24</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>参数:8字符新密码</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript"> // 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}
</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>340d120003000310</code></p>
<p>如果为3400000000000000 表示旧密码验证错误</p>
<p>断开连接后重新连接生效</p>
<h4>改名称25</h4>
<p>指令id:24</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>参数:10字符新名称</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript"> // 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}
</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>350d120003000310</code></p>
<p>如果为3500000000000000 表示密码验证错误</p>
<p>断开连接后重新连接生效</p>
<h4>查询当前状态26</h4>
<p>指令id:26</p>
<p>密钥:(认证串)toUpperCase().substr(10,8) // 此处不需要密码和md5 直接切</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:<code>360d120003000310</code></p>
<p>解析规则与21指令回复一致</p>
<h4>写配置27【业务配置】</h4>
<p>指令id:27</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>参数:16进制的字符串规则为上述(图一)</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
let mlist = []
for (let index = 0; index &lt; sendresB.length; index++) {
if (index % 2 == 0) {
mlist.push(sendresB.substr(index, 2))
}
}
for (let index = 0; index &lt; mlist.length; index++) {
const element = parseInt(mlist[index], 16);
dataView.setUint8(10 + index, element)
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>370d120003000310</code></p>
<p>如果为3700000000000000 表示密码验证错误</p>
<p>断开连接后重新连接生效</p>
<h4>写配置28【系统配置】</h4>
<p>指令id:28</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>参数:16进制的字符串规则为上述(图二)</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
let mlist = []
for (let index = 0; index &lt; sendresB.length; index++) {
if (index % 2 == 0) {
mlist.push(sendresB.substr(index, 2))
}
}
for (let index = 0; index &lt; mlist.length; index++) {
const element = parseInt(mlist[index], 16);
dataView.setUint8(10 + index, element)
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>380d120003000310</code></p>
<p>如果为3800000000000000 表示密码验证错误</p>
<p>断开连接后重新连接生效</p>
<h4>读取版本29</h4>
<p>指令id:29</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>39xxxx0003000310</code></p>
<p><code>res.substr(2, 4)</code>即为版本信息</p>
<p>如果为3900000000000000 表示密码验证错误</p>
<h4>长时间开启2a</h4>
<p>指令id:2a</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>参数:00000000 [16进制字符串 ]</p>
<p> 全0表示长开</p>
<p> 最大ffffffff 单位:秒 表示开多长时间</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
let mlist = []
for (let index = 0; index &lt; sendresB.length; index++) {
if (index % 2 == 0) {
mlist.push(sendresB.substr(index, 2))
}
}
for (let index = 0; index &lt; mlist.length; index++) {
const element = parseInt(mlist[index], 16);
dataView.setUint8(10 + index, element)
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>3a0d120003000310</code></p>
<p>解析规则与21指令一致</p>
<h4>关闭2b</h4>
<p>指令id:2b</p>
<p>密钥:认证串+8字符密码(字符串拼接)通过hex_md5加密后toUpperCase().substring(0, 8)</p>
<p>写characteristicId-write</p>
<pre><code class="language-javascript">// 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 &lt; sendresA.length; i++) {
dataView.setUint8(i, sendresA.charAt(i).charCodeAt())
}
return buffer
}</code></pre>
<p>通知characteristicId-notify:转换为ASCII字符串<code>3b0d120003000300</code></p>
<p>解析规则与21指令一致</p>
<h3>六.Demo</h3>
<p>以下为Uniapp Demo 仅供参考
[uniapp-android-demo.zip](<a href="https://www.showdoc.com.cn/server/api/attachment/visitfile/sign/55bb11d576a4632806fa3495ae2074f9">https://www.showdoc.com.cn/server/api/attachment/visitfile/sign/55bb11d576a4632806fa3495ae2074f9</a> "[uniapp-android-demo.zip")</p>