uniapp实现蓝牙自动连接并获取心电图(代码篇)

我们最近的项目是需要写一个app和公司设备通过蓝牙连接,并且获取到心电图,这次是实现自动的,用户在第一次的时候需要点击连接蓝牙实现自动连接,后续更是不需要点击直接获取心电图,直接上代码,供大家参考!

<template>
	<view>
		<page-head :title="title"></page-head>
		<!-- 心电图显示区 -->
		<view class="displayarea">
			心电图显示区
			<view class="container">
				<canvas canvas-id="grids" style="width: 750rpx; height: 750rpx;"></canvas>
			</view>
			</view>
		<!-- <view style="background-color: red;">心率:{{heartRateData[heartRateData.length-1]}}</view> -->
		<canvas canvas-id="waveform" id="waveform" style="width: 500px; height: 400px; margin: 0 auto"></canvas>
		<view class="uni-padding-wrap uni-common-mt">
			<!-- button class="heartrate">心率</button> -->
			<view class="uni-btn-v">
				<button type="primary" :disabled="disabled" @click="openBluetoothAdapter">连接蓝牙</button>
				<button type="primary" :disabled="!disabled" @click="closeBLEConnection">断开蓝牙设备</button>
				<button type="primary" :disabled="!disabled" @click="closeBluetoothAdapter">关闭蓝牙模块</button>
			</view>
		</view>
		<!-- 遮罩 -->
	</view>
