微信小程序蓝牙BLE开发实战——API及流程介绍(一)

微信小程序蓝牙BLE实战开发(一)

  • 迟来的更新。从4月份以来项目中断续在对接好几个共享产品,关于蓝牙BLE设备,通过蓝牙设备之间通信进行使用产品。
  • 开发中也遇到不少问题哈,后面抽时间续篇。写得不好,请各位大神多多指教。

此篇主要介绍一些API操作及一些返回数据结构, 项目已上线。后面抽时间上demo
ps: 如果有了解过蓝牙这块, 代码可直接复制使用


关于字节

字节(Byte):计算机信息技术用于计量存储容量的一种计量单位,作为一个单位来处理的一个二进制数字串。

其中下发指令或处理数据时都可以应用到

  • 1B(byte,字节)= 8 bit(比特), 相当于一个字符
  • 一个字节能表示的最大的整数就是255
  • 例如: 数据为5d000001be5d理解为6个字节(6B)

流程图

BLE执行大致流程,为了节省空间,画的比较乱哈。

在这里插入图片描述

使用步骤

微信小程序低功耗蓝牙API

微信小程序官方_蓝牙说明

  • 这里单独放在一个js文件中

初始化数据

根据需求定义,个人在一个项目中开发好几个产品,同时对接不同蓝牙协议供应商,有些变量放全局,根据不同供应商来操作

var serviceUUID = [] //主 service 的 uuid 列表
var writeUUID = ""; //写读 UUID
var notifyUUID = ""; //notify UUID
var filterServiceUUID = ""; //过滤获取到的服务uuid(有些会返回多条数据)
var filterDeviceName = ""; //设备名称

var macAddress = ""; //保存得到mac地址
var flagFromTypes = ''; //来源类型
var _discoveryStarted = false;
var deviceId = ''; //用于区分设备的 id

var _deviceId = '';
var _serviceId = '';
var _characteristicId = '';
var status = false; //当前状态
var action_type = ''; //操作类型
var code = -1;
var isnotExist = true

给变量赋值

设备相关服务通常蓝牙协议文档上有说明,ReadWriteNotify。如果没有说明可通过搜索设备看到

某些供应商读写通过统一用一个服务

function flagServiceIDFun(serviceType) {
    if (serviceType == 4) { // 
		serviceUUID[0] = "0000*E0-00*0-*0*0-*0*0-00**5F9**4*B"; //主 service 的 uuid 列表
		writeUUID = "00*0**E2-00*0-*0*0-*0*0-00**5F9**4*B"; //写读 UUID
		notifyUUID = "00*0**E1-00*0-*0*0-*0*0-00**5F9**4*B"; //notify UUID
		
		filterServiceUUID = "*E0";
		filterDeviceName = getNameMac(macAddress, 6, 'abc_'); //设备名称
    }
}

1. 初始化蓝牙模块

wx.openBluetoothAdapter(Object obj)
ps: 调用此方法initBle()就可以使用哦,【建议顺序查看,易理解】

function initBle(fromMac, flagTypes, currentSerial) {
    //断开连接【每次初始化先断开连接】
    closeBLEConnection();

    // macAddress = clearSymbol(fromMac);
    macAddress = fromMac; //保存mac
    flagFromTypes = flagTypes //类型来源
	currentSerialVal = currentSerial //当前操作序号
    
     // 根据类型,赋值主服务id相关信息【根据个人需求,如果只有一种设备,初始化赋值即可】
    flagServiceIDFun(flagTypes);
    
    wx.openBluetoothAdapter({
        success: (res) => {
            console.log('openBluetoothAdapter 初始化蓝牙模块是否成功:', res)

            // 监听寻找新设备事件
            onBluetoothDeviceFound();

            //开始搜寻附近的蓝牙外围设备
            startBluetoothDevicesDiscovery();
        },
        fail: (res) => {
            console.log('初始化蓝牙失败', res);
            //自行处理【可弹窗提示用户开启蓝牙】,这通过回调处理
            asddErrorCallback(res.errCode, "");

            //监听蓝牙适配器状态变化事件【根据需求是否执行】
            // wx.onBluetoothAdapterStateChange(function (res) {
            //     console.log('蓝牙适配器状态更改结果:  ', res)
            //     if (res.available) {
            //         console.log('蓝牙可用,搜索设备:--》 ')
            //         onBluetoothDeviceFound();
            //         startBluetoothDevicesDiscovery();
            //     }
            // })
        }
    })
}

