uniapp实现BLE蓝牙连接多个电子秤设备

前言:

前段时间 在公司开发一款APP 其中有一个需求对接某厂蓝牙电子秤(类似地磅秤、多蓝牙),第一次对接这种 开发中遇到好多问题、比如将plus.bluetooth.writeBLECharacteristicValue 指定指令字符串转二进制 ArrayBuffer、uni.notifyBLECharacteristicValueChange蓝牙返回指令值十六进制转换字符串等。需要的小伙伴 可以点个关注哈!

整理需求及应用场景:

其实我们做开发的 只要业务逻辑、需求理解等清楚了 剩下的就是码code的事情了、主要就是按照以下几步 基本这个需求就能完成了。

JS部分

    data() {
            return {
                a:'',
                va:'',
                b:'',
                vb:'',
                lastValue:'', //最终棒棒称重量
                temdeviceIds:[], //选择已连接设备 临时
                deviceIds:[], //选择已连接设备
                arrbuffer: undefined,
                Loading: false,
                bluetoothList: []
            };
        },
        onLoad() {
            this.InitBle()
        },
        onUnload() {
            if (this.timer1) {
                clearTimeout(this.timer1);
                this.timer1 = null;
            }
            if (this.timer2) {
                clearTimeout(this.timer2);
                this.timer2 = null;
            }
            if (this.timer3) {
                clearInterval(this.timer3);
                this.timer3 = null;
            }
        },

1.首先初始化蓝牙

InitBle() {
                let _this = this
                uni.openBluetoothAdapter({ //初始化蓝牙模块
                    success(res) {
                        console.log('初始化蓝牙', res)
                        _this.SearchBle(); //2.0
                    },
                    fail(err) {
                        uni.showToast({
                            title: '请检查是否已开启蓝牙',
                            icon: 'none',
                            duration: 1500
                        })
                    }
                })
            },
2.搜索蓝牙设备
    GetBleList() {
                var _this = this;
                uni.getBluetoothDevices({
                    success: function(res) {
                        var bluetoothArr = res.devices.filter(function(obj) {
                            return obj.name.indexOf('LEAP')!=-1 //此处是过滤自己想要连接的蓝牙设备
                        })
                        _this.bluetoothList = bluetoothArr  //将蓝牙列表展示在页面中 
                    },
                    fail: function() {
                        console.log("搜索蓝牙设备失败");
                        uni.showToast({
                            title: '搜索蓝牙设备失败或附件暂无开启的蓝牙设备',
                            icon: 'none',
                            duration: 2000
                        })
                    },
                    complete: function() {
                        uni.hideLoading()
                        _this.Loading = false
                        _this.StopSearchBle() //不管是否有搜索到蓝牙设备 都要在complete中关闭蓝牙 优化性能
                    }
                })
            },
3.停止蓝牙搜索
StopSearchBle() {
                uni.stopBluetoothDevicesDiscovery({
                    success(res) {
                        console.log('停止搜索蓝牙', res)
                    }
                })
            },
4.找到蓝牙设备后 页面点击选择某个蓝牙设备(此需求是可以连接多个蓝牙设备)
goConnectBle(e, index) {
                console.log('开始连接蓝牙', e, index)
                this.Loading = true
                let item = {
                  deviceId: e.deviceId,
                  name: e.name,
                };
                this.temdeviceIds.push(item); //临时已连接的蓝牙设备
                this.deviceIds = this.uniqueFunc(this.temdeviceIds,'deviceId') //去重后的蓝牙设备
                // 将点击蓝牙设备 存放数组遍历 用户设备可链接多蓝牙
                uni.showLoading({
                    title:'正在连接中...'
                })
//此处为重点 蓝牙连接  根据某一id连接设备,4.0
                this.deviceIds.map(item=>{
                    this.ConnectBle(item)
                })
            },

5.去重方法
uniqueFunc(arr, uniId){
              const res = new Map();
              return arr.filter((item) => !res.has(item[uniId]) && res.set(item[uniId], 1));
            }
6.选择连接蓝牙设备后相关特征信息
        ConnectBle(item) {
                var _this = this;
                uni.createBLEConnection({
                    deviceId: item.deviceId, //设备id
                    success: (res) => {
                        uni.hideLoading()
                        _this.NoticeConnection(); //连接成功后,开始监听异常
                        _this.timer2 = setTimeout(() => { //加个延迟、目的是为了确保连接成功后,再获取服务列表--可以显示正在连接效果
                            _this.Loading = false
                            _this.timer3 = setInterval(()=>{ 
                            _this.GetServiceId(item.deviceId); //5.0 获取蓝牙uuid 会存在多个 具体根据某厂商文档告知
                                _this.writeTest(item) //写入指令 告知电子秤设备(一般某厂商在开发文档、说明文档告知)
                            },10000)  //此处为模拟轮询 第一次会出现获取不到蓝牙特性信息、将值调整小一点即可获取
                            uni.showToast({
                                title: '连接成功',
                                icon: 'success',
                                duration: 800
                            })
                        }, 3000)
                    },
                    fail: function(err) {
                        if (err.errMsg == 'createBLEConnection:fail already connect') {
                            _this.Loading = false
                            uni.showToast({
                                title: '已有蓝牙连接',
                                duration: 1200
                            })
                        } else {
                            uni.showToast({
                                title: err.errMsg,
                                duration: 1200
                            })
                        }
                    },
                    complete: function() {
                        // console.log('蓝牙连接完成')
                    }
                })
            },
