[NUCLEO-WBA52CG STM32 wireless product family new series] +6. Design STM32WBA52CG and applet communication demo
[Copy link]
Since I have been involved in WeChat applet development before, in fact, applet and Bluetooth communication are not complicated. Before development, I will first introduce the structure of the WeChat applet project as follows. The applet contains a description of the overall program
and multiple pages describing their respective
The main part of a mini program consists of three files: app.js mini program logic, app.json mini program common configuration, and app.wxss mini program common style sheet, which must be placed in the root directory of the project. A mini program page consists of four files: page logic xx.js, page structure xx.wxml, page configuration xx.json, and page style sheet xx.wxss. The four files describing the page must have the same path and file name (not necessarily the same as the folder name).
After clicking the Search Bluetooth Device button, it will start searching for nearby Bluetooth devices that are broadcasting, and they will appear in the Bluetooth device list. You can select a Bluetooth device to connect to it.
After selecting a Bluetooth device, click the Connect Bluetooth Device button to connect to the Bluetooth device. Once the connection is successful, the service list and feature value list of the Bluetooth device will be obtained. If a feature value with notification permission is found, the notification callback function will be registered, so that the sensor data collected by the function node will be received. The js logic code of the settings page is as follows:
// pages/bluetoothconfig/bluetoothconfig.js
const util = require('../../utils/util.js')
var event = require('../../utils/event.js')
var delayTimer; //用来控制是否持续服务发现
var isFound = false;
var app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
logs: [],
deviceArray: [],
currDeviceID: '请选择...',
currDeviceName:'',
},
//屏幕打开时执行的函数
onLoad: function () {
let that = this;
//接收别的页面传过来的数据
event.on('EnvMonitorSendData2Device', this, function(data) {
//另外一个页面传过来的data是16进制字符串形式
console.log("要发送给蓝牙设备的数据:"+data);
var buffer=that.stringToBytes(data);
var dataView = new Uint8Array(buffer)
dataView = data;
wx.writeBLECharacteristicValue({
deviceId: app.globalData._deviceId,//蓝牙设备 id
serviceId: app.globalData._serviceId,//蓝牙特征值对应服务的 uuid
characteristicId: app.globalData._writeCharacteristicId,//蓝牙特征值的 uuid
value: buffer,//ArrayBuffer 蓝牙设备特征值对应的二进制值
success: function (res) {//接口调用成功的回调函数
console.log('发送成功')
},
fail: function(res) {//接口调用失败的回调函数
//发送蓝牙数据失败
console.log('发送失败')
}
}
)
})
},
onUnload: function() {
event.remove('EnvMonitorSendData2Device', this);
},
bindPickerChange: function(ret){
var array = this.data.deviceArray;
console.log(array[ret.detail.value]);
this.setData({
currDeviceID: array[ret.detail.value]
})
this.printLog("选中:" + array[ret.detail.value]);
},
bleSearchEvent: function(ret){
this.initBLE();
},
bleConfigEvent: function (ret) {
var deviceID = this.data.currDeviceID;
console.log("选中:" + deviceID);
if (util.isEmpty(deviceID) || deviceID == "请选择..."){
util.toastError("请先搜索设备");
return ;
}
var device = deviceID.split('[');
if(device.length <= 1){
util.toastError("请先搜索设备");
return ;
}
var l=deviceID.split('[');
this.printLog("选中蓝牙设备名字:" + l[0]);
this.setData({
currDeviceName: l[0]
})
var id = device[device.length - 1].replace("]", "");
console.log(id);
util.toastError("连接" + id);
this.createBLE(id);
},
bleCloseEvent: function(ret){
var deviceId = this.data.currDeviceID;
wx.showModal({
title: '确定断开设备吗?',
content: deviceId,
showCancel: true
})
var device = deviceId.split('[');
var id = device[device.length - 1].replace("]", "");
this.printLog("断开设备:[" + id+"]...");
this.closeBLE(id,null);
},
initBLE: function() {
this.printLog("启动蓝牙适配器, 蓝牙初始化")
this.setData("启动蓝牙适配器, 蓝牙初始化")
var that = this;
wx.openBluetoothAdapter({
success: function(res) {
console.log(res);
that.findBLE();
},
fail: function(res) {
util.toastError('请先打开蓝牙');
}
})
},
findBLE: function() {
this.printLog("打开蓝牙成功.")
var that = this
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
interval: 0,
success: function(res) {
wx.showLoading({
title: '正在搜索设备',
})
console.log(res);
delayTimer = setInterval(function(){
that.discoveryBLE() //3.0 //这里的discovery需要多次调用
}, 1000);
setTimeout(function () {
if (isFound) {
return;
} else {
wx.hideLoading();
console.log("搜索设备超时");
wx.stopBluetoothDevicesDiscovery({
success: function (res) {
console.log('连接蓝牙成功之后关闭蓝牙搜索');
}
})
clearInterval(delayTimer)
wx.showModal({
title: '搜索设备超时',
content: '请检查蓝牙设备是否正常工作,Android手机请打开GPS定位.',
showCancel: false
})
util.toastError("搜索设备超时,请打开GPS定位,再搜索")
return
}
}, 15000);
},
fail: function(res) {
that.printLog("蓝牙设备服务发现失败: " + res.errMsg);
}
})
},
discoveryBLE: function() {
var that = this
wx.getBluetoothDevices({
success: function(res) {
var list = res.devices;
console.log(list);
if(list.length <= 0){
return ;
}
var devices = [];
for (var i = 0; i < list.length; i++) {
//that.data.inputValue:表示的是需要连接的蓝牙设备ID,
//简单点来说就是我想要连接这个蓝牙设备,
//所以我去遍历我搜索到的蓝牙设备中是否有这个ID
/*var name = list[i].name || list[i].localName;
if(util.isEmpty(name)){
continue;
}
if(name.indexOf('MI') >= 0 && list[i].RSSI != 0){
console.log(list[i]);
devices.push(list[i]);
}*/
var deviceId = list[i].deviceId;
if(util.isEmpty(deviceId)){
continue;
}
if(list[i].RSSI != 0){
console.log(list[i]);
devices.push(list[i]);
}
}
console.log('总共有' + devices.length + "个设备需要设置")
if (devices.length <= 0) {
return;
}
that.connectBLE(devices);
},
fail: function() {
util.toastError('搜索蓝牙设备失败');
}
})
},
connectBLE: function(devices){
this.printLog('总共有' + devices.length + "个设备需要设置")
var that = this;
wx.hideLoading();
isFound = true;
clearInterval(delayTimer);
wx.stopBluetoothDevicesDiscovery({
success: function (res) {
that.printLog('搜索蓝牙设备成功之后关闭蓝牙搜索');
}
})
//两个的时候需要选择
var list = [];
for (var i = 0; i < devices.length; i++) {
var name = devices[i].name || devices[i].localName;
list.push(name + "[" + devices[i].deviceId + "]")
}
this.setData({
deviceArray: list
})
//默认选择
this.setData({
currDeviceID: list[0]
})
},
createBLE: function(deviceId){
var app = getApp()
this.printLog("连接: [" + deviceId+"]...");
var that = this;
//连接之前,先断开一下,防止设备已连接了
this.closeBLE(deviceId, function(res){
console.log("预先关闭,再打开");
setTimeout(function(){
wx.createBLEConnection({
deviceId: deviceId,
success: function (res) {
that.printLog("设备连接成功!");
app.globalData.deviceMac=deviceId;
that.getBLEServiceId(deviceId);
},
fail: function (res) {
that.printLog("设备连接失败:" + res.errMsg);
}
})
}, 2000)
});
},
//获取服务UUID
getBLEServiceId: function(deviceId){
this.printLog("获取设备[" + deviceId + "]服务列表")
var that = this;
wx.getBLEDeviceServices({
deviceId: deviceId,
success: function(res) {
console.log(res);
var services = res.services;
if (services.length <= 0){
that.printLog("未找到主服务列表")
return;
}
that.printLog('找到设备服务列表个数: ' + services.length);
for(var i=0;i<services.length;i++)
{
that.printLog("服务UUIDS:["+services[i].uuid+"]");
}
if (services.length == 1)
{
var service = services[0];
that.printLog("服务UUID:["+service.uuid+"] Primary:" + service.isPrimary);
that.getBLECharactedId(deviceId, service.uuid);
}
else//多个主服务
{
for(var i=0;i<services.length;i++)
{
if(services[i].uuid=="51311102-030E-485F-B122-F8F381AA84ED")
{
var service = services[i];
that.printLog("服务UUID:["+service.uuid+"] Primary:" + service.isPrimary);
app.globalData._deviceId=deviceId;
app.globalData._serviceId=service.uuid;
that.getBLECharactedId(deviceId, service.uuid);
}
}
}
},
fail: function(res){
that.printLog("获取设备服务列表失败" + res.errMsg);
}
})
},
getBLECharactedId: function(deviceId, serviceId){
this.printLog("获取设备特征值")
var that = this;
wx.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: function(res) {
console.log(res);
//这里会获取到两个特征值,一个用来写,一个用来读
var chars = res.characteristics;
if(chars.length <= 0){
that.printLog("未找到设备特征值")
return ;
}
that.printLog("找到"+serviceId+"特征值个数:" + chars.length);
if(chars.length >=1){
for(var i=0; i<chars.length; i++){
var char = chars[i];
that.printLog("特征值[" + char.uuid + "]")
var prop = char.properties;
if(prop.notify == true)
{
that.printLog("该特征值属性: Notify");
that.recvBLECharacterNotice(deviceId, serviceId, char.uuid);
}
else if(prop.write == true)
{
that.printLog("该特征值属性: Write");
app.globalData._writeCharacteristicId=char.uuid;
that.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
}
else if(prop.read == true)
{
that.printLog("该特征值属性: Read");
that.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
}
else if(prop.indicate == true)
{
that.printLog("该特征值属性: Indicate");
that.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
}
else
{
that.printLog("该特征值属性: 其他");
}
}
}else{
//TODO
}
},
fail: function(res){
that.printLog("获取设备特征值失败")
}
})
},
recvBLECharacterNotice: function(deviceId, serviceId, charId){
//接收设置是否成功
this.printLog("注册Notice 回调函数");
var that = this;
//设备一旦发送数据在此通道,就会立刻收到通知
wx.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
state: true, //启用Notify功能
success: function(res) {
wx.onBLECharacteristicValueChange(function(res){
console.log(res);
that.printLog("收到Notify数据: " + that.ab2hex(res.value));
var tmp =that.ab2hex(res.value);
var cdn = that.data.currDeviceName;
that.printLog("当前蓝牙设备名字: " + cdn);
//发送数据到其它页面
if(cdn=='STM32WB55_GattServer')
{
event.emit('environmetDataChanged',tmp);
}
});
},
fail: function(res){
console.log(res);
that.printLog("特征值Notice 接收数据失败: " + res.errMsg);
}
})
},
sendBLECharacterNotice: function (deviceId, serviceId, charId){
var that = this;
var buffer = this.string2buffer(JSON.stringify(cell));
setTimeout(function(){
wx.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
value: buffer,
})
}, 1000);
},
closeBLE: function(deviceId, callback){
var that = this;
wx.closeBLEConnection({
deviceId: deviceId,
success: function(res) {
that.printLog("断开设备[" + deviceId + "]成功.");
console.log(res)
},
fail: function(res){
//that.printLog("断开设备[" + deviceId + "]失败!");
},
complete: callback//接口调用结束的回调函数(调用成功、失败都会执行)
})
},
printLog: function(msg){
var logs = this.data.logs;
logs.push(msg);
this.setData({ logs: logs })
},
/**
* 将字符串转换成ArrayBufer
*/
string2buffer(str) {
if (!str) return;
var val = "";
for (var i = 0; i < str.length; i++) {
val += str.charCodeAt(i).toString(16);
}
console.log(val);
str = val;
val = "";
let length = str.length;
let index = 0;
let array = []
while (index < length) {
array.push(str.substring(index, index + 2));
index = index + 2;
}
val = array.join(",");
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
})).buffer
},
/**
* 将ArrayBuffer转换成字符串
*/
ab2hex(buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
},
stringToBytes(str) {
var array = new Uint8Array(str.length);
for (var i = 0, l = str.length; i < l; i++) {
array[i] = str.charCodeAt(i);
}
console.log(array);
return array.buffer;
},
myStringToHex(str){
var a='';
if(str.length == 1){
a += "0" + str;
}
else{
a=str;
}
return a.toUpperCase();// 统一大写格式输出
},
inputSSID: function(res) {
var ssid = res.detail.value;
this.setData({
ssid: ssid
})
},
inputPASS: function(res) {
var pass = res.detail.value;
this.setData({
pass: pass
})
}
})
In addition to controlling the disconnection of the Bluetooth connection, the logic code of the settings page also receives data sent from other pages (sent to the Bluetooth device). At the beginning, I stepped on many pitfalls when I was trying to realize the communication between different pages of the mini program. Later, I found a very useful open source library on GitHub. It uses a method similar to mqtt publishing and subscription. One page subscribes to a topic and registers a callback function, and the other page can directly send data to this topic.
Click the device icon to jump to the corresponding function interface. The following figure is the test interface: it provides the display of three data: power, carbon dioxide concentration, and TVOC concentration. It also provides a button to control the onboard LED, which can be expanded to control relays to control high-voltage devices such as fans to achieve multiple linkage functions.
After startup, register the callback of the Bluetooth data notification of the setting page. After receiving the notification data, parse the data. Pay attention to the data format. The data transmitted from the Bluetooth setting page is in the form of a hexadecimal string. Sending data to the Bluetooth device is achieved by calling different page communication mechanisms event.emit('EnvMonitorSendData2Device','fefe');. Communication with the device is achieved by subscribing to notifications. After receiving the data, parse it according to the custom format:
//屏幕打开时执行的函数
onLoad: function () {
//接收别的页面传过来的数据
event.on('environmetDataChanged', this, function(data) {
//另外一个页面传过来的data是16进制字符串形式
console.log("接收到蓝牙设备发来的数据:"+data)
//bat 1byte
var aa=parseInt(data[1]+data[3],16);
//co2 4byte
var f=parseInt(data[5]+data[7],16);
var g=parseInt(data[9]+data[11],16);
var bb=f*256+g;
//tvoc 4byte
var i=parseInt(data[13]+data[15],16);
var j=parseInt(data[17]+data[19],16);
var cc=i*256+j;
//实时修改显示值
var up0 = "charts[" + 0 + "].data";
var up1 = "charts[" + 1 + "].data";
var up2 = "charts[" + 2 + "].data";
this.setData({
[up0]:aa,
[up1]:bb,
[up2]:cc,
});
})
},
In the previous post, the broadcast and notification data sending functions of STM32WBA52CG have been tested, so on the device side, you only need to package and send the sensor data in a custom format.
|