2. 监听寻找新设备事件

wx.onBluetoothDeviceFound(function callback)

说明: 广播数据: 可以得到当前蓝牙设备的相关数据,另外,如果设备有返回其他数据时在advertisData数据段中得到,【有些供应商是没有返回的】


设备返回数据效果图

下图是两家供应商设备返回的数据, 左图advertisData返回8个字节数据【数据需转换】。右图则没有。同时可以看到返回name格式也是不一样的【自定义】

在这里插入图片描述


注意:
  • 安卓下部分机型需要有位置权限才能搜索到设备,需留意是否开启了位置权限
  • Android设备返回是mac地址, IOS返回是uuid,由32位字母和数据组成,并且是动态的。

说明:

为了保证准确性建议通过mac地址匹配。mac地址正常是12

假设:advertisData字段返回数据是0000365544332211。【可根据文档说明取需要数据】

  • advertisData得到数据(不一定有数据哦),截取mac地址匹配。【假如第4位至16位为mac(365544332211)】, 用变量保存下来。

如果advertisData没有返回其他数据,该如何匹配设备?

  • 通过设备name进行匹配设备, 供应商通常会以前缀+mac前/后几位,或12位。当前还有一些只有前缀的
    比如 FIC_992f3e,其中992f3e是设备6位mac地址。【根据实际情况操作】

案例:

  • 以下demo提供两种匹配设备方式. 通过macname匹配设备【每个供应商返回的name格式不一样 】
/**
 * 监听寻找新设备事件
 * 搜索匹配设备后,自动连接设备
 */
function onBluetoothDeviceFound() {
    wx.onBluetoothDeviceFound((res) => {
        console.log('广播数据结果:', res);

        res.devices.forEach(device => {
            if (!device.name && !device.localName) {
                return
            }

            // 转换后, 得出相关数据
            var hexStr = ab2hex(device.advertisData);
            console.log("广播数据中转换后:advertisData---->" + hexStr);

            //通过获取mac匹配
            if ((macAddress != "") && (macAddress == device.deviceId) && isnotExist) {
                isnotExist = false;
                deviceId = device.deviceId;
                console.log('android-->tempDeviceId:' + deviceId);
				
                //停止搜寻附近的蓝牙外围设备
                stopBluetoothDevicesDiscovery();
                
                //连接设备
                createBLEConnection();
            }
            
            
            //通过name匹配设备
            let deviceName = device.name.toUpperCase();
            if ((deviceName.indexOf(filterDeviceName) != -1) && isnotExist) {
                isnotExist = false;
                deviceId = device.deviceId;
                console.log('ios or android-->tempDeviceId:' + deviceId);
                
				//停止搜寻附近的蓝牙外围设备。
                stopBluetoothDevicesDiscovery();
                
                //连接设备
                createBLEConnection();
            }
        })
    })
}

3. 开始搜寻附近的蓝牙设备

wx.startBluetoothDevicesDiscovery(Object object)

注意 此操作比较耗费系统资源,请在搜索并连接到设备后调用 wx.stopBluetoothDevicesDiscovery方法停止搜索