7.连接成功后,开始监听异常
NoticeConnection() {
                var _this = this;
                uni.onBLEConnectionStateChange((res) => {
                    console.log('开始监听蓝牙状态', res)
                    if (!res.connected) {
                        uni.showModal({
                            title: '提示',
                            content: '蓝牙已断开,请重新搜索重连!',
                            success(res) {}
                        })
                    }
                })
            },
8.获取蓝牙(5.0)设备的服务uuid(服务uuid可能有多个)
GetServiceId(deviceId) {
                console.log('连接的蓝牙设备deviceId====', deviceId)
                var _this = this
                uni.getBLEDeviceServices({
                    deviceId,
                    success(res) {
                        _this.serviceid_list = res.services //蓝牙服务列表放在data里面只是备用
                        _this.serviceId = 'xxxxxxxxxxxxxxxxx' //这是用来监听蓝牙下发和接收的服务uuid 一般厂商提供说明文档有提到
                        _this.GetCharacteIdNotify(_this.serviceId, 
                            deviceId) //6.0  获取第2个服务uuid的特征值 (关于获取第几个uuid服务,看蓝牙方面提供的协议
                    }
                })
            },
9.根据服务uuid获取蓝牙特征值,开始监听写入和接收
    GetCharacteIdNotify(serviceId, deviceId) {
                let _this = this
                uni.getBLEDeviceCharacteristics({
                    deviceId,
                    serviceId, 
                    success(res) {
                        console.log('获取蓝牙特征值', res.characteristics)
                        _this.arrbuffer = res.characteristics
                        _this.writeId = res.characteristics[1].uuid //写入 id  此处根据实际情况写
                        _this.notifyId = res.characteristics[0].uuid //接收id 此处根据实际情况写
                    }
                })
            },
10.写入指令(按某厂商说明文档 给出指定指令)
writeTest(item) {
    this.BleWrite('$xxxx#', item.deviceId)
}
11.向蓝牙写入数据 传递电子秤设备信息
    BleWrite(instruction, deviceId) {
                // 向蓝牙设备发送一个0x00的16进制数据
                let _this = this
                let serviceId = _this.serviceId
                let characteristicId = _this.writeId
                let msg = instruction 
                const buffer = new ArrayBuffer(msg.length)
                const dataView = new DataView(buffer)
                for (var i = 0; i < msg.length; i++) {
                    dataView.setUint8(i, msg.charAt(i).charCodeAt())
                }
                plus.bluetooth.writeBLECharacteristicValue({
                    deviceId, // 蓝牙设备 deviceId
                    serviceId, // 蓝牙服务uuid,即第二个uuid
                    characteristicId, // 蓝牙特征值的 (即 writeId)
                    value: buffer, // 这里的value是ArrayBuffer类型
                    success(res) {
                        _this.startNoticeBle(_this.notifyId,deviceId) //7.0,开始侦听数据
                    },
                    fail(err) {
                        console.log('写入数据失败', err)
                        uni.showToast({
                            icon: "none",
                            title: "请确保您的手机已连接设备",
                            duration: 3000
                        })
                    }
                })
            },
12.写入指令成功后,开始侦听蓝牙发送数据 处理接收
startNoticeBle(notifyId,deviceId) {
                let _this = this
                uni.notifyBLECharacteristicValueChange({
                    state: true, // 启用 notify 功能
                    deviceId: deviceId,
                    serviceId: _this.serviceId,
                    characteristicId: notifyId,
                    success(res) {
                        setTimeout(function() {
                            uni.onBLECharacteristicValueChange((res) => {
                                _this.hexCharCodeToStr(_this.ab2hex(res.value)) // hexCharCodeToStr为蓝牙返回的是十六进制的值 此处要将十六进制值转换成字符串   ab2hex 为ArrayBuffer转16进度字符
                            })
                        }, 5) //延迟是为异步更好拿出数据
                    },
                    fail: function(err) {
                        console.log('开启监听失败', err)
                    }
                })
            },
13.ArrayBuffer转16进度字符串
    ab2hex(buffer) {
                const hexArr = Array.prototype.map.call(
                    new Uint8Array(buffer),
                    function(bit) {
                        return ('00' + bit.toString(16)).slice(-2)
                    }
                )
                return hexArr.join('')
            },
