(一)蓝牙需要知道的几个名词
1、profile
profile可以理解为一种规范,一个标准的通信协议,它存在于从机中。蓝牙组织规定了一些标准的profile,例如 HID OVER GATT ,防丢器 ,心率计等。每个profile中会包含多个service,每个service代表从机的一种能力。
2、service
service可以理解为一个服务,在ble从机中,通过有多个服务,例如电量信息服务、系统信息服务等,每个service中又包含多个characteristic特征值。每个具体的characteristic特征值才是ble通信的主题。比如当前的电量是80%,所以会通过电量的characteristic特征值存在从机的profile里,这样主机就可以通过这个characteristic来读取80%这个数据
3、characteristic
characteristic特征值,ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
4、UUID
UUID,统一识别码,我们刚才提到的service和characteristic,都需要一个唯一的uuid来标识
整理一下,每个从机都会有一个叫做profile的东西存在,不管是上面的自定义的simpleprofile,还是标准的防丢器profile,他们都是由一些列service组成,然后每个service又包含了多个characteristic,主机和从机之间的通信,均是通过characteristic来实现。
(二)流程图
微信文档总结的蓝牙通讯流程图
我觉得你看一遍微信的官方文档,按照我这个流程基本上就可以调通蓝牙。
(三)微信小程序蓝牙通讯相关代码
1.搜索附近的设备
/* 初始化蓝牙适配器 */
wx.openBluetoothAdapter({
success: function (res) {
// console.log('初始化蓝牙适配器成功');
wx.startBluetoothDevicesDiscovery({
services: [],
allowDuplicatesKey: false,
success: function (res) {
// console.log('这里是开始搜索附近设备', res);
wx.onBluetoothDeviceFound(function (res) {
console.log("成功", res);
/* 获取设备信号,判断信号强度 */
var device_RSSI_1 = res.devices[0].RSSI;
//Number将不同的对象转换成数字
var device_RSSI_2 = Number(device_RSSI_1);
//Math.abs获取绝对值
var device_RSSI = Math.abs(device_RSSI_2);
if (device_RSSI <= 60) {
var img = "../../images/signal4.png"
} else if (device_RSSI > 60 && device_RSSI <= 70) {
var img = "../../images/signal3.png"
} else if (device_RSSI > 70 && device_RSSI <= 80) {
var img = "../../images/signal2.png"
} else if (device_RSSI > 80) {
var img = "../../images/signal1.png"
}
if (res.devices[0].name == "") {
var temp = {
ID: res.devices[0].deviceId,
name: "",
RSSI: res.devices[0].RSSI,
img: img
}
} else {
var temp = {
ID: res.devices[0].deviceId,
name: res.devices[0].name,
RSSI: res.devices[0].RSSI,
img: img
}
}
var tempName = temp.name;
console.log('name', tempName)
//这里我直接把没有名字的设备过滤掉了
if (tempName != "") {
console.log('tempName', tempName)
devices.push(temp);//我定义的一个设备数组用于保存搜索到的设备
that.setData({
deviceArray: devices,
isShow: true
});
}
});
},
});
},
fail: function (res) {
wx.showLoading({
title: '请打开蓝牙',
});
}
})
}
})
},
2.连接设备
我这里的逻辑是:用户点击搜索到的设备后,跳转到另一个界面进行相应的操作。
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this;
wx.stopBluetoothDevicesDiscovery({
success: function (res) {
console.log('停止搜索设备', res)
that.setData({
connect: false
})
}
})
console.log(options);
this.setData({
deviceId: options.id,
deviceName: options.name,
num: options.num,
deviceType: options.type
});
console.log('设备的ID', this.data.deviceId);
console.log('设备的号', this.data.num);
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
var that = this;
/* 连接中动画 */
wx.showLoading({
title: '连接中...',
});
//开始连接蓝牙设备
wx.createBLEConnection({
deviceId: that.data.deviceId,
success: function (res) {
console.log('连接成功', res);
/* 获取设备的服务UUID */
wx.getBLEDeviceServices({
deviceId: that.data.deviceId,
success: function (service) {
var all_UUID = service.services; //取出所有的服务
console.log('所有的服务', all_UUID);
var UUID_lenght = all_UUID.length; //获取到服务数组的长度
/* 遍历服务数组 */
for (var index = 0; index < UUID_lenght; index++) {
var ergodic_UUID = all_UUID[index].uuid; //取出服务里面的UUID
var UUID_slice = ergodic_UUID.slice(4, 8); //截取4到8位
/* 判断是否是我们需要的FEE0 */
if (UUID_slice == 'FEE0' || UUID_slice == 'fee0') {
var index_uuid = index;
that.setData({
serviceId: all_UUID[index_uuid].uuid //确定需要的服务UUID
});
};
};
console.log('需要的服务UUID', that.data.serviceId)
that.Characteristics(); //调用获取特征值函数
},
});
that.setData({
connect: true
})
},
complete: function (res) {
console.log('打印出>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', res);
}
})
},
Characteristics: function () {
var that = this;
var device_characteristics = [];
var characteristics_uuid = {};
wx.getBLEDeviceCharacteristics({
deviceId: that.data.deviceId,
serviceId: that.data.serviceId,
success: function (res) {
var characteristics = res.characteristics; //获取到所有特征值
var characteristics_length = characteristics.length; //获取到特征值数组的长度
console.log('获取到特征值', characteristics);
console.log('获取到特征值数组长度', characteristics_length);
/* 遍历数组获取notycharacteristicsId */
for (var index = 0; index < characteristics_length; index++) {
var noty_characteristics_UUID = characteristics[index].uuid; //取出特征值里面的UUID
var characteristics_slice = noty_characteristics_UUID.slice(4, 8); //截取4到8位
/* 判断是否是我们需要的FEE1 */
if (characteristics_slice == 'FEE1' || characteristics_slice == 'fee1') {
var index_uuid = index;
that.setData({
notycharacteristicsId: characteristics[index_uuid].uuid, //需确定要的使能UUID
characteristicsId: characteristics[index_uuid].uuid //暂时确定的写入UUID
});
/* 遍历获取characteristicsId */
for (var index = 0; index < characteristics_length; index++) {
var characteristics_UUID = characteristics[index].uuid; //取出特征值里面的UUID
var characteristics_slice = characteristics_UUID.slice(4, 8); //截取4到8位
/* 判断是否是我们需要的FEE2 */
if (characteristics_slice == 'FEE2' || characteristics_slice == 'fee2') {
var index_uuid = index;
that.setData({
characteristicsId: characteristics[index_uuid].uuid //确定的写入UUID
});
};
};
};
};
console.log('使能characteristicsId', that.data.notycharacteristicsId);
console.log('写入characteristicsId', that.data.characteristicsId);
that.notycharacteristicsId(); //使能事件
},
})
},
/* 使能函数 */
notycharacteristicsId: function () {
var that = this;
var recve_value = "";
wx.notifyBLECharacteristicValueChange({
deviceId: that.data.deviceId,
serviceId: that.data.serviceId,
characteristicId: that.data.notycharacteristicsId,
state: true,
success: function (res) {
console.log('使能成功', res);
wx.hideLoading();
if (that.data.isTest) {
console.log(res);
console.log('this>>>>>>>>>>>>>>>>>>>',this);
setTimeout(function () {
that.SendTap()
}, 2000);
console.log('that>>>>>>>>>>>>>>>>>>>', that);
}
/* 设备返回值 */
wx.onBLECharacteristicValueChange(function (res) {
var length_hex = [];
var turn_back = "";
var result = res.value;
var hex = that.buf2hex(result);
recve_value = recve_value + hex;
console.log('设备>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>返回的值', recve_value);
},
fail: function (res) {
wx.showToast({
title: '通讯异常',
})
console.log('使能失败', res);
},
complete:function(res){
console.log('使能', res);
}
})
},
3.发送数据
注意:蓝牙最多一次发送20个字节,所以大于20个字节时,需要分段发送.
var value_initial_1 = "你要发的数据";
var write_array = [];//用于发送数据的数组。
/* 判断是否存在空格 */
if (value_initial_1.indexOf(' ') > 0) {
var value_initial = that.splitStr(value_initial_1, ' '); //存在空格时
console.log('删除掉空格', value_initial);
} else {
var value_initial = value_initial_1; //不存在空格时
}
/* 判断字节是否超过20字节 */
if (value_initial.length > 20) {
//当字节超过20的时候,采用分段发送
//选择16进制发送时
var value_initial_exceed = value_initial;
//将输入框的值取过来,方便循环
var value_initial_average = Math.ceil(value_initial_exceed.length / 20); //将value_initial_exceed的长度除以20,余数再向上取一,确定循环几次
console.log('需要循环的次数', value_initial_average);
for (var i = 0; i < value_initial_average; i++) {
if (value_initial_exceed.length > 20) {
var value_initial_send = value_initial_exceed.slice(0, 20); //截取前20个字节
console.log('截取到的值', value_initial_send);
value_initial_exceed = value_initial_exceed.substring(20); //value_initial_exceed替换为取掉前20字节后的数据
write_array.push(value_initial_send); //将所有截取的值放在一个数组
} else {
write_array.push(value_initial_exceed);
}
}
console.log('write_array数组', write_array);
write_array.map(function (val, index) {
setTimeout(function () {
var value_set = val;
console.log('value_set', value_set);
var write_function = that.write(value_set); //调用数据发送函数
}, index * 100)
});
} else {
//当字节不超过20的时候,直接发送
var value = value_initial;
var write_function = that.write(value); //调用数据发送函数
}
},
//写入数据
write: function (str) {
var that = this;
var value = str;
console.log('value', value);
/* 将数值转为ArrayBuffer类型数据 */
var typedArray = new Uint8Array(value.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
}));
var buffer = typedArray.buffer;
wx.writeBLECharacteristicValue({
deviceId: that.data.deviceId,
serviceId: that.data.serviceId,
characteristicId: that.data.characteristicsId,
value: buffer,
success: function (res) {
console.log('数据发送成功', res);
},
fail: function (res) {
console.log('调用失败', res);
/* 调用失败时,再次调用 */
wx.writeBLECharacteristicValue({
deviceId: that.data.deviceId,
serviceId: that.data.serviceId,
characteristicId: that.data.characteristicsId,
value: buffer,
success: function (res) {
console.log('第2次数据发送成功', res);
},
fail: function (res) {
console.log('第2次调用失败', res);
/* 调用失败时,再次调用 */
wx.writeBLECharacteristicValue({
deviceId: that.data.deviceId,
serviceId: that.data.serviceId,
characteristicId: that.data.characteristicsId,
value: buffer,
success: function (res) {
console.log('第3次数据发送成功', res);
},
fail: function (res) {
console.log('第3次调用失败', res);
}
});
}
});
}
});
},
(四)相关的转换方法
/* ArrayBuffer类型数据转为16进制字符串 */
buf2hex: function (buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
},
/* 去除输入框输入的值中的空格 */
splitStr: function (str, s) {
var newStr = "";
var strArray = str.split(s);
for (var i = 0; i < strArray.length; i++) {
newStr += strArray[i];
}
return newStr;
},
此demo包含了微信小程序充值和蓝牙通讯的功能。希望能帮您节省一定的时间,快速上手。如果有问题留言,我看到会回复您的。