function startBluetoothDevicesDiscovery() {
    console.log("执行连接蓝牙设备 回调空===" + _discoveryStarted);
    if (_discoveryStarted) {
        return;
    }
    _discoveryStarted = true
    
    wx.startBluetoothDevicesDiscovery({
        services: serviceUUID, //如果设置此参数,则只搜索广播包有对应 uuid 的主服务的蓝牙设备。
        allowDuplicatesKey: false,
        success: (res) => {
            console.log('启动搜索蓝牙设备, 结果  :', res)
            //onBluetoothDeviceFound()   //先调用此方法再使startBluetoothDevicesDiscovery
        },
        fail(res) {
            asddErrorCallback(res.errCode, "");
            console.log('startBluetoothDevicesDiscovery fail', res);
        }
    })
}

4. 停止搜寻附近的蓝牙设备

wx.stopBluetoothDevicesDiscovery(Object object)

//停止搜寻附近的蓝牙外围设备。
function stopBluetoothDevicesDiscovery() {
    wx.stopBluetoothDevicesDiscovery()
}

5. 连接低功耗蓝牙设备

wx.createBLEConnection(Object object)

/**
 * 连接蓝牙设备
 */
function createBLEConnection() {
    var that = this;
    wx.createBLEConnection({
        deviceId: deviceId,
        success: (res) => {
            wx.showToast({
                title: '设备连接成功',
                duration: 2000
            })
            getBLEDeviceServices(deviceId)
        },
        fail: (res) => {
            console.log('createBLEConnection fail', res);
            asddErrorCallback(res.errCode, "");
        }
    })
    //停止搜索
    stopBluetoothDevicesDiscovery();
}

6. 获取蓝牙所有服务

wx.onBLEConnectionStateChange(function callback)

wx.getBLEDeviceServices(Object object)

注意有些供应商会返回多个服务,只要找自己需要的服务就好

  • 监听蓝牙连接状态,可以处理连接连接意外断开等情况

  • 获取需要的蓝牙服务后,再调用获取蓝牙特征值方法


设备返回数据效果图

假设: 需要的服务是包含EE0的,只需要过滤下就可以【这里通过indexOf

在这里插入图片描述


function getBLEDeviceServices(deviceId) {
    //监听低功耗蓝牙连接状态的改变事件
    wx.onBLEConnectionStateChange(function(res) {
        console.log("onBLEConnectionStateChange:", res);
        // 该方法回调中可以用于处理连接意外断开等异常情况
        console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
        if (res.connected == false) {
            console.log("连接意外断开等****", _deviceId);
            _deviceId = '';
            if (flagFromTypes == 1 && flagFromTypes == 2) {
                asddErrorCallback(1010, ""); 
            }
        }
    });

    //获取蓝牙所有service
    wx.getBLEDeviceServices({
        deviceId: deviceId,
        success: (res) => {
            // console.log("获取蓝牙设备所有服务(service)", res);
            for (let i = 0; i < res.services.length; i++) {
                let tmpUuid = res.services[i].uuid;
                if ((res.services[i].isPrimary) && (tmpUuid.indexOf(filterServiceUUID) != -1)) {
                    //获取蓝牙特征值
                    getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
                    return
                }
            }
        },
        fail: (res) => {
            console.log('getBLEDeviceServices fail', res);
            asddErrorCallback(res.errCode, "");
        }
    })
}

7. 获取某个服务中所有特征值

wx.getBLEDeviceCharacteristics(Object object)

wx.onBLECharacteristicValueChange(function callback)

注意: 返回数据中的properties 的结构, 里面返回该服务下的特征值是否支持readwritenotifyindicate操作。


返回数据结构效果图

在这里插入图片描述

注意:
  • 根据文档协议读、写、通知服务【前面定义的变量】,遍历对象来获取想要的特征值uuid处理相关逻辑

  • 启用低功耗蓝牙设备特征值变化时的notify功能,注意:必须设备的特征值支持notify 或者indicate 才可以成功调用。

  • 监听低功耗蓝牙设备的特征值变化事件。注意:必须先启用notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。

  • 监听onBLECharacteristicValueChange,第一时间获取设备返回的数据

  • 如果需要找到设备后,先获取一些设备信息操作,可以在write中发送相关指令

