uniapp App端 连接蓝牙并下发命令

1:ly.js

// import { TextDecoder } from 'text-encoding-utf-8';
let bluetoothOpen = false; // 手机蓝牙是否打开
let bluetoothConnect = false; // 设备和蓝牙是否连接
let isHaveDevice = false; // 是否查找到设备
let deviceId = null; // 设备id
let serviceId = null; // 服务id
let notify = null; // 监听uuid
let writeId = null; // 写入uuid
let timer = null
/**
 * 获取手机蓝牙是否打开
 */
const getBluetoothState = () => {
	// 主机模式
	return new Promise((resolve, reject) => {
		// mode: 'central',
		uni.openBluetoothAdapter({
			success: (r) => {
				console.log("蓝牙初始化成功");
				// 获取蓝牙的匹配状态
				uni.getBluetoothAdapterState({
					success: function(row) {
						console.log('蓝牙状态:', row.available);
						if (row.available) {
							bluetoothOpen = true;
							connectTimeout()
							resolve();
						} else {
							// 请开启蓝牙
							uni.showToast({
								title: '请打开蓝牙',
								icon: 'none'
							})
							bluetoothOpen = false;
							bluetoothConnect = false;

							reject();
						}
					},
					fail: function(err) {
						// 请开启蓝牙
						uni.showToast({
							title: '请打开蓝牙',
							icon: 'none'
						})
						bluetoothOpen = false;
						bluetoothConnect = false;
						reject();
					}
				})
			},
			fail: (err) => {
				// 请开启蓝牙
				uni.showToast({
					title: '请前往手机设置,打开此APP蓝牙权限',
					icon: 'none'
				})
				bluetoothOpen = false;
				bluetoothConnect = false;
				reject();
			}
		});
	});
};
/**
 * 开始搜索蓝牙设备
 */
const discoveryBluetooth = () => {
	return new Promise((resolve) => {
		uni.startBluetoothDevicesDiscovery({
			success(res) {
				console.log('搜索蓝牙外围设备完成', res)
				setTimeout(() => {
					resolve();
				}, 2000);
			}
		});
	})
};
// 关闭蓝牙搜索
const stopDiscoveryBluetooth = () => {
	uni.stopBluetoothDevicesDiscovery({
		success(r) {
			console.log("停止搜索蓝牙设备", r);
		}
	});
};




/**
 * 获取搜索到的设备信息
 */
const getBluetoothDevices = (deviceName) => {
	return new Promise((resolve, reject) => {
		uni.getBluetoothDevices({
			success(res) {
				console.log('获取搜索到的设备信息', res.devices);
				bluetoothConnect = false;
				// 过滤掉name为空或者未知设备的设备
				let devices = res.devices.filter(function(obj) {
					return obj.name !== "" && obj.name !== "未知设备"
				});
				console.log('有名称蓝牙列表', devices, deviceName);
				devices && devices.forEach(item => {
					if (item.name == deviceName || item.localName == deviceName) {
						deviceId = item.deviceId;
						isHaveDevice = true;
						resolve(isHaveDevice);
						console.log('设备ID', deviceId, item);
					}
				});
				timer = setInterval(() => {
					console.log('---',deviceId);
					if (!deviceId) {
						discoveryBluetooth().then(
							getBluetoothDevices2(deviceName).then(res => {
								resolve(res);
							})
						)
					} else {
						clearInterval(timer)
						timer = null
					}
				}, 1000)

			},
			fail: function() {
				console.log('搜索蓝牙设备失败');
				bluetoothConnect = false;
				isHaveDevice = false;
				reject(isHaveDevice);
			},
			complete: function() {
				console.log("蓝牙搜索完成");

			}
		});
	});
}
const getBluetoothDevices2 = (deviceName) => {
	return new Promise((resolve, reject) => {
		uni.getBluetoothDevices({
			success(res) {
				console.log('获取搜索到的设备信息', res);
				bluetoothConnect = false;
				// 过滤掉name为空或者未知设备的设备
				let devices = res.devices.filter(function(obj) {
					return obj.name !== "" && obj.name !== "未知设备"
				});
				console.log('有名称蓝牙列表', devices, deviceName);
				devices && devices.forEach(item => {
					if (item.name == deviceName || item.localName == deviceName) {
						deviceId = item.deviceId;
						isHaveDevice = true;
						clearInterval(timer)
						timer = null
						resolve(isHaveDevice);
						console.log('设备ID', deviceId, item);
					}
				});


			},
			fail: function() {
				console.log('搜索蓝牙设备失败');
				bluetoothConnect = false;
				isHaveDevice = false;
				reject(isHaveDevice);
			}

		});
	});
}
/**
 * 连接蓝牙
 * deviceId 蓝牙设备id
 */
