Nombre: wx-simple-bluetooth
Plataformas aplicables: subprograma WeChat
Bluetooth: Bluetooth de baja energía
Versión del proyecto: 2.0.1
Este proyecto está diseñado a partir de tres niveles: conexión Bluetooth, comunicación por protocolo Bluetooth, suscripción de estado y notificación. Puede personalizar fácilmente el desarrollo de Bluetooth de su propio mini programa. Las funciones principales son las siguientes:
¡¡¡Primero debes habilitar la compilación mejorada de las herramientas de desarrollo de WeChat!!!
这个项目从蓝牙连接、蓝牙协议通信、状态订阅及通知三个层面进行设计,可以很方便的定制您自己的小程序的蓝牙开发。主要功能如下
:
El siguiente es el caso cuando la función Bluetooth, el GPS y los permisos de posicionamiento de WeChat están activados en el teléfono móvil:
getAppBLEManager.connect()
escaneará automáticamente los dispositivos Bluetooth circundantes cada 350ms
y se conectará al dispositivo con la señal más fuerte dentro de ese tiempo.uuid
del service
principal y el ID de servicio correspondiente utilizado para la comunicación; también puede agregar nombres de dispositivos Bluetooth adicionales para filtrar aún más los dispositivos;注意:目前在发送数据时大于20包的数据会被裁剪为20包
. (有些蓝牙连接问题是微信兼容或是手机问题,目前是无法解决的。如错误码10003以及部分华为手机蓝牙连接或重连困难。如果您有很好的解决方案,还请联系我,十分感谢)
lb-example-bluetooth-manager.js
para obtener más detalles.Por lo tanto, con base en esta consideración, este marco tiene las siguientes restricciones:
- El acuerdo debe formularse de acuerdo con el formato de acuerdo acordado para poder utilizar el marco normalmente.
- El número máximo de datos en un paquete del protocolo es de 20 bytes. El marco no admite formatos de protocolo de más de 20 bytes. Si los datos superan el límite, se recomienda dividirlos en varios envíos.
- Se recomienda realizar operaciones de escritura en modo serie.
- Se recomienda comprender primero la documentación oficial de Bluetooth del miniprograma para facilitar la comprensión del uso del marco.
协议约定格式:[...命令字之前的数据(非必需), 命令字(必需), ...有效数据(非必需 如控制灯光发送255,255,255), 有效数据之后的数据(非必需 如协议结束标志校、验位等)
协议格式示例:[170(帧头), 10(命令字), 1(灯光开启),255,255,255(三个255,白色灯光),233(协议结束标志,有的协议中没有这一位),18(校验位,我胡乱写的)]
有效数据是什么:
在刚刚的这个示例中,帧头、协议结束标志是固定的值,校验位是按固定算法生成的,这些不是有效数据。而1,255,255,255这四个字节是用于控制蓝牙设备的,属于有效数据。
Si este proyecto es útil, espero darle una estrella en GitHub.
modules
debajo del proyecto a su proyecto import Toast from "../../view/toast";
import UI from './ui';
import {ConnectState} from "../../modules/bluetooth/lb-bluetooth-state-example";
import {getAppBLEProtocol} from "../../modules/bluetooth/lb-example-bluetooth-protocol";
import {getAppBLEManager} from "../../modules/bluetooth/lb-example-bluetooth-manager";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
connectState: ConnectState.UNAVAILABLE
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.ui = new UI(this);
console.log(app);
//监听蓝牙连接状态、订阅蓝牙协议接收事件
//多次订阅只会在最新订阅的函数中生效。
//建议在app.js中订阅,以实现全局的事件通知
getAppBLEManager.setBLEListener({
onConnectStateChanged: async (res) => {
const {connectState} = res;
console.log('蓝牙连接状态更新', res);
this.ui.setState({state: connectState});
switch (connectState) {
case ConnectState.CONNECTED:
//在连接成功后,紧接着设置灯光颜色和亮度
//发送协议,官方提醒并行调用多次会存在写失败的可能性,所以建议使用串行方式来发送
await getAppBLEProtocol.setColorLightAndBrightness({
brightness: 100,
red: 255,
green: 0,
blue: 0
});
break;
default:
break;
}
},
/**
* 接收到的蓝牙设备传给手机的有效数据,只包含你最关心的那一部分
* protocolState和value具体的内容是在lb-example-bluetooth-protocol.js中定义的
*
* @param protocolState 蓝牙协议状态值,string类型,值是固定的几种,详情示例见:
* @param value 传递的数据,对应lb-example-bluetooth-protocol.js中的{effectiveData}字段
*/
onReceiveData: ({protocolState, value}) => {
console.log('蓝牙协议接收到新的 protocolState:', protocolState, 'value:', value);
}
});
//这里执行连接后,程序会按照你指定的规则(位于getAppBLEManager中的setFilter中指定的),自动连接到距离手机最近的蓝牙设备
getAppBLEManager.connect();
},
/**
* 断开连接
* @param e
* @returns {Promise<void>}
*/
async disconnectDevice(e) {
// closeAll() 会断开蓝牙连接、关闭适配器
await getAppBLEManager.closeAll();
this.setData({
device: {}
});
setTimeout(Toast.success, 0, '已断开连接');
},
/**
* 连接到最近的设备
*/
connectHiBreathDevice() {
getAppBLEManager.connect();
},
async onUnload() {
await getAppBLEManager.closeAll();
},
});
setFilter
y reglas de filtrado de escaneo (opcional) El archivo se encuentra en ./modules/bluetooth/lb-example-bluetooth-manager.js
import {LBlueToothManager} from "./lb-ble-common-connection/index";
import {getAppBLEProtocol} from "./lb-example-bluetooth-protocol";
/**
* 蓝牙连接方式管理类
* 初始化蓝牙连接时需筛选的设备,重写蓝牙连接规则
*/
export const getAppBLEManager = new class extends LBlueToothManager {
constructor() {
super();
//setFilter详情见父类
super.setFilter({
services: ['0000xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'],//必填
targetServiceArray: [{
serviceId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',//必填
writeCharacteristicId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxE',//必填
notifyCharacteristicId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxF',//必填
readCharacteristicId: '',//非必填
}],
targetDeviceName: '目标蓝牙设备的广播数据段中的 LocalName 数据段,如:smart-voice',//非必填,在判断时是用String.prototype.includes()函数来处理的,所以targetDeviceName不必是全称
scanInterval: 350//扫描周围设备,重复上报的时间间隔,毫秒制,非必填,默认是350ms
});
super.initBLEProtocol({bleProtocol: getAppBLEProtocol});
//setMyFindTargetDeviceNeedConnectedFun函数调用可选,不实现过滤规则框架会按默认规则执行
super.setMyFindTargetDeviceNeedConnectedFun({
/**
* 重复上报时的过滤规则,并返回过滤结果
* 在执行完该过滤函数,并且该次连接蓝牙有了最终结果后,才会在下一次上报结果回调时,再次执行该函数。
* 所以如果在一次过滤过程中或是连接蓝牙,耗时时间很长,导致本次连接结果还没得到,就接收到了下一次的上报结果,则会忽略下一次{scanFilterRuler}的执行。
* 如果不指定这个函数,则会使用默认的连接规则
* 默认的连接规则详见 lb-ble-common-connection/utils/device-connection-manager.js的{defaultFindTargetDeviceNeedConnectedFun}
* @param devices {*}是wx.onBluetoothDeviceFound(cb)中返回的{devices}
* @param targetDeviceName {string}是{setFilter}中的配置项
* @returns targetDevice 最终返回对象{targetDevice},是数组{devices}其中的一个元素;{targetDevice}可返回null,意思是本次扫描结果未找到指定设备
*/
scanFilterRuler: ({devices, targetDeviceName}) => {
console.log('执行自定义的扫描过滤规则');
const tempFilterArray = [];
for (let device of devices) {
if (device.localName?.includes(targetDeviceName)) {
tempFilterArray.push(device);
}
}
if (tempFilterArray.length) {
const device = tempFilterArray.reduce((pre, cur) => {
return pre.RSSI > cur.RSSI ? pre : cur;
});
return {targetDevice: device};
}
return {targetDevice: null};
}
})
}
/**
* 获取本机蓝牙适配器状态
* @returns {Promise<*>} 返回值见小程序官网 wx.getBluetoothAdapterState
*/
async getBLEAdapterState() {
return await super.getBLEAdapterState();
}
/**
* 获取最新的蓝牙连接状态
* @returns {*}
*/
getBLELatestConnectState() {
return super.getBLELatestConnectState();
}
}();
El archivo se encuentra en ./modules/bluetooth/lb-ble-example-protocol-body
//send-body.js
import {IBLEProtocolSendBody} from "../lb-ble-common-protocol-body/index";
import {HexTools} from "../lb-ble-common-tool/index";
/**
* 组装蓝牙协议发送数据示例
* 该框架的蓝牙协议必须按照约定格式来制定,最多20个字节
*/
export default class SendBody extends IBLEProtocolSendBody {
getDataBeforeCommandData({command, effectiveData} = {}) {
//有效数据前的数据 该示例只返回了帧头110
return [110];
}
getDataAfterEffectiveData({command, effectiveData} = {}) {
//协议结束标志
const endFlag = 233;
//该示例中checkSum的生成规则是计算协议从第0个元素累加到结束标志
let checkSum = endFlag + HexTools.hexToNum(command);
for (let item of this.getDataBeforeCommandData()) {
checkSum += item;
}
for (let item of effectiveData) {
checkSum += item;
}
//生成有效数据之后的数据
return [endFlag, checkSum];
}
}
//receive-body.js
import {IBLEProtocolReceiveBody} from "../lb-ble-common-protocol-body/index";
/**
* 组装蓝牙协议接收数据示例
* 该框架的蓝牙协议必须按照约定格式来制定,最多20个字节
*/
export default class ReceiveBody extends IBLEProtocolReceiveBody {
constructor() {
//commandIndex 命令字位置索引
//effectiveDataStartIndex 有效数据开始索引,比如:填写0,{getEffectiveReceiveDataLength}中返回20,则会在{LBlueToothProtocolOperator}的子类{getReceiveAction}实现中,在参数中返回所有数据
super({commandIndex: 1, effectiveDataStartIndex: 0});
}
/**
* 获取有效数据的字节长度
* 该长度可根据接收到的数据动态获取或是计算,或是写固定值均可
* 有效数据字节长度是指,在协议中由你的业务规定的具有特定含义的值的总字节长度
* 有效数据更多的说明,以及该长度的计算规则示例,见 IBLEProtocolReceiveBody 类的 {getEffectiveReceiveData}函数
*
* @param receiveArray 接收到的一整包数据
* @returns {number} 有效数据的字节长度
*/
getEffectiveReceiveDataLength({receiveArray}) {
return 20;
}
}
Ubicado en modules/bluetooth/lb-example-bluetooth-protocol.js
import {LBlueToothProtocolOperator} from "./lb-ble-common-protocol-operator/index";
import SendBody from "./lb-ble-example-protocol-body/send-body";
import ReceiveBody from "./lb-ble-example-protocol-body/receive-body";
import {ProtocolState} from "./lb-bluetooth-state-example";
/**
* 蓝牙协议管理类
* 在这个类中,以配置的方式来编写读操作和写操作
* 配置方式见下方示例
*/
export const getAppBLEProtocol = new class extends LBlueToothProtocolOperator {
constructor() {
super({protocolSendBody: new SendBody(), protocolReceiveBody: new ReceiveBody()});
}
/**
* 写操作(仅示例)
*/
getSendAction() {
return {
/**
* 0x01:设置灯色(写操作)
* @param red 0x00 - 0xff
* @param green 0x00 - 0xff
* @param blue 0x00 - 0xff
* @returns {Promise<void>}
*/
'0x01': async ({red, green, blue}) => {
return await this.sendProtocolData({command: '0x01', effectiveData: [red, green, blue]});
},
/**
* 0x02:设置灯亮度(写操作)
* @param brightness 灯亮度值 0~100 对应最暗和最亮
* @returns {Promise<void>}
*/
'0x02': async ({brightness}) => {
//data中的数据,填写多少个数据都可以,可以像上面的3位,也可以像这条6位。你只要能保证data的数据再加上你其他的数据,数组总长度别超过20个就行。
return await this.sendProtocolData({command: '0x02', effectiveData: [brightness, 255, 255, 255, 255, 255]});
},
}
}
/**
* 读操作(仅示例)
* {dataArray}是一个数组,包含了您要接收的有效数据。
* {dataArray}的内容是在lb-ble-example-protocol-body.js中的配置的。
* 是由您配置的 dataStartIndex 和 getEffectiveReceiveDataLength 共同决定的
*/
getReceiveAction() {
return {
/**
* 获取设备当前的灯色(读)
* 可return蓝牙协议状态protocolState和接收到的数据effectiveData,
* 该方法的返回值,只要拥有非空的protocolState,该框架便会同步地通知前端同protocolState类型的消息
* 当然是在你订阅了setBLEListener({onReceiveData})时才会在订阅的地方接收到消息。
*/
'0x10': ({dataArray}) => {
const [red, green, blue] = dataArray;
return {protocolState: ProtocolState.RECEIVE_COLOR, effectiveData: {red, green, blue}};
},
/**
* 获取设备当前的灯亮度(读)
*/
'0x11': ({dataArray}) => {
const [brightness] = dataArray;
return {protocolState: ProtocolState.RECEIVE_BRIGHTNESS, effectiveData: {brightness}};
},
/**
* 接收到设备主动发送的灯光关闭消息
* 模拟的场景是,用户关闭了设备灯光,设备需要主动推送灯光关闭事件给手机
*/
'0x12': () => {
//你可以不传递effectiveData
return {protocolState: ProtocolState.RECEIVE_LIGHT_CLOSE};
},
/**
* 接收到蓝牙设备的其他一些数据
*/
'0x13': ({dataArray}) => {
//do something
//你可以不返回任何值
}
};
}
/**
* 设置灯亮度和颜色
* @param brightness
* @param red
* @param green
* @param blue
* @returns {Promise<[unknown, unknown]>}
*/
async setColorLightAndBrightness({brightness, red, green, blue}) {
//发送协议,小程序官方提醒并行调用多次会存在写失败的可能性,所以建议使用串行方式来发送,哪种方式由你权衡
//但我这里是并行发送了两条0x01和0x02两条协议,仅演示用
return Promise.all([this.sendAction['0x01']({red, green, blue}), this.sendAction['0x02']({brightness})]);
}
}();
El archivo se encuentra en modules/bluetooth/lb-bluetooth-state-example.js
import {CommonConnectState, CommonProtocolState} from "./lb-ble-common-state/index";
//特定的蓝牙设备的协议状态,用于拓展公共的蓝牙协议状态
//使用场景:
//在手机接收到蓝牙数据成功或失败后,该框架会生成一条消息,包含了对应的蓝牙协议状态值{protocolState}以及对应的{effectiveData}(effectiveData示例见 lb-example-bluetooth-protocol.js),
//在{setBLEListener}的{onReceiveData}回调函数中,对应参数{protocolState}和{value}(value就是effectiveData)
const ProtocolState = {
...CommonProtocolState,
RECEIVE_COLOR: 'receive_color',//获取到设备的颜色值
RECEIVE_BRIGHTNESS: 'receive_brightness',//获取到设备的亮度
RECEIVE_LIGHT_CLOSE: 'receive_close',//获取到设备灯光关闭事件
};
export {
ProtocolState, CommonConnectState as ConnectState
};
negocio | Carpeta correspondiente | Archivo de muestra |
---|---|---|
Conexión bluetooth | lb-ble-common-connection (manejo de eventos de conexión, desconexión y reconexión) | abstract-bluetooth.js (el más simple, llamar a la API de la plataforma para conectarse, desconectar Bluetooth, etc.)base-bluetooth.js (registra el ID del dispositivo, el valor característico, el estado de la conexión y otra información del dispositivo conectado, maneja el envío de datos de Bluetooth y la reconexión de Bluetooth)base-bluetooth-imp.js (captura los resultados de la conexión Bluetooth, monitorea el escaneo Bluetooth de los dispositivos circundantes, las conexiones y los eventos de estado del adaptador y los maneja en consecuencia) |
Montaje del protocolo Bluetooth. | lb-ble-common-protocol-body (implementación del ensamblaje del formato del transceptor de protocolo) | i-protocol-receive-body.js i-protocol-send-body.js |
Transmisión y recepción del protocolo Bluetooth. | lb-ble-common-protocol-operator (agente que maneja el envío y la recepción de datos) | lb-bluetooth-protocol-operator.js |
Retransmisión del protocolo Bluetooth | lb-ble-common-connection | lb-bluetooth-manager.js (consulte LBlueToothCommonManager para obtener más detalles) |
Estado de Bluetooth y estado del protocolo | lb-ble-common-state | lb-bluetooth-state-example.js , que además puede expandir nuevos estados |
Suscripciones para conexión Bluetooth y eventos de estado de protocolo | lb-ble-common-connection/base | base-bluetooth-imp.js |
Hablemos de la distribución de la conexión Bluetooth y el estado del protocolo.
El archivo se encuentra en lb-ble-common-connection/base/base-bluetooth.js
latestConnectState
.setter
set latestConnectState
._onConnectStateChanged
dentro setter
.onConnectStateChanged({connectState})
de getAppBLEManager.setBLEListener
. onBLECharacteristicValueChange
se encuentra en lb-ble-common-connection/abstract-bluetooth.js
receiveOperation
encuentra en lb-ble-common-protocol-operator/lb-bluetooth-protocol-operator.js
En la función onBLECharacteristicValueChange
, después de recibir los datos, intercepto los datos válidos de acuerdo con receive-body.js
y proceso los datos válidos de acuerdo con el método de configuración de getReceiveAction
en lb-example-bluetooth-protocol.js
para producir el value, protocolState
correspondiente. value, protocolState
. filter
se genera cuando se recibe un protocolo desconocido.
onBLECharacteristicValueChange((res) => {
console.log('接收到消息', res);
if (!!valueChangeListener) {
const {value, protocolState, filter} = this.dealReceiveData({receiveBuffer: res.value});
!filter && valueChangeListener({protocolState, value});
}
});
Este código parece simple, pero hay muchos procesos detrás. Lo más crítico es esta línea const {value, protocolState, filter} = this.dealReceiveData({receiveBuffer: res.value});
;. Permítanme explicarles en detalle lo que ha hecho esta línea de trabajo:
dealReceiveData
para procesar los datos del protocolo. dealReceiveData
aquí finalmente se entrega a la función dealReceiveData
en lb-bluetooth-manager.js
para procesar los datos.this.bluetoothProtocol.receive({receiveBuffer})
en dealReceiveData
para generar datos válidos y el estado del protocolo. Esta receive
es finalmente ejecutada por la función receiveOperation
.receiveOperation
, hará referencia al elemento de configuración getReceiveAction
de la subclase de LBlueToothProtocolOperator
(la subclase es lb-example-bluetooth-protocol.js
).getReceiveAction
finalmente devuelve el objeto acordado {protocolState,effectiveData}
de acuerdo con la implementación del propio desarrollador. Después de devolver el objeto a receiveOperation
, realiza una verificación ( protocolState
que no está configurado en getReceiveAction
se procesa como CommonProtocolState.UNKNOWN
) y devuelve el objeto. objeto acordado para dealReceiveData
Variables locales effectiveData, protocolState
en la función.protocolState!==CommonProtocolState.UNKNOWN
se marcará como filter:true
; de lo contrario, el objeto acordado se devolverá al value, protocolState
en la función onBLECharacteristicValueChange
.Eso es todo lo que hace esta línea de código.
El objeto acordado se pasará como parámetro a valueChangeListener({protocolState, value})
y se ejecutará la devolución de llamada. Después de eso, el front-end puede recibir los eventos suscritos, es decir, el tipo de protocolo y el objeto value
se reciben en onReceiveData({protocolState, value})
de getAppBLEManager.setBLEListener
.
Documento
Registro de cambios
control de calidad
LICENCIA
Para intercambios técnicos, únase al grupo QQ: 821711186