【NUCLEO-WB09KE】PC机与蓝牙设备通讯与web编程
[复制链接]
1、测试介绍
本测试 使用开发板模拟心率仪,用来验证连接性、BLE、BLE 协议、BLE 配对、BLE 配置文件等项目。其中的程序可以作为BLE的编程框架来使用。测试使用NUCLEO-WB09KE作为BLE“服务端”,客户端使用web页面的javascript程序作为“客户端”。本测试最开始使用移动客户端作为测试工具,最近我在学习scratch编程时,发现现在的node.js也可以支持BLE的API,并且可以很好的使用web浏览器作为客户端向web网站发送测试数据,于是就改用javascript作为客户端了。
2、客户端介绍
客户端的源码是我从 项目下载的。心率仪作为BLE的标准设备。通讯和数据格式已经被固定格式化了。只要提供规定的API函数就可以使用任何的客户端程序和设备链接。
首先是定义一个index.html文件作为页面
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Heart Rate Sensor Demo</title>
<meta name="description" content="Monitor a heart rate sensor with a Web Bluetooth app.">
<link rel="icon" sizes="192x192" href="../favicon.png">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="container">
<div id="statusText">GET ❤</div>
<canvas id="waves"></canvas>
</div>
<script src="heartRateSensor.js"></script>
<script src="app.js"></script>
</body>
</html>
程序主要分为app.js,本程序主要是用来在画布“canvas”上面显示数据和界面交互。画布的id="waves"。
var canvas = document.querySelector('canvas');
var statusText = document.querySelector('#statusText');
statusText.addEventListener('click', function() {
statusText.textContent = 'Breathe...';
heartRates = [];
heartRateSensor.connect()
.then(() => heartRateSensor.startNotificationsHeartRateMeasurement().then(handleHeartRateMeasurement))
.catch(error => {
statusText.textContent = error;
});
});
function handleHeartRateMeasurement(heartRateMeasurement) {
heartRateMeasurement.addEventListener('characteristicvaluechanged', event => {
var heartRateMeasurement = heartRateSensor.parseHeartRate(event.target.value);
statusText.innerHTML = heartRateMeasurement.heartRate + ' ❤';
heartRates.push(heartRateMeasurement.heartRate);
drawWaves();
});
}
var heartRates = [];
var mode = 'bar';
canvas.addEventListener('click', event => {
mode = mode === 'bar' ? 'line' : 'bar';
drawWaves();
});
function drawWaves() {
requestAnimationFrame(() => {
canvas.width = parseInt(getComputedStyle(canvas).width.slice(0, -2)) * devicePixelRatio;
canvas.height = parseInt(getComputedStyle(canvas).height.slice(0, -2)) * devicePixelRatio;
var context = canvas.getContext('2d');
var margin = 2;
var max = Math.max(0, Math.round(canvas.width / 11));
var offset = Math.max(0, heartRates.length - max);
context.clearRect(0, 0, canvas.width, canvas.height);
context.strokeStyle = '#00796B';
if (mode === 'bar') {
for (var i = 0; i < Math.max(heartRates.length, max); i++) {
var barHeight = Math.round(heartRates[i + offset ] * canvas.height / 200);
context.rect(11 * i + margin, canvas.height - barHeight, margin, Math.max(0, barHeight - margin));
context.stroke();
}
} else if (mode === 'line') {
context.beginPath();
context.lineWidth = 6;
context.lineJoin = 'round';
context.shadowBlur = '1';
context.shadowColor = '#333';
context.shadowOffsetY = '1';
for (var i = 0; i < Math.max(heartRates.length, max); i++) {
var lineHeight = Math.round(heartRates[i + offset ] * canvas.height / 200);
if (i === 0) {
context.moveTo(11 * i, canvas.height - lineHeight);
} else {
context.lineTo(11 * i, canvas.height - lineHeight);
}
context.stroke();
}
}
});
}
window.onresize = drawWaves;
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
drawWaves();
}
});
heartRateSensor.js文件,js使用BLE通过API从设备中调测试数据。设备BLE被定义在navigator.bluetooth.requestDevice中,函数将返回一个设备class,成功后装配该对象。
(function() {
'use strict';
class HeartRateSensor {
constructor() {
this.device = null;
this.server = null;
this._characteristics = new Map();
}
connect() {
return navigator.bluetooth.requestDevice({filters:[{services:[ 'heart_rate' ]}]})
.then(device => {
this.device = device;
return device.gatt.connect();
})
.then(server => {
this.server = server;
return server.getPrimaryService('heart_rate');
})
.then(service => {
return this._cacheCharacteristic(service, 'heart_rate_measurement');
})
}
/* Heart Rate Service */
startNotificationsHeartRateMeasurement() {
return this._startNotifications('heart_rate_measurement');
}
stopNotificationsHeartRateMeasurement() {
return this._stopNotifications('heart_rate_measurement');
}
parseHeartRate(value) {
// In Chrome 50+, a DataView is returned instead of an ArrayBuffer.
value = value.buffer ? value : new DataView(value);
let flags = value.getUint8(0);
let rate16Bits = flags & 0x1;
let result = {};
let index = 1;
if (rate16Bits) {
result.heartRate = value.getUint16(index, /*littleEndian=*/true);
index += 2;
} else {
result.heartRate = value.getUint8(index);
index += 1;
}
let contactDetected = flags & 0x2;
let contactSensorPresent = flags & 0x4;
if (contactSensorPresent) {
result.contactDetected = !!contactDetected;
}
let energyPresent = flags & 0x8;
if (energyPresent) {
result.energyExpended = value.getUint16(index, /*littleEndian=*/true);
index += 2;
}
let rrIntervalPresent = flags & 0x10;
if (rrIntervalPresent) {
let rrIntervals = [];
for (; index + 1 < value.byteLength; index += 2) {
rrIntervals.push(value.getUint16(index, /*littleEndian=*/true));
}
result.rrIntervals = rrIntervals;
}
return result;
}
/* Utils */
_cacheCharacteristic(service, characteristicUuid) {
return service.getCharacteristic(characteristicUuid)
.then(characteristic => {
this._characteristics.set(characteristicUuid, characteristic);
});
}
_readCharacteristicValue(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
return characteristic.readValue()
.then(value => {
// In Chrome 50+, a DataView is returned instead of an ArrayBuffer.
value = value.buffer ? value : new DataView(value);
return value;
});
}
_writeCharacteristicValue(characteristicUuid, value) {
let characteristic = this._characteristics.get(characteristicUuid);
return characteristic.writeValue(value);
}
_startNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
// Returns characteristic to set up characteristicvaluechanged event
// handlers in the resolved promise.
return characteristic.startNotifications()
.then(() => characteristic);
}
_stopNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
// Returns characteristic to remove characteristicvaluechanged event
// handlers in the resolved promise.
return characteristic.stopNotifications()
.then(() => characteristic);
}
}
window.heartRateSensor = new HeartRateSensor();
})();
当收到数据后调用相关的事件函数。BLE的API请参考:https://developer.mozilla.org/en-US/docs/Web/API/BluetoothDevice
3、测试过程
(1)首先使用STM32Cub下载BLE_HeartRate程序到本地磁盘。
(2)使用编译工具烧写到开发板。
我使用的是keil工具烧写到开发板。烧写完成后,开发板就可以模拟心率仪了。
(3)使用PC机连接和配对心率仪HR_5C
这里需要注意使用比较高的BLE适配器,最初我使用的是4.0的适配器,只能连接但是会出现问题。这里不知道是我的适配器的问题还是什么问题。我换成BLE 5.4的适配器就没有出现问题了。我的BLE 4.0是需要驱动的。是不是和这个有关系就不知道了,所以还请知道的朋友科普。
(4)配对连接设备
如果不配对就会连接失败
这个API会记录上次的配对设备,如果连接不上可以将设备重新上电和运行程序。
(5)配对连接后就可以从页面上显示数据了
4、总结
蓝牙设备有很多的属性文件。如果想开发BLE设备需要仔细的研究GATT官方。ST官方的协议栈编程的框架学习曲线较为陡直。目前我也是入门,在后期的测试中我会分享一些心得。
|