const connectBluetooth = () => {
	return new Promise((resolve, reject) => {
		uni.createBLEConnection({
			deviceId: deviceId, // 设备id
			success() {
				bluetoothConnect = true;
				console.log('连接蓝牙成功', deviceId);
				// 蓝牙连接成功后关闭蓝牙搜索
				stopDiscoveryBluetooth();
				// 获取服务id
				getServiceId();
				setTimeout(() => {
					resolve();
				})
			},
			fail(err) {
				bluetoothConnect = false;
				console.log("蓝牙连接失败", err);
				reject();
			}
		});
	});
};
// 获取服务id
const getServiceId = () => {
	setTimeout(() => {
		uni.getBLEDeviceServices({
			deviceId: deviceId,
			success(res) {
				console.log(res, '服务id成功', deviceId);
				// 方案一
				res.services.forEach(item => {
					let firstFive = item.uuid.substring(0, 8);
					console.log(item, firstFive);
					if (firstFive == '0000FFF0') {
						serviceId = item.uuid;
						// 调用蓝牙监听和写入功能
						getCharacteId();
					}
				});


				// 方案二
				// let model = res.services[0];
				// serviceId = model.uuid;
				// console.log(res, '服务id成功', serviceId);
				// // 调用蓝牙监听和写入功能
				// getCharacteId();
			},
			fail(err) {
				console.log('获取服务失败', err);
			}
		})
	}, 1000)
};
// 获取蓝牙低功耗设备某个服务中所有特征
const getCharacteId = () => {
	uni.getBLEDeviceCharacteristics({
		deviceId: deviceId, // 蓝牙设备id
		serviceId: serviceId, // 蓝牙服务UUID
		success(res) {
			console.log('数据监听', res);
			res.characteristics.forEach(item => {
				// 003
				if (item.properties.notify === true) {
					// 监听
					notify = item.uuid;

					startNotice();
				}
				// 002
				if (item.properties.write === true) {
					// 写入 

					let writeId = item.uuid;
					uni.setStorageSync("writeId", item.uuid);


				}
			});
		},
		fail(err) {
			console.log("数据监听失败", err)
		}
	})
};
// 启用低功耗蓝牙设备特征值变化时的notify功能
const startNotice = () => {
	uni.notifyBLECharacteristicValueChange({
		characteristicId: notify,
		deviceId: deviceId,
		serviceId: serviceId,
		state: true,
		success(res) {
			// 监听低功耗蓝牙设备的特征值变化
			uni.onBLECharacteristicValueChange(result => {
				console.log("监听低功耗蓝牙设备的特征值变化", result);
				if (result.value) {
					// let decoder = new TextDecoder('utf-8');
					// let data = decoder.decode(result.value);
					// let data = result.value;
					console.log('帽子返回数据', result)
				}
			})
		}
	});
};


// 蓝牙发送数据
const writeData = (buffer) => {
	return new Promise((resolve, reject) => {
		console.log(uni.getStorageSync("writeId"), '下发命令1writeId');
		console.log(deviceId, '下发命令2deviceId');
		console.log(serviceId, '下发命令3serviceId');
		console.log(buffer, '下发命令4buffer');

		uni.writeBLECharacteristicValue({
			characteristicId: uni.getStorageSync("writeId"),
			deviceId: deviceId,
			serviceId: serviceId,
			value: buffer,
			success(res) {
				console.log("writeBLECharacteristicValue success", res);
				resolve();
			},
			fail(err) {
				console.log("报错了", err);
				reject();
			}
		});
	});
};

// 断开蓝牙
const closeBlec = () => {
	return new Promise((resolve, reject) => {
		uni.closeBLEConnection({
			deviceId: deviceId,
			success(res) {
				resolve();
				console.log(res, '断开成功')
			},
			fail(err) {
				reject();
				console.log("断开失败", err);
			}
		})
	})

}

const BLECStateChange = () => {
	return new Promise((resolve, reject) => {
		uni.onBLEConnectionStateChange(function(res) {
			// 该方法回调中可以用于处理连接意外断开等异常情况
			console.log(
				`device ${res.deviceId} state has changed, connected: ${res.connected}`)
			return resolve(res)

		})
	})

}

