uni-app使用蓝牙
<p id="main-toc"><strong>目录</strong></p><p id="%E5%89%8D%E8%A8%80%C2%A0%20%C2%A0-toc" style="margin-left: 0">前言 </p>
<p id="%C2%A0%E8%BF%9E%E6%8E%A5%E8%93%9D%E7%89%99-toc" style="margin-left: 0"> 连接蓝牙</p>
<p id="%E5%BC%80%E5%90%AF%E8%93%9D%E7%89%99%E9%80%82%E9%85%8D%E5%99%A8-toc" style="margin-left: 40px">开启蓝牙适配器</p>
<p id="%C2%A0%E5%8F%91%E7%8E%B0%E8%93%9D%E7%89%99-toc" style="margin-left: 40px"> 发现蓝牙</p>
<p id="%E8%BF%9E%E6%8E%A5%E8%93%9D%E7%89%99-toc" style="margin-left: 40px">连接蓝牙</p>
<p id="%E6%94%B6%E5%8F%91%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE-toc" style="margin-left: 0">收发蓝牙数据</p>
<p id="%C2%A0%E8%8E%B7%E5%8F%96%E6%9C%8D%E5%8A%A1ID-toc" style="margin-left: 40px"> 获取服务ID</p>
<p id="%E8%8E%B7%E5%8F%96%E7%89%B9%E5%BE%81%E5%80%BC-toc" style="margin-left: 40px">获取特征值</p>
<p id="%E8%AF%BB%E5%8F%96%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE-toc" style="margin-left: 40px">读取蓝牙数据</p>
<p id="%E5%86%99%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE-toc" style="margin-left: 40px">写蓝牙数据</p>
<p id="%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91-toc" style="margin-left: 0">遇到的坑</p>
<p id="%E8%8E%B7%E5%8F%96serviceId%E7%9A%84%E5%9D%91-toc" style="margin-left: 40px">获取serviceId的坑</p>
<p id="%C2%A0%20%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8D%E6%94%AF%E6%8C%81%E8%AF%BB%E5%86%99-toc" style="margin-left: 40px"> 特征值不支持读写</p>
<p id="%C2%A0notify%E6%88%90%E5%8A%9F%E5%90%8E%E7%AB%8B%E5%88%BB%E5%86%99%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE-toc" style="margin-left: 40px"> notify成功后立刻写蓝牙数据</p>
<p id="%E5%B7%A5%E5%85%B7%E6%96%B9%E6%B3%95-toc" style="margin-left: 0">工具方法</p>
<hr id="hr-toc">
<p></p>
<h1 id="%E5%89%8D%E8%A8%80%C2%A0%20%C2%A0">前言 </h1>
<p> 原因是公司要搞个共享单车给内部员工使用,所以需要用手机连接锁蓝牙,然后扫码开锁。这个时候就要看看uni-app的蓝牙模块了。</p>
<p> uni-app的蓝牙模块看起来只支持低功耗蓝牙,即ble蓝牙。</p>
<p><img src="https://img-blog.csdnimg.cn/1fdb33b24c4643e4baa8384ceaa88840.png"></p>
<p> 然后公司就找了个厂商,这个厂商就给了我一份文档,如下图所示,</p>
<p><img src="https://img-blog.csdnimg.cn/e814f1b3fee0405f902d17d6a8965351.png"></p>
<p> 这份文档呢有一些指令,指令如下图,可以看到这些指令呢都是一些16进制的数据,即发送给蓝牙的数据为,0xfe 0x66 0x00 0x21 0x00 (app->车锁的模块)</p>
<p>最后的CRC是将前面的数据加密。</p>
<p>最后车锁会返回给APP数据,即 车锁->app模块。</p>
<p><img src="https://img-blog.csdnimg.cn/34360e34551643549edb7d5238951a6d.png"></p>
<p> 厂家的蓝牙锁就是市面上常见的,如下图,有一个二维码,扫一下二维码开锁就是需要实现的逻辑。</p>
<p><img src="https://img-blog.csdnimg.cn/8864cedbb2cf4773b9ed4209de6234c3.png"></p>
<h1 id="%C2%A0%E8%BF%9E%E6%8E%A5%E8%93%9D%E7%89%99"> 连接蓝牙</h1>
<p> 首先需要开启蓝牙适配器,发现蓝牙,然后连接蓝牙。</p>
<p> 对应模块分别为,</p>
<h2 id="%E5%BC%80%E5%90%AF%E8%93%9D%E7%89%99%E9%80%82%E9%85%8D%E5%99%A8">开启蓝牙适配器</h2>
<p><img src="https://img-blog.csdnimg.cn/197598671a72480091786e2916c88002.png"></p>
<h2 id="%C2%A0%E5%8F%91%E7%8E%B0%E8%93%9D%E7%89%99"> 发现蓝牙</h2>
<p><img src="https://img-blog.csdnimg.cn/ffa99947ef5d489cb7900ac80e5b3674.png"></p>
<p> <img src="https://img-blog.csdnimg.cn/8950c5d43a964909baedc8135c7c902e.png"></p>
<p> 当startBluetoothDevicesDiscovery调用后,搜索到的蓝牙就会在onBluetoothDeviceFound中被搜索到,</p>
<p>devices返回的数据如下,而本次的目标就是name:BleLock。</p>
<blockquote>
<p>{<br> "devices": [<br> {<br> "deviceId": "2A:40:6F:28:24:90",<br> "name": "",<br> "RSSI": -53,<br> "localName": "",<br> .......<br> }<br> ]<br> }</p>
<p></p>
<p>{<br> "devices": [<br> {<br> "deviceId": "32:71:0D:BF:43:FA",<br> "name": "",<br> "RSSI": -57,<br> "localName": "",<br> .......<br> }<br> ]<br> }</p>
<p></p>
<p>{<br> "devices": [<br> {<br> "deviceId": "ED:C0:93:EF:0D:C5",<br> "name": "BleLock",<br> "RSSI": -45,<br> .......<br> }<br> ]<br> }</p>
<p><br> </p>
</blockquote>
<h2 id="%E8%BF%9E%E6%8E%A5%E8%93%9D%E7%89%99">连接蓝牙</h2>
<p><img src="https://img-blog.csdnimg.cn/c7daa3b07f6142a39dc8ad22e0aca0b4.png"></p>
<p> 与上面方法对应的还有,停止搜索,关闭蓝牙连接,关闭蓝牙适配器,在Uni-app的API中蓝牙栏目都有对应方法。</p>
<p> 当发现到目标蓝牙后,一般就调用停止搜索方法,连接上蓝牙完成任务后就关闭蓝牙连接,关闭蓝牙适配器。</p>
<p> 即调用顺序为: <span style="color: rgba(254, 44, 36, 1)">打开蓝牙适配器--> 监听搜索--> 开始搜索--> (发现目标蓝牙后) 关闭搜索--></span></p>
<p><span style="color: rgba(254, 44, 36, 1)">连接蓝牙--> 发送蓝牙指令 --> 断开蓝牙--> 关闭蓝牙适配器</span>。</p>
<p> 当然,打开蓝牙适配器只需要在onLoad中执行即可,不需要多次执行,但在我实际操作中,我发现有时候打开蓝牙适配器成功后,依旧无法连接蓝牙。</p>
<p> 所以我每次在扫码时都是按照上述顺序来操作一遍,扫一次码走一遍流程,大不了失败后多扫码几次。</p>
<h1 id="%E6%94%B6%E5%8F%91%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE">收发蓝牙数据</h1>
<p> 当连接上蓝牙后,需要收发蓝牙数据,收发蓝牙数据需要先获取服务ID,再获取特征值,即下图中的serviceId和characteristicId。</p>
<p><img src="https://img-blog.csdnimg.cn/c497943603fd43eeac2d0aeaec514d76.png"></p>
<p> <img src="https://img-blog.csdnimg.cn/14d999a0f3e5418eb3cf887cf57831fa.png"></p>
<h2 id="%C2%A0%E8%8E%B7%E5%8F%96%E6%9C%8D%E5%8A%A1ID"> 获取服务ID</h2>
<p><img src="https://img-blog.csdnimg.cn/ffcbf58d90754a6c8112ffffe420cb22.png"></p>
<p> 调用此方法后,发现会输出如下数据,此处的uuid就是serviceId</p>
<blockquote>
<p>[<br> {<br> "uuid": "00001800-0000-1000-8000-00805F9B34FB",<br> "isPrimary": true<br> },<br> {<br> "uuid": "00001801-0000-1000-8000-00805F9B34FB",<br> "isPrimary": true<br> },<br> {<br> "uuid": "6E400001-E6AC-A7E7-B1B3-E699BAE8D000",<br> "isPrimary": true<br> }<br> ]</p>
</blockquote>
<p> 此时需要循环遍历每个serviceId获取到特征值,但是在厂商中只有6e4开头获取到的特征值才能操作蓝牙,所以就只需要提取此处6e4开头的serviceId即可。</p>
<h2 id="%E8%8E%B7%E5%8F%96%E7%89%B9%E5%BE%81%E5%80%BC">获取特征值</h2>
<p><img src="https://img-blog.csdnimg.cn/a295bb2e19f642138f629c266b9ae0a1.png"></p>
<p> 下面的uuid就是特征值,其实都写数据只需要write:true和notify:true的特征值即可。</p>
<p style="margin-left: 0.0001pt; text-align: justify"> read:true表示支持读取蓝牙数据,</p>
<p style="margin-left: 0.0001pt; text-align: justify"> write:true表示支持向蓝牙写数据,</p>
<p style="margin-left: 0.0001pt; text-align: justify"> notify:true 表示支持监听蓝牙返回的数据,</p>
<blockquote>
<p>[<br> {<br> "uuid": "6E400003-E6AC-A7E7-B1B3-E699BAE8D000",<br> "properties": {<br> "read": false,<br> "write": false,<br> "notify": true,<br> "indicate": false<br> }<br> },<br> {<br> "uuid": "6E400002-E6AC-A7E7-B1B3-E699BAE8D000",<br> "properties": {<br> "read": false,<br> "write": true,<br> "notify": false,<br> "indicate": false<br> }<br> }<br> ] </p>
</blockquote>
<h2 id="%E8%AF%BB%E5%8F%96%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE">读取蓝牙数据</h2>
<p> 一般是在连接蓝牙成功后就需要调用如下方法,在notify方法成功后就需要调用onBLECharacteristicValueChange方法,监听返回。</p>
<pre><code class="language-javascript">uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: ths.deviceInfo.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId: characteristicId,
success(res) {
// 必须在这里的回调才能获取
uni.onBLECharacteristicValueChange(function(res) {
var receiveValue = ths.ab2hex(res.value) //2进制数据转为16进制字符串
console.log("蓝牙返回数据为:"+receiveValue)
})
//获取key此处一定要延迟,要等Notify成功后才能发送
setTimeout(function(){
ths.unlock();//开锁
},2000);
},
fail(res) {
console.log(res)
ths.startNotify = false;
},
})
// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}</code></pre>
<p> <span style="color: rgba(254, 44, 36, 1)">蓝牙返回的数据是二进制的,是无法打印出来,所以需要转换为字符串</span>。</p>
<p></p>
<h2 id="%E5%86%99%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE">写蓝牙数据</h2>
<p> 向蓝牙写入的是二进制数据,所以需要转为二进制,</p>
<p> 根据厂商文档需要开锁,那么需要写入 0xfe 0x66 0x00 0x21 0x00</p>
<p><img src="https://img-blog.csdnimg.cn/9ffecae251af4ec7b6284fea92db1da0.png"></p>
<pre><code class="language-javascript">// 向蓝牙设备发送一个0x00的16进制数据
const buffer = new ArrayBuffer(6)
const dataView = new DataView(buffer)
dataView.setUint8(0,0xFE);//STX
dataView.setUint8(1,0x66);//NUM
dataView.setUint8(2,0x00);//通信秘钥 Key
dataView.setUint8(3,0x21);//CMd
dataView.setUint8(4,0x00);//LEN
dataView.setUint8(5,0xB4);//CRC
dataView.setUint8(6,0xF6);
uni.writeBLECharacteristicValue({
// 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
deviceId:deviceIdValue,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId:serviceIdValue,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId:characteristicIdValue,
// 这里的value是ArrayBuffer类型
value: buffer,
success(res) {
console.log('writeBLECharacteristicValue success', res)
},
fail(res) {
console.log(res)
},
})</code></pre>
<p> 注意此处的success返回,只是告诉你蓝牙写成功否,并不是蓝牙的返回数据,蓝牙的返回数据在 onBLECharacteristicValueChange 中。</p>
<p> 此时到这里应该是能操作蓝牙了,但是uni-app中还遇到一些坑。</p>
<h1 id="%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91">遇到的坑</h1>
<h2 id="%E8%8E%B7%E5%8F%96serviceId%E7%9A%84%E5%9D%91">获取serviceId的坑</h2>
<p> 刚连接蓝牙成功时就调用getBLEDeviceServices获取,结果一直报蓝牙未连接,解决的方式是加个setTimeout,如果延迟后还获取不到serviceId,那么用户就重新扫码吧。</p>
<pre><code class="language-javascript">connectBle() {
var ths = this;
uni.createBLEConnection({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId:ths.deviceInfo.deviceId,
success(res) {
//此处也需要延迟,刚连接上时,无法获取到特征值
setTimeout(function(){
ths.getServices();
},2000)
},
fail(res) {
ths.showModalInfo("连接蓝牙失败");
},
complete(res) { //无论有没有连上蓝牙,都要停止搜索
ths.stopFindBluetooth(); //停止搜索蓝牙
}
})
},</code></pre>
<h2 id="%C2%A0%20%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8D%E6%94%AF%E6%8C%81%E8%AF%BB%E5%86%99"> 特征值不支持读写</h2>
<p> 可以看到获取到了如下特征值,有write:true的(00002A00-0000-1000-8000-00805F9B34FB),但是用这个特征值写数据,你会发现就是写不了,会报特征值不支持写入。</p>
<p> 原因是厂商设置了只支持6e4开头的特征值,麻蛋的,当时我以为是uni-app获取特征值数据错了呢!</p>
<blockquote>
<p>[<br> {<br> "uuid": "00002A00-0000-1000-8000-00805F9B34FB",<br> "properties": {<br> "read": true,<br> "write": true,<br> "notify": false,<br> "indicate": false<br> }<br> },<br> {<br> "uuid": "00002A01-0000-1000-8000-00805F9B34FB",<br> "properties": {<br> "read": true,<br> "write": false,<br> "notify": false,<br> "indicate": false<br> }<br> },<br> {<br> "uuid": "00002A04-0000-1000-8000-00805F9B34FB",<br> "properties": {<br> "read": true,<br> "write": false,<br> "notify": false,<br> "indicate": false<br> }<br> }<br> ]</p>
</blockquote>
<h2 id="%C2%A0notify%E6%88%90%E5%8A%9F%E5%90%8E%E7%AB%8B%E5%88%BB%E5%86%99%E8%93%9D%E7%89%99%E6%95%B0%E6%8D%AE"> notify成功后立刻写蓝牙数据</h2>
<p> 如下图可以看到,在notify成功后,我当时是立刻调用解锁方法,结果一直没有蓝牙数据返回,甚至写数据还报特征值不支持写入,no connection等,</p>
<p> 这个鬼原因我也不知道,后面加个延迟解决了,推测应该是success调用应该不是真正的成功,需要等一会,</p>
<p> 但不得不说,这个延迟就是定时炸弹,谁也不知道需要延迟多久。 <img src="https://img-blog.csdnimg.cn/77b2ae3a5030453dad4faf929e564911.png"></p>
<p> 可以看到在连接蓝牙成功后获取serviceId,Notify后发送指令都是需要延迟,明明给出success了,结果还是无法去调用。</p>
<p> 针对这个问题,去看了官网,好像也没啥回应,大部分人也是延迟一下。 </p>
<h1 id="%E5%B7%A5%E5%85%B7%E6%96%B9%E6%B3%95">工具方法</h1>
<pre><code class="language-javascript">/**
* 将一个比如abcd的字符串转为16进制数据
* @param {Object} str
*/
function stringToHex(str) {
var hexArray = [];
for (var i = 0; i < str.length; i++) {
var hexStr = str.charCodeAt(i).toString(16);
hexArray.push(str16(hexStr));
}
return hexArray;
}
var str = "yOTmK50z"
var dataArray = stringToHex("yOTmK50z"); //返回16进制的数组 0x79, 0x4f, 0x54, 0x6d, 0x4b, 0x35, 0x30,0x7a
/**
* 将字符串变为16进制数据
* @param {Object} str16进制的字符串样式,不能是随便的字符串
*/
function str16(str) {
return parseInt(str, 16);
}
var hexStr = "0c"; //对应16进制,对应10进制12
var num = str16(str);//返回12,也可以是0x0c,两者一样
/**
* 将一个十进制的数变为十六进制的字符串
* @param {Object} value
*/
function int2HexStr(value) {
return value.toString(16);
}
var num = 12;
var hexStr = int2HexStr(num) //返回 0c
/**
* 字符串分割为数组,2位一个
* @param {Object} str
*/
function str2intArray(str) {
var length = str.length / 2;
var data = [];
for (var i = 0; i < length; i++) {
data = str16(str.substring(i * 2, (i + 1) * 2));
}
return data;
}
//0x79, 0x4f, 0x54, 0x6d, 0x4b, 0x35, 0x30,0x7a
var hexStr = "794f546d4b35307a"
var dataArray = str2intArray(hexStr) //变为一个数组,里面保存16进制的数
/**
* 时间戳变为yyyy-MM-dd HH:mm:ss格式的时间
* @param {Object} timestamp
*/
function timestamp2YYYYMMDDHHmmSS(timestamp){
var n=new Date(timestamp)
return n.toLocaleDateString().replace(/\//g, "-") + " " + n.toTimeString().substr(0, 8)
}
//获取时间戳
var timestampNum = Math.round(new Date() / 1000)// 返回秒级别的时间戳,1673248864
var hexStr = int2HexStr(timestampNum); // 1673248864 转换为16进制63bbc060
var timestamp16Array = str2intArray(hexStr); // 将 63bbc060 变为 0x63 0xbb 0xc0 0x60的数组
//产生随机数
Math.round(Math.random() * 100); //[0,100)
</code></pre>
<p></p>
<p></p><br><br>
来源:https://www.cnblogs.com/weiyanei/p/17039919.html
頁:
[1]