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

上篇主要介绍在开发过程中应用到相关API操作。接下来介绍个人在项目开发中应用蓝牙BLE一些事情。

由于时间比较仓促, 有些注释没那么详细。请理解~写的不好欢迎各位大神指点。

文章目录

  • 微信小程序蓝牙BLE开发实战(二)
      • 项目介绍
      • 效果图
      • 扫码使用`格子柜`流程
      • 蓝牙通讯协议
      • 加密理解
    • 实例(`蓝牙操作`功能)
      • 一. 新建`js`文件及使用时引入
        • 1. 定义全局变量
        • 2. 自定义方法
        • 3. 初始化蓝牙`入口`
        • 4. 应用到`蓝牙`API方法
        • 5. 处理`设备返回数据`及`操作指令`(如何发指令?)
        • 6. 处理回调一些方法
        • 7. 蓝牙连接过程中错误码
        • 8. 导出方法
      • 二 、在需要页面中调用`蓝牙`
        • 1. 引入文件
        • 2. 全局中定义处理回调函数
          • 全局方法中如何给`data`中属性赋值
        • 3. 调用执行`蓝牙`方法
        • 4. 执行开锁
      • BLE项目实战下载
      • 关于下篇内容

项目介绍

由于公司近阶段开发的共享产品比较多,有共享充电宝、陪护床等,这里就不一一介绍哈。大部分产品通过蓝牙通讯的。

快捷方便通过扫设备二维码进入小程序使用设备

  • 这里拿最近开发一个产品售卖机,生活中比较常见的一个,例如: 储物柜、格子柜等。
  • 特点: 一个售卖机中有n个小格子(根据产品分类)

效果图

扫码使用格子柜流程

售卖机上有设备的二维码(自己生成普通二维码),直接扫码

ps: 有个别标注链接的,是个人在项目开发中应用总结知识点

  • 微信扫普通二维码进入小程序,授权获取用户信息 用户授权成功后,检测用户是否打开蓝牙
  • 未打开时弹窗提示用户, 否则自动跳转到售卖机页面。
  • 售卖机页面会展示售卖商品(比如8个,那么就展示8个商品)
  • 选择需要购买的商品, 点击购买按钮
  • 支付金额
  • 支付完成后, 调用执行蓝牙方法开始搜索设备—>匹配设备—>连接设备...—>开锁成功(柜门打开) 【查看蓝牙具体流程】如连接设备失败,页面显示重连按钮
  • 拿出商品,关好柜门(可返回购买页,继续购买)。

蓝牙通讯协议

主要通过与供应商提供的蓝牙板子通讯协议和硬件对接,实现操控设备具备的功能。

  • 不同的供应商设备蓝牙通讯协议有所不同。反之产品不同通讯流程也不同。

    有些文档比较不规范,看起来会很茫然, 慢慢理解查资料

加密理解

基于数据安全性完整性等等,蓝牙协议通过会在有加密的过程,抵御攻击者。

密码之AES五种加密模式(CBC、ECB、CTR、OCF、CFB)

  • 下发某个指令时需要对数据进行加密,设备会返回数据,通过后再操作其他指令。
  • 下发指令加密, 得到设备返回数据后需要解密,如果验证通过,才可以继续操作后面流程。

不管使用哪种加密模式,会标注在协议文档或以其他方式说明

ps: 个人在项目中使用模式有 AES ECB 128CFB 128, 还有最近使用一种CRC(循环冗余校验)来校验。
AES加解密案例


实例(蓝牙操作功能)

说明:

  • 以下代码直接copy.js中就可以使用。(这里有分段说明下,按顺序复制即可)
  • 因在项目中对接设备比较多, 把蓝牙设备相关功能剥离到js文件中。
  • 注意: 开发工具上不支持调试蓝牙功能的
  • 【蓝牙API详情介绍请点击】

一. 新建js文件及使用时引入

1. 定义全局变量

根据个人需求适用