/*设置蓝牙搜索时间30秒, 如果超时没有搜索到就停止搜索*/
const connectTimeout = () => {
	setTimeout(() => {
		if (!bluetoothConnect) {
			clearInterval(timer)
			timer = null
			uni.stopBluetoothDevicesDiscovery({
				success: (res) => {
					console.log("停止搜索成功")
				}

			})
			uni.showToast({
				title: '蓝牙连接失败',
				icon: 'none'
			})
		}

	}, 30000)
}




// closeBLEConnection  deviceId  断开蓝牙
export default {
	getBluetoothState,
	discoveryBluetooth,
	stopDiscoveryBluetooth,
	getBluetoothDevices,
	connectBluetooth,
	getServiceId,
	getCharacteId,
	startNotice,
	writeData,
	closeBlec,
	BLECStateChange
};

2:inde.vue  页面使用

<template>
	<view class="device_container">
		<image src="https://beoka-file.oss-cn-beijing.aliyuncs.com/beoka_Iot/img/1000001166582801.png" class="bg">
		</image>
		<view class="card">
			<view class="tsip" @click="clicljydTisp">
			</view>
			<image class="mb" src="@/static/mb.png" style="width: 100%;height: 100%;"></image>
			<view class="info">
				<view class="beoka">
					<image src="@/static/beoka.png"></image>
				</view>
				<view class="infos">
					<view>
						蓝牙名称: {{bluetoothName?bluetoothName: 'XXXX XXXX XXXX'}}
					</view>
					<view>
						设备编号:{{deviceNo?deviceNo:'XXXXXXXXXX'}}
						<!-- 产品编号:{{deviceName?'866 9288 98376':' XXXX XXXX XXXX'}} -->
					</view>
				</view>
				<view class="ipt">
					<span>写入编码:</span>
					<view class="input">
						<input type="number" v-model="code" placeholder="请输入设备编码" />
						<image @click="scanCodeValue" src="@/static/scan.png" mode=""></image>
					</view>
				</view>


			</view>
		</view>


		<view>
			<view class="btn" v-if="bluetoothName&&bluetoothStatus">
				<view class="text" @click="clickBreak">
					断开蓝牙
				</view>
				<view class="text" @click="clickgh">
					确定写入编码
				</view>
			</view>

			<view class="sm" v-else @click="snacode">扫码连接设备 {{stateText}}</view>
		</view>

		<!-- 连接引导弹窗 -->
		<uni-popup ref="ljydPopup" type="center">
			<view class="ljyd">
				<view class="title">
					连接引导
					<image src="@/static/close.png" @click="clickClose('ljydPopup')"></image>
				</view>
				<view class="item">
					<view class="itemText">
						1.打开设备电源
					</view>
					<view class="itemImg">
						<image src="@/static/img1.png"></image>
					</view>
				</view>
				<view class="item">
					<view class="itemText">
						1.打开手机蓝牙
					</view>
					<view class="itemImg">
						<image src="@/static/img2.png"></image>
					</view>
				</view>
				<view class="item">
					<view class="itemText">
						1.连接设备
					</view>
					<view class="itemImg">
						<image src="@/static/img3.png"></image>
					</view>
				</view>
			</view>

		</uni-popup>

	</view>
</template>

