关于前端:微信小程序-BLE-蓝牙封装

42次阅读

共计 5676 个字符,预计需要花费 15 分钟才能阅读完成。

为了简化微信小程序环境下的蓝牙接入流程,通过线上正式我的项目一年的运行,发现 BLE 这块 API 许多坑,且难以移植复用,所以将它封装进去进步可维护性以及可移植性。

如何应用

装置 Eventenitter

npm install eventemitter2 --save

引入

在我的项目根目录 utils 文件夹下增加如下文件:ble.js、bleHandler.js、tools.js、error.js
实现下面步骤,就能够间接在小程序中应用蓝牙性能了。✨

示例

const emitter = new EventEmitter2();
const ble = new BLE(blename, emitter)

ble.listen(res => {if (res.type == 'connect') {switch(res.data){
        case "未关上适配器":break
      case "蓝牙已连贯":break
      case ""
        break
    }
  }else if (res.type == "response") {console.log('收到设施音讯响应:', res)
    //TODO
  }
})

ble.init()

实现细节

应用办法如上,很简略,只须要保护一个全局的 ble 实例,则能够进行蓝牙的各种性能操作。第二部引入的那几个文件是用来干嘛的呢?
大体上将蓝牙的连贯、通信、保护过程按性能的复杂程度分为三层:BLE、BLEHandler、Tool,ble 更偏差用户层,blehandler 提供一些流程性管制,tool 则齐全是封装的微信 API,隔离一些简约的工作,使代码看起来简洁一些。

源码解析

import BLEHandler from "./bleHandler"

class BLE extends BLEHandler {constructor(blename, emitter) {super(blename, emitter)
    }
    listen(callback) {
        // 蓝牙事件注册, 关上 channel
        this.emitter.removeAllListeners("channel")
        this.emitter.on("channel", callback)
    }
    removeListen() {
        // 移除所有蓝牙事件
        this.emitter.removeAllListeners("channel")
    }
    async init() {
        let flow = false
        // 关上蓝牙适配器状态监听
        this.onBLEConnectionStateChange()
        // 蓝牙适配器初始化
        await this.openAdapter()
        // 搜寻蓝牙设施
        await this.startSearch()
        // 获取设施 ID
        flow = await this.onBluetoothFound()
        // 进行搜寻设施
        await this.stopSearchBluetooth()
        if (!flow) return
        // 连贯蓝牙
        await this.connectBlue();
        // 获取 serviceId
        await this.getBLEServices()
        // 设置特征值
        await this.getCharacteristics();
        // 订阅特征值
        await this.notifyBLECharacteristicValueChange()
        // 关上传输监听,期待设施反馈数据
        this.onBLECharacteristicValueChange()}
    // 发送指令
    async send(mudata, cmd) {let flow = await this.sentOrder(mudata, cmd)
        return flow
    }
    async close() {await this.closeBLEConnection()
        await this.closeBLEAdapter()}

}

export {BLE};

BLEHandler(promise 的封装,及 Eventenitter 通信管制)

import * as t from "./tools"
import {HTTP} from "../server";

/**
 * 蓝牙工具类
 * 封装小程序蓝牙流程办法
 * 处理事件通信
 */
class BLEHandler {constructor(blename, emitter) {
        this.blename = blename
        this.emitter = emitter
        this.readCharacteristicId = "";
        this.writeCharacteristicId = "";
        this.notifyCharacteristicId = "";
        this.deviceId = "";
        this.serviceId = "";
        this.lastDate = new Date().getTime()
    }
    async openAdapter() {let [err, res] = await t._openAdapter.call(this);
        if (err != null) {
            this.emitter.emit("channel", {
                type: "connect",
                data: "未关上适配器"
            })
            return;
        }
        return true
    }
    async startSearch() {let [err, res] = await t._startSearch.call(this);
        if (err != null) {return;}
        this.emitter.emit("channel", {
            type: "connect",
            data: "蓝牙搜寻中"
        })

    }
    async onBluetoothFound() {let [err, res] = await t._onBluetoothFound.call(this);
        if (err != null) {
            this.emitter.emit("channel", {
                type: "connect",
                data: "未找到设施"
            })
            // 勾销适配器
            this.closeBLEAdapter()
            wx.setStorageSync("bluestatus", "");
            return;
        }
        this.emitter.emit("channel", {
            type: "connect",
            data: "正在连接中"
        })
        return true
    }
    async stopSearchBluetooth() {let [err, res] = await t._stopSearchBluetooth.call(this);
        if (err != null) {return;}
    }
    async connectBlue() {let [err, res] = await t._connectBlue.call(this);
        if (err != null) {return;}
    }
    async getBLEServices() {let [err, res] = await t._getBLEServices.call(this);
        if (err != null) {return;}
    }
    async getCharacteristics() {let [err, res] = await t._getCharacteristics.call(this);
        if (err != null) {
            this.emitter.emit("channel", {
                type: "connect",
                data: "无奈订阅特征值"
            })
            // 勾销连贯
            this.closeBLEConnection()
            this.closeBLEAdapter()
            wx.setStorageSync("bluestatus", "");
            return;
        }
        return true
    }
    async notifyBLECharacteristicValueChange() {let [err, res] = await t._notifyBLECharacteristicValueChange.call(this);
        if (err != null) {
            // 勾销连贯
            this.emitter.emit("channel", {
                type: "connect",
                data: "无奈订阅特征值"
            })
            this.closeBLEConnection()
            this.closeBLEAdapter()
            wx.setStorageSync("bluestatus", "");
            return;
        }
        this.emitter.emit("channel", {
            type: "connect",
            data: "蓝牙已连贯"
        })
        wx.setStorageSync("bluestatus", "on");
        return true
    }
    async closeBLEConnection() {let [err, res] = await t._closeBLEConnection.call(this);
        if (err != null) {return;}
    }
    async closeBLEAdapter() {let [err, res] = await t._closeBLEAdapter.call(this);
        if (err != null) {return;}
    }
    async sentOrder(mudata, cmd) {let data = t._sentOrder(mudata, cmd)
        console.log("-- 发送数据:", data)
        let arrayBuffer = new Uint8Array(data).buffer;
        let [err, res] = await t._writeBLECharacteristicValue.call(this, arrayBuffer)
        if (err != null) {return}
        return true

    }