</template>
<script>
	let num = 0;
	let timer = null;
	export default {
		data() {
			return {
				title: 'bluetooth',
				disabled: false,
				deviceId: '',
				serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
				writeCharacteristicsId: '',
				connectCharacteristicsId: '',
				characteristicId: '0000FFF1-0000-1000-8000-00805F9B34FB',
				heartRateData: [],
				macAddress: '',
				macValue: '',
				valueChangeData: {
					value: ''
				},
				extraLine: [],
			};
		},
		
		onLoad() {
			this.setOnBLECharacteristicValueChange();
			
			this.openBluetoothAdapter()
		},
		onShow() {
			const ctx = uni.createCanvasContext('waveform', this);
			if(timer != null){
				clearInterval(timer);
			}
			timer = setInterval(() => {
				if (this.heartRateData.length) {
					this.drawLine(ctx, this.heartRateData)
				}
			}, 50)
		},
		beforeDestroy() {
			clearInterval(timer);
			timer = null;
			this.closeBLEConnection()
			uni.closeBluetoothAdapter()
			
		},
		methods: {
			moveHandle() {},
			/**
			 * 关闭遮罩*/
			maskclose() {
				this.maskShow = false;
			},
			/**
			 * 选择设备*/
			queryDevices() {
				// this.newDeviceLoad = true;
				this.showMaskType = 'device';
				this.maskShow = true;
			},
			tapQuery(item) {
				if (this.showMaskType === 'device') {
					this.$set(this.disabled, 4, false);
					if (this.equipment.length > 0) {
						this.equipment[0] = item;
					} else {
						this.equipment.push(item);
					}
					this.newDeviceLoad = false;
				}
				if (this.showMaskType === 'service') {
					this.$set(this.disabled, 6, false);
					if (this.servicesData.length > 0) {
						this.servicesData[0] = item;
					} else {
						this.servicesData.push(item);
					}
				}
				if (this.showMaskType === 'characteristics') {
					this.$set(this.disabled, 7, false);
					if (this.characteristicsData.length > 0) {
						this.characteristicsData[0] = item;
					} else {
						this.characteristicsData.push(item);
					}
				}
				this.maskShow = false;
			},
			/**
			 * 第一步
			 * 初始化蓝牙设备
			 *
			 * */
			openBluetoothAdapter() {
				// 设置 监听蓝牙适配器状态变化事件
				console.log("第一步");
				this.disabled = true
				// this.setOnBluetoothAdapterStateChange()
				uni.openBluetoothAdapter({
					success: e => {
						console.log('openBluetoothAdapter success', e);
						this.startBluetoothDevicesDiscovery()
					},
					fail: e => {
						console.log(e)
						console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
						if (e.errCode !== 0) {
							initTypes(e.errCode, e.errMsg);
						}
					}
				});
			},
			/**
			 * 第二步,
			 * 监听蓝牙适配器状态变化事件
			 */
			setOnBluetoothAdapterStateChange() {
				console.log("第二步");
				uni.onBluetoothAdapterStateChange((res) => {
					console.log("第二步", res);
					if (res.available) {
						// 蓝牙适配器 ,则开始搜索蓝牙设备
						this.startBluetoothDevicesDiscovery()
					}
				})
			},
			/**
			 * 第二步,
			 * 开始搜索蓝牙设备
			 * */
			startBluetoothDevicesDiscovery() {
				console.log("第三步");
				// 搜索之前监听寻找到新设备的事件
				this.onBluetoothDeviceFound();
				uni.startBluetoothDevicesDiscovery({
					success: e => {

					},
					fail: e => {
						console.log('搜索蓝牙设备失败,错误码:' + e.errCode);
						if (e.errCode !== 0) {
							initTypes(e.errCode);
						}
					}
				});
			},
			/**
			 * 第三步
			 * 寻找到新设备
			 * 
			 * */
			onBluetoothDeviceFound() {
				console.log("第四步");
				uni.onBluetoothDeviceFound(res => {
					console.log(res);
					// 监听寻找到新设备的事件
					for (var i = 0; i < res.devices.length; i++) {
						let device = res.devices[i]
						// 根据设备名称找到对应的设备,然后链接设备
						if (device.name && device.name == 'FSC-BT1036-LE-09F8') {
							this.deviceId = device.deviceId
							// 停止设备查找
							this.stopBluetoothDevicesDiscovery()
							uni.showLoading({
								title: '连接蓝牙...'
							});
							uni.createBLEConnection({
								deviceId: this.deviceId,
								success:res => {
									uni.hideLoading()
									setTimeout(()=> {
										this.getBLEDeviceServices().then(res => {
											console.log(res);
											return this.getBLEDeviceCharacteristics()
										}).then(res => {
											console.log(res);
											return this.bleNotifyAndlisten()
										}).then(res => {
											console.log(res);
										
											// 需要延时 再去调用读取数据
											setTimeout(() => {
												this.readBLECharacteristicValue()
											}, 1000);
										})
									},1000)
									
								},
								fail: (err) => {
									uni.hideLoading()
									uni.showModal({
										title: "温馨提示",
										content: '"蓝牙连接失败'
									})
								}
							});
						}
					}
				});
			},
			/**
			 * 第四步
			 * 连接低功耗蓝牙设备
			 * */
			createBLEConnection(deviceId) {
				return 
			},
			/**
			 *  第五步
			 * 获取蓝牙所有服务
			 */
			getBLEDeviceServices() {
				return new Promise((reslove, reject) => {
					uni.getBLEDeviceServices({
						deviceId: this.deviceId,
						success: res => {
							console.log(res);
							reslove("[ok-5].获取蓝牙所有服务成功!")
						},
						fail: err => {
							reject(err)
						}
					})
				})
			},
			/**
			 *  第六步
			 * 获取该蓝牙设备某个服务的特征
			 */
			getBLEDeviceCharacteristics() {
				return new Promise((reslove, reject) => {
					uni.getBLEDeviceCharacteristics({
						deviceId: this.deviceId,
						serviceId: this.serviceId,
						success: res => {
							console.log('getBLEDeviceCharacteristics success');
							console.log(res);
							let characteristics = res.characteristics
							for (let i = 0; i < characteristics.length; i++) {
								let item = characteristics[i]
								if (item.properties.write) {
									this.writeCharacteristicsId = item.uuid;
								}

								if (item.properties.notify || item.properties.indicate) {
									this.connectCharacteristicsId = item.uuid
								}
							}
							console.log(this.writeCharacteristicsId, this.connectCharacteristicsId);
							reslove("[ok-6].获取蓝牙下该服务的特征成功!")
						},
						fail: err => {
							console.log('getBLEDeviceCharacteristics fail',err);
							reject(err)
						}
					})

				})
			},
			/**
			 *  第七步
			 * 启用低功耗蓝牙设备特征值变化时的 notify 功能,
			 */
			bleNotifyAndlisten() {
				return new Promise((reslove, reject) => {
					uni.notifyBLECharacteristicValueChange({
						state: true,
						deviceId: this.deviceId,
						serviceId: this.serviceId,
						characteristicId: this.connectCharacteristicsId,
						success: res => {
							console.log('notifyBLECharacteristicValueChange success', res);
							reslove("[ok-7]. notify 启动成功")
						},
						fail: err => {
							console.log('notifyBLECharacteristicValueChange err', err);
							reject(err)
						}
					})
				})
			},
			/**
			 *  第八步
			 * 发起读数据请求  
			 * 读取低功耗蓝牙设备的特征值的二进制数据值。注意:必须设备的特征值支持 read 才可以成功调用 */
			readBLECharacteristicValue() {
				console.log("第八步");
				uni.readBLECharacteristicValue({
					deviceId: this.deviceId,
					serviceId: this.serviceId,
					characteristicId: this.connectCharacteristicsId,
					success: (res) => {
						console.log('读取设备数据值成功');
						console.log(JSON.stringify(res));
					},
					fail: (res) => {
						console.log('读取设备数据值失败,错误码:' + res.errCode);
						// if (res.errCode !== 0) {
						// 	initTypes(e.errCode);
						// }
					}
				});
				//this.onBLECharacteristicValueChange();
			},
			/**
			 * 第九步,解析数据
			 * 监听低功耗蓝牙设备的特征值变化事件。必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。
			 * 
			 * */
			setOnBLECharacteristicValueChange() {
				let that = this
				// 必须在这里的回调才能获取
				uni.onBLECharacteristicValueChange((res) => {
					console.log("第九步");
					console.log('监听低功耗蓝牙设备的特征值变化事件成功');
					console.log(
						`characteristic ${res.characteristicId} has changed, now is ${JSON.stringify(res)}`);
					const unit8Array = new Uint8Array(res.value);
					// console.log("unit8Array: ", unit8Array);
					// 获取canvas绘图上下文
					// let heartRate = that.heartRateData;
					if (this.heartRateData.length >= 100) {
						this.heartRateData.shift();
					}
					this.heartRateData.push(unit8Array);
					// that.heartRateData = heartRate;
					console.log("----heartRate", this.heartRateData)
					console.log("length", this.heartRateData.length)
					this.macAddress = res.deviceId;
					this.macValue = this.ab2hex(res.value);
					this.valueChangeData.value = this.ab2hex(res.value);
					this.extraLine.push(this.macValue);
					this.valueChangeData = this.extraLine.join(' \n ');
				});
			},
			/**
			 * 停止搜索蓝牙设备*/
			stopBluetoothDevicesDiscovery() {
				uni.stopBluetoothDevicesDiscovery({
					success: e => {},
					fail: e => {
						console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);

					}
				});
			},

			/**
			 * 断开与低功耗蓝牙设备的连接*/
			closeBLEConnection() {
				uni.closeBLEConnection({
					deviceId:this.deviceId,
					success: res => {
						console.log(res);
						console.log('断开低功耗蓝牙成功:' + res.errMsg);
						this.$set(this.disabled, 1, false);
						this.$set(this.disabled, 3, true);
						this.$set(this.disabled, 4, true);
						this.$set(this.disabled, 5, true);
						this.$set(this.disabled, 6, true);
						this.$set(this.disabled, 7, true);
						this.$set(this.disabled, 8, true);
						this.$set(this.disabled, 9, true);
						this.equipment = [];
						this.servicesData = [];
						this.characteristicsData = [];
					},
					fail: e => {
						console.log('断开低功耗蓝牙成功,错误码:' + e.errCode);
						if (e.errCode !== 0) {
							initTypes(e.errCode);
						}
					}
				});
			},
			ab2hex(buffer) {
				const hexArr = Array.prototype.map.call(
					new Uint8Array(buffer),
					function(bit) {
						return ('00' + bit.toString(16)).slice(-2)
					}
				)
				return hexArr.join('')
			},
			drawLine(ctx) {
				// num++;
				// console.log("----------------num",num)
				// 				const y = (Number(unit8Array[0]) + (Number(unit8Array[1])<<8) + (Number(unit8Array[2])<<16) + (Number(unit8Array[3])<<24));

				// 				console.log("----",y);
				// 			 // console.log("ctx: ", ctx);
				// 			 console.log("that.heartRateDat: ", heartRate);
				// 绘制波形图
				const width = 600;
				const height = 600;
				// 设置波形图样式
				ctx.setStrokeStyle('#F79A18');
				ctx.setLineWidth(3);
				ctx.setLineCap("round")
				ctx.clearRect(0, 0, width, height);
				// 清除Canvas并绘制新的波形图
				ctx.beginPath();
				ctx.moveTo(-10, 0); // 将起点移动到Canvas的中间位置
				for (let i = 0; i < this.heartRateData.length; i++) {
					const y = (Number(this.heartRateData[i][0]) + (Number(this.heartRateData[i][1]) << 8) + (Number(this
						.heartRateData[i][2]) << 16) + (Number(
						this.heartRateData[i][3]) << 24));
					ctx.lineTo(i * 5, -y / 260 + 250); // 将数据绘制 n 到Canvas上,放大50倍以适应Canvas高度
				}
				ctx.stroke();
				ctx.draw();
			},
			/**
			 *     断开蓝牙模块          */
			closeBluetoothAdapter(OBJECT) {
				uni.closeBluetoothAdapter({
					success: res => {
						console.log('断开蓝牙模块成功');
						this.isStop = true;
						this.$set(this.disabled, 0, false);
						this.$set(this.disabled, 1, true);
						this.$set(this.disabled, 2, true);
						this.$set(this.disabled, 3, true);
						this.$set(this.disabled, 4, true);
						this.$set(this.disabled, 5, true);
						this.$set(this.disabled, 6, true);
						this.$set(this.disabled, 7, true);
						this.$set(this.disabled, 8, true);
						this.$set(this.disabled, 9, true);
						this.$set(this.disabled, 10, true);
						this.equipment = [];
						this.servicesData = [];
						this.characteristicsData = [];
						this.valueChangeData = {};
						this.adapterState = [];
						this.searchLoad = false;
						toast('断开蓝牙模块');
					}
				});
			}
		}
	};

	/**
	 * 判断初始化蓝牙状态*/
	function initTypes(code, errMsg) {
		switch (code) {
			case 10000:
				toast('未初始化蓝牙适配器');
				break;
			case 10001:
				toast('未检测到蓝牙,请打开蓝牙重试!');
				break;
			case 10002:
				toast('没有找到指定设备');
				break;
			case 10003:
				toast('连接失败');
				break;
			case 10004:
				toast('没有找到指定服务');
				break;
			case 10005:
				toast('没有找到指定特征值');
				break;
			case 10006:
				toast('当前连接已断开');
				break;
			case 10007:
				toast('当前特征值不支持此操作');
				break;
			case 10008:
				toast('其余所有系统上报的异常');
				break;
			case 10009:
				toast('Android 系统特有,系统版本低于 4.3 不支持 BLE');
				break;
			default:
				toast(errMsg);
		}
	}

	/**
	 * 弹出框封装    */
	function toast(content, showCancel = false) {
		uni.showModal({
			title: '提示',
			content,
			showCancel
		});
	}