/**
 * 获取蓝牙特征值
 */
function getBLEDeviceCharacteristics(deviceId, serviceId) {
    console.log("设备:" + deviceId + '******************服务:' + serviceId);
    wx.getBLEDeviceCharacteristics({
        deviceId: deviceId,
        serviceId: serviceId,
        success: (res) => {
            // console.log('蓝牙设备特征值信息:', res);
            console.log('getBLEDeviceCharacteristics success', res.characteristics)
            for (let i = 0; i < res.characteristics.length; i++) {
                let item = res.characteristics[i]
                var itemUUID = item.uuid.toUpperCase(); //转大写

                //read操作
                if (item.properties.read && itemUUID == writeUUID) {
                    wx.readBLECharacteristicValue({
                        deviceId: deviceId,
                        serviceId: serviceId,
                        characteristicId: item.uuid,
                    })
                }
                
                //write操作
                if (item.properties.write && itemUUID == writeUUID) {
                    console.log("写 特征值 -----------------------" + item.uuid);
                    _deviceId = deviceId
                    _serviceId = serviceId
                    _characteristicId = item.uuid

                    //发送 信息查询指令 【根据需求】
                    if (flagFromTypes == 1 || flagFromTypes == 2) { //血压、秤
                        handleTimeToHex();
                    } 
                }

                    
				//notify操作,注意调用监听特征值变化
                if (notifyUUID == itemUUID) {
                    if (item.properties.notify || item.properties.indicate) {
                        console.log('调用notifyBLECharacteristicValueChange前', item.uuid);
                        wx.notifyBLECharacteristicValueChange({
                            deviceId: deviceId,
                            serviceId: serviceId,
                            characteristicId: item.uuid,
                            state: true,
                            success(res) {
                                console.log('notification通知数据', res);
                                status = true;
                                // wx.hideLoading();
                            },
                            fail(res) {
                                console.log('notifyBLECharacteristicValueChange fali', res);
                            }
                        })
                    }
                }
            }
        },
        fail: (res) => {
            console.log('getBLEDeviceCharacteristics fail', res)
            asddErrorCallback(res.errCode, "");
        }
    })


    // 操作之前先监听,保证第一时间获取数据
    wx.onBLECharacteristicValueChange(function(res) {
        console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)

        console.log("操作类型:" + action_type);

        var resData = ab2hex(res.value); //转16进制
        console.log("设备返回数据--->", resData); //5d0000000001be304d

        // 判断不同类型处理数据
        if (flagFromTypes == 1) {
            console.log('开始调用 血压计=====》处理返回的数据');
            bloodPressureObj.filterStr(resData);
        }
        
    })
}

8. 写入数据

wx.writeBLECharacteristicValue(Object object)

注意: 这里我用1秒延迟,如果马上发送数据,出现发送失败问题

  • 接收hex参数【下发指令数据】,向设备写入二进制数据。注意:注意:必须设备的特征值支持 write 才可以成功调用。
/**
 * 写入数据
 */
function writeData(hex, action = '') {
    if (!status) {
        return;
    }

    if (!_deviceId) {
        asddWriteErrors('w');
        return;
    }

    setTimeout(() => {
        //类型转换
        var enDataBuf = new Uint8Array(hex);
        var buffer1 = enDataBuf.buffer
        console.log("发送内容长度:", buffer1.byteLength)
        console.log('写入的数据:' + _deviceId + '服务serviceId---》' + _serviceId + '特征characteristicId---》' + _characteristicId);

        wx.writeBLECharacteristicValue({
            deviceId: _deviceId,
            serviceId: _serviceId,
            characteristicId: _characteristicId,
            value: buffer1,
            success: (res) => {
                wx.hideLoading();
                console.log("写数据返回结果", res.errMsg);
                
                //项目需求: 发送某个指令后的需要处理回调
                 if (action == 'lastZero') {
                    console.log('最后一次写入00需执行回调========》');
                    //回调 目的: 执行调用提交接口
                    eyeCareObj.eyeCareCallback();
                 }
            },
            fail(res) {
                console.log("写数据失败..", res);
                asddErrorCallback(res.errCode, "");
            }
        })
    }, 1000)
}

