//bluetooth.js
const bluetooth = {
deviceName: "", //设备名
isFound: false, //是否查找到需要连接的蓝牙设备
deviceId: null, //蓝牙设备id
serviceId: null, //蓝牙服务 UUID
writeCharId: null, //蓝牙设备特征的 UUID
eventList: [], //事件列表
connectStatus: false, //连接状态
heatBeatInterval: 10, //心跳时长
heatBeatTimes: 0, //心跳未回应次数
heatBeatTimer: null, //心跳定时器
failCallback: null, //失败回调
successCallback: null, //成功回调
initBle: function (deviceName, timeout, success, fail) {
var that = this;
// 安卓下部分机型需要有位置权限才能搜索到设备,需留意是否开启了位置权限
wx.startLocationUpdate({
//开启小程序进入前台时接收位置消息
type: "gcj02", //返回可用于 wx.openLocation 的坐标
success(res) {
wx.stopLocationUpdate();
that.deviceName = deviceName;
that.successCallback = success;
that.failCallback = fail;
wx.getSetting({
//获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
success(res) {
//用户是否有设置蓝牙
if (res.authSetting.hasOwnProperty("scope.bluetooth")) {
if (!res.authSetting["scope.bluetooth"]) {
//用户未开启蓝牙
wx.openSetting({
//调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
success(res) {},
});
} else {
wx.closeBluetoothAdapter({
//释放蓝牙资源,关闭蓝牙模块。调用该方法将断开所有已建立的连接并释放系统资源。建议在使用蓝牙流程后,与 wx.openBluetoothAdapter 成对调用。
success: (res) => {
wx.openBluetoothAdapter({
//初始化蓝牙模块
success(res) {
wx.startBluetoothDevicesDiscovery({
//开始搜寻附近的蓝牙外围设备
success(res) {
that.discoveryBle();
//搜索设备超时处理
setTimeout(function () {
if (that.isFound) {
return;
} else {
wx.stopBluetoothDevicesDiscovery({
//停止搜寻附近的蓝牙外围设备
success: function (res) {
console.log("搜索设备超时");
},
});
wx.offBluetoothDeviceFound(that.listener); //移除搜索到新设备的事件的全部监听函数
that.failCallback &&
that.failCallback("timeout");
return;
}
}, timeout * 1000);
},
fail: function (res) {
console.log("蓝牙设备服务发现失败: " + res.errMsg);
},
});
},
fail(err) {
console.log("openBluetoothAdapterError", err);
//当前蓝牙适配器不可用
if (err.errCode == 10001)
that.failCallback && that.failCallback("BLE_CLOSED");
},
});
},
});
}
} else {
//'scope.bluetooth'属性不存在,需要授权
wx.authorize({
//提前向用户发起授权请求。调用后会立刻弹窗询问用户是否同意授权小程序使用某项功能或获取用户的某些数据,但不会实际调用对应接口。如果用户之前已经同意授权,则不会出现弹窗,直接返回成功。
scope: "scope.bluetooth",
success() {
// 用户已经同意小程序使用手机蓝牙功能,后续调用 蓝牙 接口不会弹窗询问
console.log(res.authSetting);
},
});
}
},
});
},
});
},
//搜索设备
discoveryBle: function () {
var that = this;
//监听搜索到新设备的事件
wx.onBluetoothDeviceFound(function (res) {
var devicesList = res.devices;
if (devicesList.length <= 0) {
return;
}
try{
devicesList.forEach((item, index) => {
let name =item.localName||item.name
if (name.trim() == that.deviceName) {
//查找到当前需要连接的蓝牙设备
that.isFound = true;
wx.offBluetoothDeviceFound(); //移除搜索到新设备的事件的全部监听函数
wx.stopBluetoothDevicesDiscovery({
//停止搜寻附近的蓝牙外围设备
success: function (res) {
console.log("连接蓝牙成功之后关闭蓝牙搜索");
that.deviceId = item.deviceId
//连接蓝牙设备
that.connectBle(item.deviceId);
},
});
throw '终止循环'
}
});
}catch(err){
console.log(err)
}
});
},
//连接蓝牙设备
connectBle: function (deviceId) {
var that = this;
wx.closeBLEConnection({
//断开与蓝牙低功耗设备的连接
deviceId,
success: function (res) {
console.log("断开设备[" + deviceId + "]成功.");
},
fail: function (res) {
console.log("断开设备失败.");
},
complete: function () {
//接口调用结束的回调函数(调用成功、失败都会执行)
wx.createBLEConnection({
//连接蓝牙低功耗设备。
deviceId,
success: function (res) {
//设备连接成功
that.connectStatus = true;
that.reconnectTimes = 0;
wx.onBLEConnectionStateChange(function (res) {
//监听蓝牙低功耗连接状态改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等
// 该方法回调中可以用于处理连接意外断开等异常情况
if (!res.connected) {
that.failCallback && that.failCallback("CONNECT_CLOSE");
}
});
wx.onBluetoothAdapterStateChange(function (res) {
//监听蓝牙适配器状态变化事件
if (!res.available)
that.failCallback && that.failCallback("BLE_CHANGE_CLOSE");
});
wx.getBLEMTU({
//获取蓝牙低功耗的最大传输单元
deviceId,
success: () => {
wx.setBLEMTU({
//协商设置蓝牙低功耗的最大传输单元 (Maximum Transmission Unit, MTU)。需在 wx.createBLEConnection 调用成功后调用。仅安卓系统 5.1 以上版本有效,iOS 因系统限制不支持。
deviceId,
mtu: 512,
success: (res) => {
console.log('setBLEMTUsuccess', res)
},
fail: (err) => {
console.log('setBLEMTUfail', err)
}
})
},
});
wx.onBLEMTUChange(
//监听蓝牙低功耗的最大传输单元变化事件(仅安卓触发)。
(res)=>{
console.log('onBLEMTUChange',res)
})
//获取蓝牙低功耗设备所有服务 (service)。
that.getService(deviceId);
},
fail: function (res) {
console.log("设备连接失败" + res.errMsg);
},
});
},
});
},
//获取蓝牙低功耗设备所有服务 (service)。
getService: function (deviceId) {
var that = this;
wx.getBLEDeviceServices({
deviceId,
success: function (res) {
var services = res.services;
if (services.length <= 0) {
that.printLog("未找到主服务列表");
return;
}
services.forEach((item) => {
let uuid = String(item.uuid).split("-")[0];
if (uuid == "0000FFF1") {
that.getCharacteristics(deviceId, item.uuid);
} else {
app.toast("未找到服务,请确认连接设备是否正确");
}
});
},
fail: function (res) {
that.printLog("获取设备服务列表失败" + res.errMsg);
},
});
},
//获取蓝牙低功耗设备某个服务中所有特征 (characteristic)
getCharacteristics: function (deviceId, serviceId) {
var that = this;
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success(res) {
if (res.characteristics.length <= 0) {
app.toast("deviceId, serviceId");
return;
}
res.characteristics.forEach((item, index) => {
if (item.properties.notify) {
//该特征支持 notify 操作
that.recvBLECharacterNotice(deviceId, serviceId, item.uuid);
}
if (item.properties.write) {
// 该特征支持 write 操作
that.serviceId = serviceId;
that.writeCharId = item.uuid; //蓝牙设备特征的 UUID
}
});
that.successCallback && that.successCallback();
},
fail(res) {
that.printLog("获取设备特征值失败");
},
});
},
//启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。
//接收蓝牙特征通知
recvBLECharacterNotice: function (deviceId, serviceId, charId) {
var that = this;
//注意:必须设备的特征支持 notify 或者 indicate 才可以成功调用。
wx.notifyBLECharacteristicValueChange({
deviceId, //蓝牙设备 id
serviceId, //蓝牙特征对应服务的 UUID
characteristicId, //蓝牙特征的 UUID
state: true, //是否启用 notify
success(res) {
//监听蓝牙低功耗设备的特征值变化事件
//注意:必须先调用 wx.notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。
wx.onBLECharacteristicValueChange(function (res) {
console.log(
`characteristic ${res.characteristicId} has changed, now is ${res.value}`
);
const msg = that.arrayBufferToString(res.value);
const msgArray = msg.split("/>");
msgArray.shift(0);
msgArray.forEach((msgItem) => {
let ary = msgItem.split("|");
if (ary.length < 3) return;
let data = ary[3].split("~");
// 根据名称调用事件
let eventName = `${ary[2]}`;
console.log(eventName.toString());
let res = {
ascMsg: msg,
msgLength: ary[1],
actionTag: ary[2],
status: data.length > 0 ? data[0] : "",
data: data.length > 1 ? data.slice(1) : "",
};
console.log("接收蓝牙通知内容", res);
that.eventList.forEach((element) => {
if (eventName == element["action"] && element["callback"] != null)
element["callback"](res);
});
});
});
},
fail(res) {
console.log("特征值notify 接收数据失败: " + res.errMsg);
},
});
},
//断开蓝牙设备
disConnectBle: function (successCallback, failCallback) {
var that = this;
wx.closeBLEConnection({
//断开与蓝牙低功耗设备的连接
deviceId: that.deviceId,
success: function () {
console.log("断开设备[" + that.deviceId + "]成功.");
that.connectStatus = false;
successCallback && successCallback();
},
fail: function () {
console.log("断开设备失败.");
failCallback && failCallback();
},
});
},
//监听通信事件方法
onListen: function (action, callback = () => {}, repeat = false) {
var that = this;
const event = {
action: action,
callback: callback,
};
//数组去重
if (!repeat) {
that.eventList.forEach((item, index) => {
if (item.action == action) {
that.eventList.splice(index, 1);
}
});
}
that.eventList.push(event);
},
//发送心跳
heartbeat() {
var that = this;
if (!that.connectStatus) {
console.log("请先连接设备");
return;
}
//发送心跳
that.heatBeatTimer = setInterval(() => {
//心跳定时器
if (that.heatBeatTimes >= 3) {
console.log("失去心跳");
that.disConnectBle();
that.stopHeatBeat();
that.failCallback && that.failCallback("CONNECT_CLOSE");
return;
}
that.heatBeatTimes++; //心跳未回应次数
that.sendBLECharacterNotice("MAINTAIN", [], (res) => {
console.log("发送蓝牙心跳成功");
});
}, that.heatBeatInterval * 1000);
},
//结束心跳
stopHeatBeat() {
var that = this;
clearInterval(that.heatBeatTimer);
that.heatBeatTimes = 0;
},
//向蓝牙设备发送通知
sendBLECharacterNotice: function (actionTag, params, listener) {
var that = this;
if (that.writeCharId == null) {
console.log("设备不支持接收信息");
return;
}
let buffer = null;
let data = null;
that.onListen(
actionTag,
listener
)
if (actionTag == "PACKET") {
buffer = params[0].buffer;
} else {
data = `|${actionTag}|${params.length != 0 ? params?.join("~") : "~"}|#`;
let msg = `/>|${data.length + 5}${data}`;
buffer = that.stringToArrayBuffer(msg);
const msgdata = that.arrayBufferToString(buffer);
}
//向蓝牙低功耗设备特征值中写入二进制数据。
//注意:必须设备的特征支持 write 才可以成功调用。
wx.writeBLECharacteristicValue({
deviceId: that.deviceId,
serviceId: that.serviceId,
characteristicId: that.writeCharId,
value: buffer, //是ArrayBuffer类型
success: function (res) {
console.log(res)
console.log(`发送成功--------${actionTag=='PACKET'?`固件升级分包`:`/>|${data.length + 5}${data}`}`);
},
fail: function (res) {
console.log("发送失败.", res);
},
complete: function () {},
});
},
//string转ArrayBuffer
stringToArrayBuffer: function (str) {
var bytes = new Array();
var len, c;
len = str.length;
for (var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10ffff) {
bytes.push(((c >> 18) & 0x07) | 0xf0);
bytes.push(((c >> 12) & 0x3f) | 0x80);
bytes.push(((c >> 6) & 0x3f) | 0x80);
bytes.push((c & 0x3f) | 0x80);
} else if (c >= 0x000800 && c <= 0x00ffff) {
bytes.push(((c >> 12) & 0x0f) | 0xe0);
bytes.push(((c >> 6) & 0x3f) | 0x80);
bytes.push((c & 0x3f) | 0x80);
} else if (c >= 0x000080 && c <= 0x0007ff) {
bytes.push(((c >> 6) & 0x1f) | 0xc0);
bytes.push((c & 0x3f) | 0x80);
} else {
bytes.push(c & 0xff);
}
}
var array = new Int8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
array[i] = bytes[i];
}
return array.buffer;
},
//ArrayBuffer转string
arrayBufferToString: function (arr) {
if (typeof arr === "string") {
return arr;
}
var dataview = new DataView(arr);
var ints = new Uint8Array(arr.byteLength);
for (var i = 0; i < ints.length; i++) {
ints[i] = dataview.getUint8(i);
}
arr = ints;
var str = "",
_arr = arr;
for (var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if (v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for (var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
},
};
export default bluetooth;
//bluetoothApi.js
import bluetooth from './bluetooth.js'
const bluetoothApi= {
//蓝牙初始化
btInit(deviceName, timeout, success, fail) {
bluetooth.initBle(deviceName, timeout, success, fail)
},
// 固件升级
btUpgrade(params, callback) {
bluetooth.sendBLECharacterNotice(
'UPGRADE',
[params['allLength'], params['packageNum'], params['packageLength'],params['version']],
(res) => {
callback(res);
},
);
},
// 监听简易章筒长按压预警
btListenLongPress(callback) {
bluetooth.onListen(
'WARN',
callback
);
},
//断开蓝牙
btDisConnect() {
bluetooth.disConnectBle();
},
}
export default bluetoothApi
// bluetoothMethod.js
import bluetoothApi from './bluetoothApi.js'
import bluetoothMqttApi from './bluetoothMqttApi.js'
const bluetoothMethod = {
// 蓝牙连接设备
connectDeviceMethod({number,params,success,fail}) {
bluetoothApi.btInit(number, 20, () => {
bluetoothMqttApi.userLogin(params, success, fail)
}, fail);
},
//固件升级
upgradeMehod({params,success,fail}) {
bluetoothApi.btUpgrade(params, (res) => {
if (res['status'] == 'OK') {
success(res);
}else{
fail(res);
}
});
},
//长按压预警
listenLongPressMethod({success}) {
bluetoothApi.btListenLongPress((res) => {
if (res['status'] == 'OK') {
success(res);
}
})
},
}
export default bluetoothMethod
//app.js
import bluetoothMethod from './bluetoothMethod.js'
App({
methodProvider:bluetoothMethod
})
//需要连接蓝牙的页面
app.methodProvider.listenLongPressMethod({
success: (res) => {
console.log('超时按压了',res)
}
})
app.methodProvider.upgradeMehod({
params: params,
success: (res) => {
console.log('升级成功',res)
},
fail: (res) => {
console.log('升级失败',res)
}
})
微信官方文档小程序相关API