ชื่อ: wx-simple-bluetooth
แพลตฟอร์มที่ใช้งานได้: แอพเพล็ต WeChat
บลูทูธ: บลูทูธพลังงานต่ำ
เวอร์ชันโครงการ: 2.0.1
โปรเจ็กต์นี้ได้รับการออกแบบจากสามระดับ: การเชื่อมต่อ Bluetooth, การสื่อสารโปรโตคอล Bluetooth, การสมัครสมาชิกสถานะ และการแจ้งเตือน โดยสามารถปรับแต่งการพัฒนา Bluetooth ของมินิโปรแกรมของคุณเองได้อย่างง่ายดาย หน้าที่หลักมีดังนี้:
!!!คุณต้องเปิดใช้งานการรวบรวมเครื่องมือพัฒนา WeChat ที่ปรับปรุงแล้วก่อน!!!
这个项目从蓝牙连接、蓝牙协议通信、状态订阅及通知三个层面进行设计,可以很方便的定制您自己的小程序的蓝牙开发。主要功能如下
:
ต่อไปนี้เป็นกรณีที่เปิดใช้งานฟังก์ชัน Bluetooth, GPS และ WeChat ในโทรศัพท์มือถือ:
getAppBLEManager.connect()
จะสแกนอุปกรณ์ Bluetooth โดยรอบโดยอัตโนมัติทุกๆ 350ms
และเชื่อมต่อกับอุปกรณ์ที่มีสัญญาณแรงที่สุดภายในเวลานั้นuuid
ของ service
หลักและ ID บริการที่เกี่ยวข้องที่ใช้สำหรับการสื่อสารได้ คุณยังสามารถเพิ่มชื่ออุปกรณ์ Bluetooth เพิ่มเติมเพื่อกรองอุปกรณ์เพิ่มเติมได้注意:目前在发送数据时大于20包的数据会被裁剪为20包
(有些蓝牙连接问题是微信兼容或是手机问题,目前是无法解决的。如错误码10003以及部分华为手机蓝牙连接或重连困难。如果您有很好的解决方案,还请联系我,十分感谢)
lb-example-bluetooth-manager.js
เพื่อดูรายละเอียดดังนั้น จากการพิจารณานี้ กรอบการทำงานนี้จึงมีข้อจำกัดดังต่อไปนี้:
- ข้อตกลงจะต้องจัดทำตามรูปแบบข้อตกลงที่ตกลงกันไว้จึงจะใช้กรอบการทำงานได้ตามปกติ
- จำนวนข้อมูลสูงสุดในแพ็คเก็ตของโปรโตคอลคือ 20 ไบต์ กรอบงานไม่รองรับรูปแบบโปรโตคอลที่มีขนาดใหญ่กว่า 20 ไบต์ หากข้อมูลเกินขีดจำกัด ขอแนะนำให้แบ่งออกเป็นการส่งหลายรายการ
- ขอแนะนำให้ดำเนินการเขียนในโหมดอนุกรม
- ขอแนะนำให้ทำความเข้าใจเอกสาร Bluetooth อย่างเป็นทางการของมินิโปรแกรมก่อน เพื่ออำนวยความสะดวกในการทำความเข้าใจการใช้เฟรมเวิร์ก
协议约定格式:[...命令字之前的数据(非必需), 命令字(必需), ...有效数据(非必需 如控制灯光发送255,255,255), 有效数据之后的数据(非必需 如协议结束标志校、验位等)
协议格式示例:[170(帧头), 10(命令字), 1(灯光开启),255,255,255(三个255,白色灯光),233(协议结束标志,有的协议中没有这一位),18(校验位,我胡乱写的)]
有效数据是什么:
在刚刚的这个示例中,帧头、协议结束标志是固定的值,校验位是按固定算法生成的,这些不是有效数据。而1,255,255,255这四个字节是用于控制蓝牙设备的,属于有效数据。
หากโครงการนี้มีประโยชน์ ฉันหวังว่าจะให้โครงการนี้ติดดาวบน GitHub!
modules
ภายใต้โครงการไปยังโครงการของคุณ 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
ของคุณเองและกฎการกรองการสแกน (ตัวเลือก) ไฟล์อยู่ที่ ./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();
}
}();
ไฟล์ตั้งอยู่ที่ ./modules/bluetooth/lb-ble-example-protocol-body
-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;
}
}
ตั้งอยู่ใน 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})]);
}
}();
ไฟล์อยู่ใน 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
};
ธุรกิจ | โฟลเดอร์ที่สอดคล้องกัน | ไฟล์ตัวอย่าง |
---|---|---|
การเชื่อมต่อบลูทูธ | lb-ble-common-connection (การจัดการเหตุการณ์การเชื่อมต่อ การตัดการเชื่อมต่อ และการเชื่อมต่อใหม่) | abstract-bluetooth.js (วิธีที่ง่ายที่สุดในการเรียกแพลตฟอร์ม API เพื่อเชื่อมต่อ, ตัดการเชื่อมต่อ Bluetooth ฯลฯ )base-bluetooth.js (บันทึก deviceId ค่าคุณลักษณะ สถานะการเชื่อมต่อ และข้อมูลอื่น ๆ ของอุปกรณ์ที่เชื่อมต่อ จัดการการส่งข้อมูล Bluetooth และการเชื่อมต่อ Bluetooth ใหม่)base-bluetooth-imp.js (บันทึกผลการเชื่อมต่อ Bluetooth ตรวจสอบการสแกน Bluetooth ของอุปกรณ์โดยรอบ การเชื่อมต่อ และเหตุการณ์สถานะอแด็ปเตอร์ และจัดการตามนั้น) |
การประกอบโปรโตคอลบลูทูธ | lb-ble-common-protocol-body (การดำเนินการประกอบรูปแบบตัวรับส่งสัญญาณโปรโตคอล) | i-protocol-receive-body.js i-protocol-send-body.js |
การส่งและรับโปรโตคอล Bluetooth | lb-ble-common-protocol-operator (ตัวแทนที่จัดการการส่งและรับข้อมูล) | lb-bluetooth-protocol-operator.js |
การส่งสัญญาณโปรโตคอล Bluetooth ใหม่ | lb-ble-common-connection | lb-bluetooth-manager.js (ดูรายละเอียด LBlueToothCommonManager ) |
สถานะบลูทูธและสถานะโปรโตคอล | lb-ble-common-state | lb-bluetooth-state-example.js ซึ่งสามารถขยายสถานะใหม่เพิ่มเติมได้ |
การสมัครรับการเชื่อมต่อ Bluetooth และเหตุการณ์สถานะโปรโตคอล | lb-ble-common-connection/base | base-bluetooth-imp.js |
เรามาพูดถึงการกระจายการเชื่อมต่อ Bluetooth และสถานะโปรโตคอลกันดีกว่า
ไฟล์อยู่ที่ lb-ble-common-connection/base/base-bluetooth.js
latestConnectState
setter
set latestConnectState
_onConnectStateChanged
ภายใน setter
onConnectStateChanged({connectState})
ของ getAppBLEManager.setBLEListener
onBLECharacteristicValueChange
ตั้งอยู่ใน lb-ble-common-connection/abstract-bluetooth.js
receiveOperation
ตั้งอยู่ใน lb-ble-common-protocol-operator/lb-bluetooth-protocol-operator.js
ในฟังก์ชัน onBLECharacteristicValueChange
หลังจากได้รับข้อมูล ฉันจะดักข้อมูลที่ถูกต้องตาม receive-body.js
และประมวลผลข้อมูลที่ถูกต้องตามวิธีการกำหนดค่าของ getReceiveAction
ใน lb-example-bluetooth-protocol.js
เพื่อสร้าง value, protocolState
ที่สอดคล้องกัน value, protocolState
. filter
จะถูกสร้างขึ้นเมื่อได้รับโปรโตคอลที่ไม่รู้จัก
onBLECharacteristicValueChange((res) => {
console.log('接收到消息', res);
if (!!valueChangeListener) {
const {value, protocolState, filter} = this.dealReceiveData({receiveBuffer: res.value});
!filter && valueChangeListener({protocolState, value});
}
});
โค้ดนี้ดูเรียบง่าย แต่มีกระบวนการมากมายอยู่เบื้องหลัง สิ่งที่สำคัญที่สุดคือบรรทัดนี้ const {value, protocolState, filter} = this.dealReceiveData({receiveBuffer: res.value});
; ให้ฉันอธิบายรายละเอียดว่าสายงานนี้ทำอะไรได้บ้าง:
dealReceiveData
เพื่อประมวลผลข้อมูลโปรโตคอล ในที่สุด dealReceiveData
ที่นี่จะถูกส่งไปยังฟังก์ชัน dealReceiveData
ใน lb-bluetooth-manager.js
เพื่อประมวลผลข้อมูลthis.bluetoothProtocol.receive({receiveBuffer})
ใน dealReceiveData
เพื่อสร้างข้อมูลและสถานะโปรโตคอลที่ถูกต้อง ในที่สุด receive
นี้จะดำเนินการโดยฟังก์ชัน receiveOperation
receiveOperation
มันจะอ้างอิงรายการการกำหนดค่า getReceiveAction
ของคลาสย่อยของ LBlueToothProtocolOperator
(คลาสย่อยคือ lb-example-bluetooth-protocol.js
)getReceiveAction
จะส่งคืนอ็อบเจ็กต์ที่ตกลงกันไว้ {protocolState,effectiveData}
ตามการใช้งานของนักพัฒนาเอง หลังจากส่งคืนอ็อบเจ็กต์ไปที่ receiveOperation
แล้ว จะทำการตรวจสอบ ( protocolState
ที่ไม่ได้กำหนดค่าใน getReceiveAction
จะถูกประมวลผลเป็น CommonProtocolState.UNKNOWN
) และส่งคืน ตกลงวัตถุเพื่อ dealReceiveData
ตัวแปรท้องถิ่น effectiveData, protocolState
ในฟังก์ชันprotocolState!==CommonProtocolState.UNKNOWN
จะถูกทำเครื่องหมายเป็น filter:true
; มิฉะนั้น อ็อบเจ็กต์ที่ตกลงจะถูกส่งคืนไปยัง value, protocolState
ในฟังก์ชัน onBLECharacteristicValueChange
นั่นคือทั้งหมดที่บรรทัดโค้ดนี้ทำ
ออบเจ็กต์ที่ตกลงไว้จะถูกส่งผ่านเป็นพารามิเตอร์ไปยัง valueChangeListener({protocolState, value})
และการโทรกลับจะถูกดำเนินการ หลังจากนั้น ส่วนหน้าสามารถรับเหตุการณ์ที่สมัครรับข้อมูลได้ กล่าวคือ ประเภทโปรโตคอลและออบเจ็กต์ value
จะได้รับในฟังก์ชัน onReceiveData({protocolState, value})
ของ getAppBLEManager.setBLEListener
เอกสาร
บันทึกการเปลี่ยนแปลง
ประกันคุณภาพ
ใบอนุญาต
สำหรับการแลกเปลี่ยนทางเทคนิค โปรดเข้าร่วมกลุ่ม QQ: 821711186