14.将十六进制转换字符串
    hexCharCodeToStr(hexCharCodeStr) {  
                const _this = this
                _this.lastValue = ''
                        var trimedStr = hexCharCodeStr.trim();  
                        var reslutr = ''
                        var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;  
                        var len = rawStr.length;  
                        if (len % 2 !== 0) {  
                            alert("存在非法字符!");  
                            return "";  
                        }   
                        var curCharCode;  
                        var resultStr = [];  
                        for (var i = 0; i < len; i = i + 2) {  
                            curCharCode = parseInt(rawStr.substr(i, 2), 16);  
                            resultStr.push(String.fromCharCode(curCharCode));  
                        }  
//设备的特征值 这里值的是设备号 和蓝牙没有关系
                if(resultStr.join("").replace(',', '').indexOf('W')!=-1){
                        if(_this.a=='' || _this.a == resultStr.join("").replace(',', '').substring(0, 5)){
                            _this.a = resultStr.join("").replace(',', '').substring(0, 5)
                            _this.va = resultStr.join("").replace(',', '').substring(6, resultStr.length - 2)
                        }else if(_this.b=='' || _this.b== resultStr.join("").replace(',', '').substring(0, 5)){
                            _this.b = resultStr.join("").replace(',', '').substring(0, 5)
                            _this.vb = resultStr.join("").replace(',', '').substring(6, resultStr.length - 2)
                        }
                } 
                        console.log('电子秤称完整信息',resultStr.join("").replace(',',''))
                        _this.lastValue =Number(_this.va) + Number(_this.vb)
}
15.设备无法连接 需要重新获取
请调用 第一个方法 this.InitBle()

html部分

<template>
    <view class="box">
        <view class="content" v-if="Loading">
            <view class="content_b" />
        </view>
        <view style="height: 100%;" v-else>
            <view class="content" v-if="bluetoothList.length>0">
            <view class="" v-if="bluetoothList && bluetoothList.lenght!=0">
                 <view class="ffass" v-for="(item,index) in bluetoothList" :key="index">
                    <view class="trycsd">
                        <view class="nmjer">
                            <image src="/static/image/xxx.png" mode="" class="mjqwex"></image>
                        </view>
                        <text>{{item.name}}</text>
                    </view>
                    <text class="fdstrd" @click="goConnectBle(item,index)">点击链接</text>
                 </view>
            </view>
                <view class="" v-show="lastValue">
                    当前重量:{{lastValue}} +kg  
                </view>  
            </view>
            <view class="content" v-else style="margin: 300rpx auto;margin-bottom: 0;">
                <view class="content_t">
                    <view class="common_txt" style="color: #000">
                        未发现附近的蓝牙设备
                    </view>
                    <view style="margin-bottom: 50rpx;" size="default" shape="circle" :hairLine="true"
                        class="common_txt" @click="ReSearchBle()">
                        重新扫描
                    </view>
                </view>
                <view class="content_b" />
            </view>
        </view>
    </view>
</template>

css部分

<style lang="scss" scoped>
    .content {
        display: flex;
        flex: 1;
        height: 100%;
        flex-direction: column;
        .bluetooth_item {
            border-bottom: 1rpx solid #ccc;
            line-height: 100rpx;
            padding: 0 50rpx;
            display: flex;
            justify-content: space-between;
        }
    }
    .content_t {
        flex: 1;
        display: flex;
        flex-direction: column;
        justify-content: flex-end;
        align-items: center;

        .txt {
            color: red;
            margin: 40rpx 0rpx 120rpx;
            font-size: 34rpx;
        }
    }
    .content_b {
        flex: 1;
    }
    .common_txt {
        color: indianred;
        font-size: 34rpx;
    }
    .ffass{
        width: 698rpx;
        height: 180rpx;
        background: #FFFFFF;
        border-radius: 10rpx;
        overflow: hidden;
        margin: 30rpx auto;
        display: flex;
        justify-content: space-between;
        align-items: center;
        .nmjer{
            width: 152rpx;
            height: 152rpx;
            background: #F9F9F9;
            border-radius: 10rpx;
            display: flex;
            justify-content: center;
            align-items: center;
            margin-right: 30rpx;
            margin-left: 14rpx;
            .mjqwex{
                width: 85rpx;
                height: 85rpx;
            }
        }
        .trycsd{
            display: flex;
            align-items: center;
        }
        .fdstrd{
            display: inline-block;
            height: 68rpx;
            width: 196rpx;
            background: #FFFFFF;
            border-radius: 5rpx;
            color: #5A9CFE;
            font-size: 32rpx;
            border: 1px solid #5A9CFE;
            margin-right: 20rpx;
            line-height: 68rpx;
            text-align: center;
        }
    }
</style>

觉得还可以的话,麻烦给作者点个关注咯,您的关注就是动力!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容