9. 断开蓝牙设备的连接

wx.closeBLEConnection
wx.closeBluetoothAdapter

注意: 断开蓝牙设备连接同时还要关闭蓝牙模块, 否则安卓设备下再次无法搜索到设备

/**
 * 断开蓝牙连接
 */
function closeBLEConnection() {
	//停止搜索
    stopBluetoothDevicesDiscovery();
    console.log("断开与低功耗蓝牙设备的连接。", deviceId);

    if (deviceId) {
        wx.closeBLEConnection({
            deviceId: deviceId,
            success: function(res) {
                console.log("closeBLEConnection。success", res);

            },
            fail: function(res) {
                console.log("closeBLEConnection。fail", res);
            },
            complete: function() {
                status = false;
            }
        })

        //关闭蓝牙模块
        wx.closeBluetoothAdapter({
            success: function(res) {
                console.log("closeBluetoothAdapter ==>res:", res);
            },
            fail: function(error) {
                console.log("closeBluetoothAdapter ==>error:", error);
            }
        })
    }

    _discoveryStarted = false;
    isnotExist = true;
    _deviceId = '';
    deviceId = '';
}

10. 错误码判断

10006错误码我单独处理, 自动重连操作

function bluetoothStatus(errorType) {
    switch (errorType) {
        case 10001:
            wx.showModal({
                title: '提示',
                content: '请检查手机蓝牙是否打开',
                showCancel: false
            })
            break;
        case 10002:
            wx.showToast({
                title: '没有找到指定设备',
                icon: 'none'
            })
            break;
        case 10003:
            wx.showToast({
                title: '连接失败',
                icon: 'none'
            })
            closeBLEConnection();
            break;
        case 10004:
            wx.showToast({
                title: '没有找到指定服务',
                icon: 'none'
            })
            closeBLEConnection();
            break;
        case 10005:
            wx.showToast({
                title: '没有找到指定特征值',
                icon: 'none'
            })
            closeBLEConnection();
            break;
        case 10007:
        case 10008:
        case 10013:
            wx.showToast({
                title: '设备启动失败,请重试',
                icon: 'none'
            })
            break;
        case 10009:
            wx.showModal({
                title: '提示',
                content: '当前系统版本过低,请更新版本体验',
                showCancel: false
            })
            break;
        case 10012:
            wx.showToast({
                title: '连接超时',
                icon: 'none'
            })
            break;
    }
}

处理数据方法
/**
 *  匹配规则: 取名称后面的mac地址
 *  mac地址: 假设C7:E2:90:17:1A:40
 * 	len: 截取长度为
 */
function getNameMac(macAddress, len, name) {
    let clearColonMac = clearSymbol(macAddress);
    let lastFourMac = clearColonMac.substring(clearColonMac.length - len);
    let strName = name.toUpperCase();
    strName = strName + lastFourMac.toUpperCase(); //转大写
    console.log('拼接后的' + strName); //abc_171A40
    return strName
}

/**
 * 去掉 冒号
 */
function clearSymbol(str) {
    str = str.replace(/:/g, ""); //取消字符串中出现的所有冒号
    return str;
}

/**
 * rrayBuffer转16进度字符串
 */
function ab2hex(buffer) {
    var hexArr = Array.prototype.map.call(
        new Uint8Array(buffer),
        function(bit) {
            return ('00' + bit.toString(16)).slice(-2)
        }
    )
    return hexArr.join('');
}

关于下篇内容

在实际中使用的案例。

  • 38
    点赞
  • 202
    收藏
    觉得还不错? 一键收藏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值