<script>
	// Hi-BEOKA-POC003AEE
	import bluetooth from '@/uilts/ly.js';

	export default {
		data() {
			return {

				deviceNo: '',
				stateText: '未连接',
				bluetoothStatus: false, // 蓝牙连接状态
				bluetoothName: '', //蓝牙名字
				buffer: '', //下发数据
				code: '',
			}
		},

		onLoad() {

		},
		onShow() {

		},

		methods: {
			// 扫码
			snacode() {
				let _this = this;
				uni.scanCode({
					success: function(res) {
						let str = res.result
						var deviceNo = _this.getParameterByName('no', str)
						var bluetoothName = _this.getParameterByName('bluetooth', str)
						console.log(str, deviceNo, bluetoothName);
						if (deviceNo && bluetoothName) {
							_this.deviceNo = deviceNo
							_this.bluetoothName = bluetoothName
							_this.initBluetooth(() => {
								console.log('蓝牙连接成功');
							}, bluetoothName);
						} else {
							uni.showToast({
								title: '二维码参数错误',
								icon: 'none'
							})
						}
					},
					fail: (err => {
						uni.showToast({
							title: '扫码失败',
							icon: 'none'
						})
					})

				});
			},

			scanCodeValue() {
				let _this = this
				uni.scanCode({
					success: function(res) {
						let value = res.result
						_this.code = value
						console.log(value, 'deviceNo');
					}
				})
			},

			clickgh() {
				//针头=0xAA55   T=0x01 - 0xFF L=0x01 – 0xFF  V=0x00 - N
				if (!this.code) {
					return uni.showToast({
						title: '请输入设备编码',
						icon: 'none'
					})
				}
				let str = this.code;
				let arr = Array.from(str);
				let v = ["AA", "55", "09", '08', ...arr]; //命令数据表示16进制 
				//转16进制
				let list = this.parseInt(v)
				// 10进制转换
				let buffer = this.arr2ab(list)
				this.buffer = buffer
				console.log(buffer, list, this.bluetoothName, 'buffer');
				let _this = this
				if (this.bluetoothStatus) {
					_this.writeBlueData(buffer)
				} else {
					_this.initBluetooth(() => {
						_this.writeBlueData(buffer)
					}, _this.bluetoothName);
				}

			},
			clickBreak() {
				let _this = this
				_this.deviceNo = ''
				_this.stateText = '未连接'
				_this.bluetoothStatus = false // 蓝牙连接状态
				_this.bluetoothName = '' //蓝牙名字
				_this.buffer = '' //下发数据
				_this.code = ''
				bluetooth.closeBlec().then(res => {
					uni.showToast({
						title: '断开成功',
						icon: 'none'
					})
				})
			},
			// 向设备发送数据
			writeBlueData(buffer) {
				let _this = this
				uni.showLoading({
					title: '写入中...',
					icon: 'none'
				})
				bluetooth.writeData(buffer).then(res => {
					setTimeout(() => {
						_this.deviceNo = ''
						_this.stateText = '未连接'
						_this.bluetoothStatus = false // 蓝牙连接状态
						_this.bluetoothName = '' //蓝牙名字
						_this.buffer = '' //下发数据
						_this.code = ''
						bluetooth.closeBlec().then(res => {
							uni.showToast({
								title: '写入成功',
								icon: 'none'
							})
						})

					}, 1000)
					_this.bluetoothStatus = false // 蓝牙连接状态



				})
				.catch(err=>{
					uni.showToast({
						title: '写入失败',
						icon: 'none'
					})
				})
			},
			// 获取蓝牙和设备是否已连接
			initBluetooth(callback, bluetoothName) {
				let _this = this;
				_this.stateText = '搜索蓝牙'
				uni.showLoading({
					title: _this.stateText
				})
				// 初始化蓝牙
				bluetooth.getBluetoothState().then(() => {
					// 搜索外围蓝牙设备
					bluetooth.discoveryBluetooth().then(() => {
						_this.discoveryLoading = true;
						// 获取蓝牙设备
						bluetooth.getBluetoothDevices(bluetoothName).then((isHaveDevice) => {
							if (isHaveDevice) {
								_this.stateText = '连接中...'
								// 搜索到指定设备,连接蓝牙
								bluetooth.connectBluetooth().then((bluetoothConnect) => {
									console.log('连接成功');
									setTimeout(() => {
										callback()
										_this.stateText = '连接成功'
										uni.hideLoading();
										uni.showToast({
											title: '蓝牙连接成功',
											icon: 'none'
										})
										_this.bluetoothStatus = true;
									}, 1000)

									// 监听蓝牙与设备连接状态
									bluetooth.BLECStateChange().then(res => {
										_this.bluetoothStatus = res.connected
										_this.stateText = '未连接'
										console.log(res, '监听设备连接状态');
									})
								}).catch((err) => {
									_this.stateText = '连接失败'
									_this.bluetoothStatus = false;
									uni.hideLoading();
									uni.showToast({
										title: '蓝牙连接失败',
										icon: 'none'
									})
								})
							} else {
								// 未搜到设备
								_this.bluetoothStatus = false;
								uni.hideLoading();
								uni.showToast({
									title: '请打开设备',
									icon: 'none'
								})
							}
						}, () => {
							// 蓝牙搜索失败
							uni.hideLoading();
							uni.showToast({
								title: '蓝牙连接失败',
								icon: 'none'
							})
							_this.bluetoothStatus = false;
						});
					});
				}, () => {
					// 未开启蓝牙
					_this.bluetoothStatus = false;
				});
			},
			// 16进制
			parseInt(arr) {
				let list = arr.map((v) => {
					return parseInt(v, 16); //16表示v 是16进制格式的数据
				});
				return list

			},

			// 高低位
			IntToBytesLittleEndian(number, length) {
				var bytes = [];
				var i = 0;
				do {
					bytes[i++] = number & (255);
					number = number >> 8;
				} while (i < length)
				return bytes.reverse();
			},

			// 10进制转换
			arr2ab(arr) {
				const buffer = new ArrayBuffer(arr.length);
				const dataView = new DataView(buffer);
				for (var i = 0; i < arr.length; i++) {
					dataView.setUint8(i, arr[i]);
				}
				return buffer;
			},


			// 获取扫码进来 地址携带的参数
			getParameterByName(name, url) {
				name = name.replace(/[\[\]]/g, "\\$&");
				var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
					results = regex.exec(url);
				if (!results) return null;
				if (!results[2]) return '';
				return decodeURIComponent(results[2].replace(/\+/g, " "));
			},
			// 点击关闭
			clickClose(name) {
				this.$refs[name].close()
			},

			// 连接引导弹窗
			clicljydTisp() {
				console.log('999');
				this.$refs.ljydPopup.open()
			},

			clickPay() {
				uni.navigateTo({
					url: '/pages/index/orderlist'
				})

			},
		},

	}