    // 关上蓝牙适配器状态监听
    onBLEConnectionStateChange() {
        wx.onBLEConnectionStateChange(res => {
            // 该办法回调中能够用于解决连贯意外断开等异常情况
            if (!res.connected) {this.closeBLEAdapter()
                wx.setStorageSync("bluestatus", "");
                this.emitter.emit("channel", {
                    type: "connect",
                    data: "蓝牙已断开"
                })
            }
        }, err => {console.log('err', err)
        })
    }

    // 收到设施推送的 notification
    onBLECharacteristicValueChange() {
        wx.onBLECharacteristicValueChange(res => {let arrbf = new Uint8Array(res.value)
            console.log("收到上传数据:",arrbf)
            console.log("工夫戳",new Date().getTime())
            arrbf.map(res=>{console.log(res)
            })
            if (this._checkData(arrbf)) {if (arrbf[3] != 0x00) {let nowDate = new Date().getTime()
                    if ((nowDate - this.lastDate) > 900) {console.log('-- 节流 900ms,Lock!')
                        this.lastDate = nowDate
                        this._uploadInfo(arrbf)
                        this.emitter.emit("channel", {
                            type: "response",
                            data: arrbf
                        })
                    }
                }
            }
        })
    }
    _uploadInfo(message) {console.log("-- 筹备数据同步!", this._mapToArray(message))
        let bleorder = wx.getStorageSync("bleorder");
        let blecabinet = wx.getStorageSync("blecabinet")
        HTTP({
            url: "cabinet/uploadBlueData",
            methods: "post",
            data: {
                cabinetQrCode: blecabinet,
                order: bleorder,
                message: this._mapToArray(message)
            }
        }).then(res => {console.log("✔ 数据同步胜利!")

        }, err => {console.log('✘ 数据同步失败', err)
        })
    }
    _mapToArray(arrbf) {let arr = []
        arrbf.map(item => {arr.push(item)
        })
        return arr
    }
    // 校验数据正确性
    _checkData(arrbf) {
        // 校验帧头帧尾
        if (arrbf[0] != 0xEE || arrbf[1] != 0xFA || arrbf[arrbf.length - 1] != 0xFF || arrbf[arrbf.length - 2] != 0xFC) {console.log('✘ 帧头帧尾不匹配,请重发')
            console.log('帧头:', arrbf[0])
            console.log('帧头:', arrbf[1])
            console.log('帧尾:', arrbf[arrbf.length - 1])
            console.log('帧尾:', arrbf[arrbf.length - 2])
            return false
        }
        // 校验 CRC
        let crc = t._modBusCRC16(arrbf, 2, arrbf.length - 5)
        if (arrbf[arrbf.length - 3] != crc & 0xff && arrbf[arrbf.length - 4] != (crc >> 8) & 0xff) {console.log('✘ crc 校验谬误,请重发')
            return false
        }
        let time = new Date().toLocaleTimeString()
        console.log(`✔ CRC 数据校验胜利!${arrbf[3] == 0 ? '❤' : '命令码:' + arrbf[3]},time:${time}`)
        return true
    }

}
export default BLEHandler

流程图

我的项目地址:https://github.com/arsize/ble

正文完
 0