const app = getApp()
var utils = require('.././util.js');
var cryptoService = require('../bleFile/cryptoService.js'); //引入加密文件
var crc = require('../bleFile/crc.js'); //引入crc校验var serviceUUID = [] //主 service 的 uuid 列表
var writeUUID = ""; //写读 UUID
var notifyUUID = ""; //notify UUID
var delayUUID = ""; //护眼仪控制时长UUID
var filterServiceUUID = ""; //过滤获取到的服务uuid(有些会返回多条数据)
var filterDeviceName = ""; //设备名称var macAddress = ""; //保存得到mac地址
var flagFromTypes = ''; //来源类型(根据不同产品执行蓝牙协议)  例:1-->充电宝 2-->售卖机....
var _discoveryStarted = false;
var deviceId = ''; //用于区分设备的 idvar _deviceId = '';
var _serviceId = '';
var _characteristicId = '';
var status = false; //当前状态
var action_type = ''; //操作类型
var code = -1;
var isnotExist = true //防止多次执行var asddCallBack = null; //接收数据处理
var asddErrorCallback = null; //接收错误码处理
var asddWriteErrors = null; //接收写入错误处理var tempStr = ''; //拼接设备返回数据
var rssiVal = ''; //RSSI值
var currentSerialVal = ''; //当前点击格子柜序号//当前操作类型
var OptionEnum = {None: -1,Connection: 0, //连接Reconnection: 1, //重连CloseLock: 2, //关闭VendingOpenLock: 8, //打开售卖机ReadStatus: 9, //读售卖机状态ReadBattery: 10, //读售卖机电量
};
//这个是变量, 存储数据的变量
var action_type = OptionEnum.None;

2. 自定义方法