</script>

<style lang="scss">
	page {
		height: 100%;
	}

	.bg {
		width: 100vw;
		height: 100vh;
	}

	.ljgh,

	.sm {
		border-radius: 100px;
		background: #3A87EF;
		width: calc(100% - 96rpx);
		height: 112rpx;
		box-shadow: 0px 20px 60px 0px rgba(58, 96, 178, 0.30);
		text-align: center;
		line-height: 112rpx;
		color: #FFF;
		font-size: 32rpx;
		font-style: normal;
		font-weight: 500;
		position: fixed;
		bottom: 120rpx;
		left: 50%;
		transform: translateX(-50%);
		letter-spacing: 2rpx;
	}

	.btn {
		width: 100%;
		position: fixed;
		bottom: 120rpx;
		display: flex;
		justify-content: space-evenly;

		.text {
			width: 45vw;
			height: 112rpx;
			border-radius: 100px;
			background: #3A87EF;
			line-height: 112rpx;
			color: #FFF;
			font-size: 32rpx;
			font-style: normal;
			font-weight: 500;
			text-align: center;
		}
	}





	.card {
		width: calc(100% - 64rpx);
		height: 520rpx;
		position: fixed;
		top: 43%;
		left: 50%;
		transform: translateX(-50%);

		.md {
			width: 100%;
			height: 100%;
		}

		.tsip {
			width: 32rpx;
			height: 32rpx;
			border-radius: 100%;
			position: absolute;
			right: 44rpx;
			top: 32rpx;
			// background-color: red;
			z-index: 1;

		}

		.info {
			position: absolute;
			top: 0;
			padding: 60rpx 40rpx 22rpx;
			box-sizing: border-box;
			width: 100%;
			height: 100%;

			.beoka {
				width: 65px;
				height: 20px;

				image {
					width: 65px;
					height: 20px;
				}

			}
		}

		.infos {
			font-size: 32rpx;
			font-style: normal;
			font-weight: 700;
			line-height: normal;
			margin-top: 44rpx;
			border-bottom: 2rpx dashed #E9ECF3;
			padding-bottom: 32rpx;
			color: #666;

			view {
				margin-top: 32rpx;
			}
		}

		.ipt {
			margin-top: 44rpx;
			display: flex;
			align-items: center;
			width: 100%;
			span {
				font-size: 32rpx;
				font-style: normal;
				font-weight: 700;
				line-height: normal;
			}

			.input {
				border: 2rpx solid #ccc;
				height: 80rpx;
				flex: 1;
				border-radius: 8rpx;
				display: flex;
				align-items: center;
				padding: 0 20rpx;

				input {
					flex: 1;
				}

				image {
					width: 50rpx;
					height: 50rpx;
				}
			}

		}


	}

	.ljyd {
		width: 644rpx;
		background-color: #fff;
		border-radius: 28rpx;
		padding-bottom: 16rpx;
		// height: 517px;

		.title {
			color: #333;
			text-align: center;
			font-size: 32rpx;
			font-style: normal;
			font-weight: 600;
			line-height: 44rpx;
			/* 137.5% */
			width: 100%;
			padding: 32rpx 0 48rpx;
			position: relative;

			image {
				width: 20rpx;
				height: 20rpx;
				position: absolute;
				right: 44rpx;
				top: 45rpx;
			}
		}

		.item {
			margin: 0 32rpx 32rpx;

			.itemText {
				color: var(--unnamed, #333);
				text-align: left;
				font-family: PingFang HK;
				font-size: 16px;
				font-style: normal;
				font-weight: 400;
				line-height: normal;
				letter-spacing: -0.408px;
				margin-bottom: 12rpx;
			}

			.itemImg {
				width: 340rpx;
				height: 210rpx;
				margin: auto;

				image {
					width: 100%;
					height: 100%;
				}
			}
		}
	}
</style>

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值