</script>

<style>
	.uni-title {
		/* width: 100%; */
		/* height: 80rpx; */
		text-align: center;
	}

	.displayarea {}

	.uni-mask {
		position: fixed;
		top: 0;
		left: 0;
		bottom: 0;
		display: flex;
		align-items: center;
		width: 100%;
		background: rgba(0, 0, 0, 0.6);
		padding: 0 30rpx;
		box-sizing: border-box;
	}

	.uni-scroll_box {
		height: 70%;
		background: #fff;
		border-radius: 20rpx;
	}

	.uni-list-box {
		margin: 0 20rpx;
		padding: 15rpx 0;
		border-bottom: 1px #f5f5f5 solid;
		box-sizing: border-box;
	}

	.uni-list:last-child {
		border: none;
	}

	.uni-list_name {
		font-size: 30rpx;
		color: #333;
		white-space: pre-wrap;
	}

	.uni-list_item {
		font-size: 24rpx;
		color: #555;
		line-height: 1.5;
	}

	.uni-success_box {
		position: absolute;
		left: 0;
		bottom: 0;
		min-height: 100rpx;
		width: 100%;
		background: #fff;
		box-sizing: border-box;
		border-top: 1px #eee solid;
	}

	.uni-success_sub {
		/* width: 100%%; */
		height: 100rpx;
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 0 30rpx;
	}

	.uni-close_button {
		padding: 0 20rpx;
		height: 60rpx;
		line-height: 60rpx;
		background: #ce3c39;
		color: #ffffff;
		border-radius: 10rpx;
	}

	.uni-success_content {
		height: 600rpx;
		margin: 30rpx;
		margin-top: 0;
		border: 1px #eee solid;
		padding: 30rpx;
	}

	.uni-content_list {
		padding-bottom: 10rpx;
		border-bottom: 1px #f5f5f5 solid;
	}

	.uni-tips {
		text-align: center;
		font-size: 24rpx;
		color: #666;
	}

	.heartrate {
		width: 200rpx;
		height: 150rpx;
		line-height: 150rpx;
		background-color: #ce3c39;
		border: 0;
		color: #fff;
	}
</style>

我这里是直接拿设备的名称和uuid,因考虑到用户大多数为中老年人,实现一键自动连接完全有必要(只是自己的一些看法,这个还是得根据需求来)只不过我们的需求是这样的,如果有更好的方法请大家多多评论!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值