function inArray(arr, key, val) {for (let i = 0; i < arr.length; i++) {if (arr[i][key] === val) {return i;}}return -1;
}// ArrayBuffer转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('');
}/*** 去掉 冒号*/
function clearSymbol(str) {str = str.replace(/:/g, ""); //取消字符串中出现的所有冒号return str;
}/***  匹配规则: 取名称后面的mac*/
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);return strName
}/*** 区分不同类型的服务相关uuid* 1-->充电宝 2-->售卖机*/
function flagServiceIDFun(serviceType) {console.log('方法中拿到type======>', serviceType);if (serviceType == 1) {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"; //写读 UUIDnotifyUUID = "00*0**E1-00*0-*0*0-*0*0-00**5F9**4*B"; //notify UUIDfilterServiceUUID = "*E0";} else if (serviceType == 2) {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"; //写读 UUIDnotifyUUID = "00*0**E1-00*0-*0*0-*0*0-00**5F9**4*B"; //notify UUIDfilterServiceUUID = "*E0";//这里介绍用name匹配方法filterDeviceName = getNameMac(macAddress, 6, 'abc_'); //设备名称}
}

3. 初始化蓝牙入口

注意: 在使用.js文件中, 只需要调用initBle()方法即可实现蓝牙实现整个过程。【方法里面每个步骤都有调用对应方法】


说明: 所有asddErrorCallback(res.errCode, "");处理错误码回调, 在使用的页面中接收并处理回调函数。

/**
* 初始化蓝牙模块
* 参数1: mac地址
* 参数2: 当前操作类型
* 参数3: 当前点击格子的序号(第几个格子)
*/
function initBle(fromMac, flagTypes, currentSerial) {tempStr = ''; //清空//断开连接【每次初始化先断开连接】closeBLEConnection();console.log("你获取到mac地址了么....", fromMac + '获取到类型为====》' + flagTypes);// macAddress = clearSymbol(fromMac);macAddress = fromMac; //保存macflagFromTypes = flagTypes //类型来源//获取RSSI值let getRssi = wx.getStorageSync('rssi');rssiVal = getRssi;//调用主服务id相关flagServiceIDFun(flagTypes);// 获取当前点击售卖机序号值currentSerialVal = currentSerial_discoveryStarted = false;//初始化蓝牙模块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();//     }// })}})
}

4. 应用到蓝牙API方法

/*** 监听寻找新设备事件* 注意咯: 这里有展示三种不同方式来连接设备,请看备注【mac, name, 根据系统的】 各取所需吧。*/
function onBluetoothDeviceFound() {wx.onBluetoothDeviceFound((res) => {console.log('广播数据结果:', res);res.devices.forEach(device => {if (!device.name && !device.localName) {return}// 转换后, 根据协议文档取需要数据 (advertisData不一定有数据)if (device.advertisData != null) { //判断对象是否为null,advertisData不一定有var hexStr = ab2hex(device.advertisData);console.log("广播数据中转换后:advertisData---->" + hexStr);}// 充电宝 android用mac, ios用name匹配if (flagFromTypes == 1) {if (app.getPlatform() == 'android') {if ((macAddress != "") && (macAddress == device.deviceId) && isnotExist) {isnotExist = false;deviceId = device.deviceId;stopBluetoothDevicesDiscovery();//连接设备createBLEConnection();}} else if (app.getPlatform() == 'ios') {let deviceName = device.name.toUpperCase();if ((deviceName.indexOf(filterDeviceName) != -1) && isnotExist) {isnotExist = false;deviceId = device.deviceId;//停止搜寻附近的蓝牙外围设备stopBluetoothDevicesDiscovery();//连接设备createBLEConnection();}}} else if (flagFromTypes == 2) {//判断RSSI值在某个范围内时搜索设备,根据个人需求判断if (parseFloat(device.RSSI) > parseFloat(rssiVal)) {                // 格子柜Android IOS通过名称匹配let deviceName = device.name.toUpperCase();if ((deviceName.indexOf(filterDeviceName) != -1) && isnotExist) {isnotExist = false;deviceId = device.deviceId;//停止搜寻附近的蓝牙外围设备stopBluetoothDevicesDiscovery();//连接设备createBLEConnection();}}} })})
}/*** 执行连接蓝牙设备*/
function startBluetoothDevicesDiscovery() {if (_discoveryStarted) {return;}_discoveryStarted = truewx.startBluetoothDevicesDiscovery({services: serviceUUID,allowDuplicatesKey: false,success: (res) => {console.log('启动搜索蓝牙设备, 结果  :', res)//onBluetoothDeviceFound()  //先调用此方法再使用startBluetoothDevicesDiscovery},fail(res) {asddErrorCallback(res.errCode, "");console.log('startBluetoothDevicesDiscovery fail', res);}})
}//停止搜寻附近的蓝牙外围设备。
function stopBluetoothDevicesDiscovery() {wx.stopBluetoothDevicesDiscovery()
}/*** 连接蓝牙设备*/
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(); //停止搜索
}/*** 断开蓝牙连接*/
function closeBLEConnection() {//停止搜索stopBluetoothDevicesDiscovery();tempStr = ''; //清空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 = '';
}/*** 获取蓝牙所有服务*/
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, ""); //?}}});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, "");}})
}/*** 获取蓝牙特征值*/
function getBLEDeviceCharacteristics(deviceId, serviceId) {wx.getBLEDeviceCharacteristics({deviceId: deviceId,serviceId: serviceId,success: (res) => {// console.log('蓝牙设备特征值信息:', res);for (let i = 0; i < res.characteristics.length; i++) {let item = res.characteristics[i]var itemUUID = item.uuid.toUpperCase(); //转大写if (item.properties.read && itemUUID == writeUUID) {wx.readBLECharacteristicValue({deviceId: deviceId,serviceId: serviceId,characteristicId: item.uuid,})}if (item.properties.write && itemUUID == writeUUID) {_deviceId = deviceId_serviceId = serviceId_characteristicId = item.uuid//发送指令【说明:如需连接设备后发相关指令可以在这里调用】if (flagFromTypes == 1) { //充电宝powerBank.send(); //充电开机指令} else if (flagFromTypes == 2) { //售卖机vendingObj.checkEnKey(); //AES校验}}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);console.log("设备返回数据--->", resData); //5d0000000001be304d// 判断不同类型处理数据if (flagFromTypes == 2) {console.log('开始调用 自动售卖机====> 处理返回的数据');vendingObj.handleResData(resData); //处理返回数据}})
}/*** 写入数据*/
function writeData(hex, action = '') {if (!status) {return;}if (!_deviceId) {asddWriteErrors('w');return;}setTimeout(() => {//向设备特征值中写入二进制数据//这里使用`TypedArray视图`中`Uint8Array(无符号 8 位整数)`操作var enDataBuf = new Uint8Array(hex);var buffer1 = enDataBuf.bufferwx.writeBLECharacteristicValue({deviceId: _deviceId,serviceId: _serviceId,characteristicId: _characteristicId,value: buffer1,success: (res) => {wx.hideLoading();console.log("写数据返回结果", res.errMsg);},fail(res) {console.log("写数据失败..", res);asddErrorCallback(res.errCode, "");}})}, 1000)
}

5. 处理设备返回数据操作指令(如何发指令?)

售卖机为例, 注意: 交互流程指令格式根据文档说明

/*** 售卖机*/
var vendingObj = {/*** 校验AES密钥* 字符、key文档有说明*/checkEnKey: function() {status = true;var strKey = [0xd0, 0xf0, 0xf0, 0x80, 0x50, 0xa0, 0x60, 0x10, 0x20, 0x50, 0xc0, 0xd0, 0x80, 0x80, 0x40, 0x90];var key = [0xd0, 0xf0, 0xf0, 0x80, 0x50, 0xa0, 0x60, 0x10, 0x20, 0x50, 0xc0, 0xd0, 0x80, 0x80, 0x40, 0x90];var cryptoKey = new Uint8Array(key);enKEY = cryptoKey;//得出加密后的指令, 十六进制的数据var enHEX = cryptoService.updateEncrypt(strKey, enKEY);writeData(enHEX);},/*** 查询设备信息*/queryDeviceInfo: function() {action_type = OptionEnum.ReadBattery; //改变操作类型let hex = [0x69, 0xf2, 0x00, 0x89];writeData(hex); //写入数据},/*** 开锁指令*/openVendingLock: function(callBack) {status = true;action_type = OptionEnum.VendingOpenLock;asddCallBack = callBack;//获取当前开锁的编号及转换let getCurrentVal = Number(currentSerialVal);getCurrentVal = getCurrentVal.toString(16);// let tempVal = '0x0' + getCurrentVal;let tempVal = parseInt(getCurrentVal, 16);console.log('====开锁编号===》', tempVal);let hex = [0xfe, 0x4e, 0x30, 0x46, 0x4a, 0x00, tempVal, 0x00, 0x00, 0x3e];writeData(hex);},/*** 处理格子机查询信息电量回调* 目的:获取到相关数据,发送给后端(查看设备电量)*/readBatteryCallBack: function(battery) {console.log("=======>>>电量:", battery);action_type = OptionEnum.None;//这里获取到电量, 返回给index.js页面if (asddCallBack != null) {asddCallBack(battery, OptionEnum.ReadBattery);} else {console.log("是否为空=======标签 2");}},/*** 处理开锁成功回调*/openLockCallback: function(resData) {var isOpenLock = false;// 根据当前点击的柜子序号,截取对应的数据状态var star = Number(currentSerialVal) * 2var subSerial = resData.substring(star, star + 2);if (subSerial.toUpperCase() == 'F0') {isOpenLock = true;}console.log("=======>>>开锁:", isOpenLock ? "成功" : "失败");action_type = OptionEnum.None;if (asddCallBack != null) {asddCallBack(isOpenLock, OptionEnum.VendingOpenLock);} else {console.log("是否为空=======标签 3");}},/*** 处理返回数据* 例如: 00f05d09000001be304d*/handleResData: function (resData) {let checkStatus = resData.substring(2, 4);if (checkStatus.toUpperCase() == 'F0' && action_type == OptionEnum.Connection) { //校验状态vendingObj.queryDeviceInfo(); //查询设备信息} else if (action_type == OptionEnum.ReadBattery) { //操作的是获取设备电量let batteryVal = resData.substring(6, 8);batteryVal = parseInt(batteryVal, 16);vendingObj.readBatteryCallBack(batteryVal);} else if (action_type == OptionEnum.VendingOpenLock) { //操作的是 开锁vendingObj.openLockCallback(resData);}}
}

6. 处理回调一些方法

//设置连接
function setConnectionActionType(callBack) {action_type = OptionEnum.Connection;asddCallBack = callBack;
}//设置重连
function setReconnectionActionType() {action_type = OptionEnum.Reconnection;
}// 设置错误
function setAsddErrorCallback(callBack) {asddErrorCallback = callBack;
}//设置关闭
function setCloseActionType(callBack) {action_type = OptionEnum.CloseLock;asddCallBack = callBack;
}//设置写入错误
function setWriteError(callBack) {asddWriteErrors = callBack;
}//清除
function clearCallBack() {asddCallBack = null;
}/*** 清空loadding*/
function hideLoading() {wx.hideLoading();
}/*** 检查是否打开蓝牙* 未连接设备前检测*/
function checkIsOpenBluetooth(isEXec) {wx.openBluetoothAdapter({success: (res) => {// console.log('openBluetoothAdapter 初始化蓝牙模块是否成功:', res);isEXec(true);},fail: (res) => {// console.log('初始化蓝牙失败', res);wx.showModal({title: '提示',content: '请检查手机蓝牙是否打开',showCancel: false})isEXec(false);}})
}

7. 蓝牙连接过程中错误码

/*** 蓝牙连接过程中错误码* 10000 / 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;}
}

8. 导出方法

外部需使用的方法,注意导出。否则无法使用

module.exports = {initBle: initBle,clearCallBack: clearCallBack,closeBLEConnection: closeBLEConnection,setConnectionActionType: setConnectionActionType,setReconnectionActionType: setReconnectionActionType,setAsddErrorCallback: setAsddErrorCallback,setCloseActionType: setCloseActionType,setWriteError: setWriteError,checkIsOpenBluetooth: checkIsOpenBluetooth,bluetoothStatus: bluetoothStatus,openVendingLock: vendingObj.openVendingLock,
}

二 、在需要页面中调用蓝牙

1. 引入文件

const app = getApp()
let commonBLE = require('../../../../../utils/fromCategory/commonBLE.js'); //公共BLE
let commonBLEDatas = null; //保存当前页面对象

2. 全局中定义处理回调函数

全局方法中如何给data中属性赋值
  • 定义一个全局变量,来保存当前页面对象
  • onLoad()中给变量赋值, 即commonBLEDatas = this
  • 使用: 通过变量名.data.属性,即commonBLEDatas.data.battery = obj
/*** 处理返回部分数据回调函数* obj: 传过来的数据* types: 当前操作类型 【可在`commBLE.js`查看定义的】*/
let callBack = function(obj, types) {console.log("index.js回调函数" + obj, types);// commonBLE.clearCallBack();if (commonBLEDatas.data.currentMeasureType == 4) { //售卖机if (types == 10) {//给电量赋值commonBLEDatas.data.battery = obj;//调用开锁指令commonBLEDatas.successOpenLock();} }
}/*** 处理错误码回调*/
let errorCallBack = function(errorType, errorMessage) {// feedbackApi.hideTimerToast(); //清空loaddingcommonBLEDatas.deviceConnectFail(); //展示 设备连接失败if (errorType == 10000 || errorType == 10006) {//连接蓝牙commonBLE.setReconnectionActionType();commonBLE.setAsddErrorCallback(errorCallBack);commonBLE.setWriteError(writeError);if (commonBLEDatas.data.currentMeasureType == 4) {commonBLE.initBle(commonBLEDatas.data.macAdress, OptionEnumType.Vending, commonBLEDatas.data.currentSerial);} } else {commonBLE.bluetoothStatus(errorType);}
}/*** 写入失败回调*/
let writeError = function(writeE) {console.log('写入数据状态', writeE);//写入错误页面展示状态commonBLEDatas.setData({connectStatus: 1,isConnect: 2,clickFlags: false})if (writeE == 'w') {feedbackApi.hideTimerToast(); //清空loaddingclearInterval(commonBLEDatas.downSecondId); //清空倒计时wx.showToast({title: '连接失败,请再次尝试(0)',icon: 'none'})commonBLE.closeBLEConnection();}
}

3. 调用执行蓝牙方法

  • 点击购买商品按钮, 执行此方法。
  • 传参: 当前设备的mac地址(后台获取的)操作的产品类型操作序号(操作几号柜子)

说明: 柜子序号1,2,3,4…, 和页面展示商品对应的。

Page({/*** 页面的初始数据*/data: {currentMeasureType: 2, //测试 当前类型售卖机macAdress: '3b3533115510', //mac地址(这里后台保存时没有:号的,可自行处理)currentSerial: '', //当前操作格子序号},/*** 生命周期函数--监听页面加载*/onLoad: function(options) {commonBLEDatas = this;},/*** 连接BLE*/connectVendingBLE: function() {let that = this;commonBLE.setConnectionActionType(callBack); //连接后操作回调commonBLE.setAsddErrorCallback(errorCallBack); //执行错误时错误码回调commonBLE.setWriteError(writeError); //写入数据错误回调if (that.data.currentMeasureType == 2) {commonBLE.initBle(that.data.macAdress, OptionEnumType.Vending, that.data.currentSerial);} },/*** 设备连接失败*/deviceConnectFail: function () {// feedbackApi.hideTimerToast(); //清空loaddingclearInterval(this.downSecondId); //清空倒计时this.setData({descTxt2: '设备连接失败',connectStatus: 1,isConnect: 2,clickFlags: false})},
})

4. 执行开锁

如获取电量成功后, 执行开锁指令,成功后页面展示对应【具体流程根据需求来】

/**
* 售卖机开锁成功后提交数据  并展示相关的ui
* 写在 Page({})中的, 这里为了说明写在外面
*/
successOpenLock: function() {let that = this;// 调用开锁及处理回调数据commonBLE.openVendingLock(function(isOpenLock, obj) {console.log('处理开锁指令回调====》' + isOpenLock);commonBLE.clearCallBack();var tempStr = "失败";if (isOpenLock) {tempStr = "成功";// 提交数据并展示成功开锁后Uithat.showSuccessBack();}else {wx.showToast({title: "开锁" + tempStr,duration: 3000,image: '/page/common/libs/assets/images/error.png',})that.deviceConnectFail(); //展示 设备连接失败//断开蓝牙连接commonBLE.closeBLEConnection();}});
},
 <button type="primary" bindtap="connectVendingBLE">购买商品</button>

BLE项目实战下载

蓝牙BLE项目实战

关于下篇内容

  • BLE开发中遇到问题及踩坑

微信小程序蓝牙BLE开发实战——案例(二)相关推荐

  1. 微信小程序蓝牙BLE开发——关于进制转换(四)

    微信小程序蓝牙BLE开发--进制转换 这段时间开发共享设备,对接蓝牙BLE设备通信协议,过程中用到一些进制转换, 记录下方便使用. 有些参考大神们,感谢分享. 文章目录 微信小程序蓝牙BLE开发--进 ...

  2. 微信小程序蓝牙BLE开发——写入一串16进制数据,低字节在前(五)

    微信小程序BLE发送一串16进制数据,低字节在前 文章目录 微信小程序BLE发送一串16进制数据,低字节在前 场景 获取UTC时间 写入数据 向设备发送16进制数据 字节转换 验证发送UTC时间 场景 ...

  3. 微信小程序 - 蓝牙BLE小程序开发

    1.前言 最近领导看我比较闲,安排我开发一个蓝牙BLE微信小程序,刚开始接到这个项目时,我第一反应时,"卧槽"".老子在公司的岗位是做Windows和Android 软件 ...

  4. 李艺《微信小程序全栈开发实战》(第一章)

    李艺<微信小程序全栈开发实战>(第一章) 双线程运行机制 小程序的特点及开发能力 小程序的特点 小程序的开发能力 开发小程序的一般流程 小程序的运行机制 小程序双线程 视图的持续更新是如何 ...

  5. “喜茶Go”微信小程序新零售商业实战案例经验分享-概述

    作者介绍:郝天翔,10年移动互联网软件行业经验,全栈高级软件工程师,微信生态开发先行者,主要参与智慧城市新零售电商领域方案的设计与实施,本次分享商业案例"喜茶Go"已落地于全国多座 ...

  6. 微信小程序蓝牙功能开发与问题记录

    一.蓝牙支持情况 1. 微信小程序对蓝牙的支持情况 目前普遍使用的蓝牙规格:经典蓝牙和蓝牙低功耗. 经典蓝牙(蓝牙基础率/增强数据率):常用在对数据传输带宽有一定要求的大数据量传输场景上,比如需要传输 ...

  7. 微信小程序支付组件开发实战

    该文章纪录了我在开发小程序支付过程中的具体流程 1. 申请微信支付 小程序认证后进入微信支付申请小程序的微信支付 填写企业信息对公账户并上传凭证后,微信支付会打一笔随机金额到对公账户,输入金额完成验证 ...

  8. Udesk微信小程序即时通讯开发入门(二、代码篇)

    作者:张振琦 开发微信小程序,需要下载<微信开发者工具>,在微信官方文档小程序.工具.下载里面可以找到.安装好后,新建一个小程序项目,填写自己的AppId,会生成一个默认的项目. 默认项目 ...

  9. 乐鑫Esp32学习之旅 19 重磅开源,如何在微信小程序上ble蓝牙配网esp32,blufi的那些事!

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 " ...

最新文章

  1. 第111天:Ajax之jQuery实现方法
  2. Py中的类型注解【转载】
  3. python埋点测试_埋点进化论:从埋点到无埋点
  4. 云栖专辑 | 阿里开发者们的第6个感悟:享受折磨
  5. 输出语句 WriteLine 与格式化输出 1006 c#
  6. OpenShift Security (11) - 用RHACS在DevOps的CICD中扫描部署中的安全风险
  7. 【POJ 1860】Currency Exchange
  8. 在powerDesigner中通过SQL生成pdm
  9. 扇贝有道180915每日一句
  10. vue小案例---cnode
  11. 撞线百亿后,良品铺子峥嵘毕现?
  12. 清明假期,超市可重点主推的品类
  13. java用画正弦函数_用java语言绘制正弦曲线
  14. python飞机大战程序导入_Python飞机大战项目的准备 导入Pygame最全攻略~
  15. 光荣特库摩游戏《莱莎的炼金工房》宣布动画化,2023 年 7 月开播
  16. 从iTunes获取App信息
  17. 如何更换戴尔 燃7000 Dell Inspiron 7460的电池
  18. 习题9-2(免费糖果)【深搜dfs】+【记忆化搜索】
  19. (三)linux文件与目录管理
  20. 数据归档,存储的完美储备军

热门文章

  1. ERROR 1045 (28000): Access denied for user 'ODBC'@'localhost' (using password: N O) MYSQL
  2. 测试基础篇之(postman接口和Fiddler测试)
  3. 云漫圈 | 漫画:什么是加密算法?
  4. 流量思维:拉新、留存、转化、裂变-运营设计(0)
  5. 【转帖】关于找工作的鸡零狗碎(续)
  6. Java初学者应当做什么?
  7. 75.android 简单的获取当前可用运行内存,总运行内存,获取包含系统软件在内的所有内存,获取系统参数显示的内存大小。
  8. OJ每日一练——求平均年龄
  9. 从纪念碑谷的收益看小而美的游戏的盈利困境
  10. spring cloud 2020.0.3 学习记录(四)配置中心config以及bus实现自动配置更新