uni-app 微信小程序蓝牙模块的解耦封装-持续更新

这篇具有很好参考价值的文章主要介绍了uni-app 微信小程序蓝牙模块的解耦封装-持续更新。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、核心代码

core.js

import {
	showModal,
	stringToHex,
	sleep,
	uniqueArr,
	arrayBufferToHexString,
	hexStringToArrayBuffer,
	compareVersion
} from './tool.js'
//buffer分包,根据MTU的长度进行分包,蓝牙分发官方建议20个字节一包,但是随着硬件的发展,可设置一次传输的最大单元
//根据MTU的大小,把所发数据进行分包处理
const getBufferArrayByMtu = (data) => {
	let writePacketLen = bluetoothCore.mtu;
	let num = 0;
	if (typeof data == "undefined") return [];
	let allBuffer = hexStringToArrayBuffer(data); //16进制字符串转ArrayBuffer
	let bufferLen = allBuffer.byteLength;
	let bufferArray = [];
	while (bufferLen > 0) {
		let buffer;
		if (bufferLen > writePacketLen) {
			buffer = allBuffer.slice(num, num + writePacketLen);
			num = num + writePacketLen;
			bufferLen -= writePacketLen;
			bufferArray.push(buffer);
		} else {
			buffer = allBuffer.slice(num, num + bufferLen);
			num += bufferLen;
			bufferLen -= bufferLen;
			bufferArray.push(buffer);
		}
	}
	return bufferArray;
}



// 3位数是自己定义的,4位数是蓝牙功能确定的,因为页面有交互,故定义一些字段进行交互提示
export const bluetoothStatus = {
	'000': '正在搜索蓝牙设备',
	'001': '正在连接',
	'002': '连接成功',
	'003': '正在通讯',
	'004': '通讯完成',
	'005': '请检查设备是否开启蓝牙!',
	'006': '蓝牙已断开',
	"-1": "已经连接",
	"0": "正常",
	"10000": "开启蓝牙后,才可连接设备",
	"10001": "开启蓝牙后,才可连接设备",
	"10002": "没有找到指定设备",
	"10003": "连接失败",
	"10004": "没有找到指定服务",
	"10005": "没有找到指定特征值",
	"10006": "当前连接已断开",
	"10007": "当前特征值不支持此操作",
	"10008": "其余所有系统上报的异常",
	"10009": "Android 系统特有,系统版本低于 4.3 不支持 BLE",
	"10012": "操作超时",
	"10013": "连接 deviceId 为空或者是格式不正确"
}



//核心的重要的bluetoothCore对象
let bluetoothCore = {
	bluetoothStatus: {}, //有时候需要自己定义通讯的提示文字
	errorCallback: () => {}, //通讯过程中出现错误的回调处理方法
	isErrorShowodal: true, //错误消息是否用showModal展示,因为有些错误不需要展示,或者已经展示在了其他的地方
	tip: '', //展示在页面上的信息,包括正式进行的提示和蓝牙反馈的错误提示
	status: '', //需要像页面展示的蓝牙状态
	SERVICEID: "0000FEE7-0000-2222-8000-00805F9B34FB",
	NOTIFYID: "0000FEC1-0000-2222-8000-00805F9B34FB",
	WRITEID: "0000FEC1-0000-2222-8000-00805F9B34FB",
	STARTPACKET: '54424F582C435143542C', //报文回复的开始标志
	ENDPACKET: '2C54424F58454E44', //报文回复的结束标志
	planConnectBluetoothDevice: {}, //计划去连接的蓝牙设备
	connectedBluetoothDevice: {}, //已经连接上的蓝牙设备
	bluetoothList: [], //展示的蓝牙列表
	isDiscovering: true, //0为未在搜索,1为正在搜索
	isImmediateConnect: true, //是否直接连接
	isImmediateWrite: true, //是否立即写数据
	filterProp: 'name', // 蓝牙过滤的属性名称
	filterPattern: /^\d{10}$/, // 过滤蓝牙列表的正则表达式
	isConnected: false, //设备是否连接,实际的连接状态
	sendPacket: '', //当前发送的报文
	respondPacket: '', //监听到的组合报文
	isInit: false, //是否进行了初始化,只进行初始化一次就可以了
	mtu: 20,
	setTip: {}, //哪一步不需要设置提示 {'004':false}
	discoveryTimer: null, // 蓝牙搜索停止的计时器
	bluetoothDevicesDiscoveryTimeout: 10000, //多少秒后停止搜索蓝牙
	isStopExe: false, //蓝牙断开后,是否停止执行
	isCancelOparate: false, //是否中途取消操作,取消操作后,在异步方法中停止执行
	isReceivePacketFinished: false, //是否此次接报文完毕,如果接收报文完毕,其他接收的就是日志,此字段表示,在日志开始的时候,可以发送指令,指令正常回复有开始和结束标志,但是日志报文没有
	communicationStatus: 0, //每次通讯前设置为0,通讯成功为1,通讯成功之前就失败为-1,有可能在此次通讯中断开了蓝牙,要把有些状态还原,比如开关状态
	parsePacketCallback: (result) => {}, //报文解析完后的回调
	//解析报文的方式,每种蓝牙设备的协议方式不一样,故解析也不一样
	parsePacketMethod: (value) => {

	},
	//执行蓝牙的取消操作,由于蓝牙异步操作,在以下几个重要地方做拦截操作,搜索监听中、下发指令中、解析指令中
	cancelOparate: () => {
		bluetoothCore.isCancelOparate = true;
		bluetoothCore.status = '000';
		bluetoothCore.tip = '正在搜索蓝牙设备';
	},
	//解析日志报文不需要判断首帧和尾帧,直接解析收到的字符串
	parseLogPacketMethod: () => {

	},
	//蓝牙连接后的回调
	bluetoothConnectedCallback: (connected) => {

	},
	//下发指令,调用这个方法
	writePacket: (params) => {
		if (bluetoothCore.isCancelOparate) return;
		bluetoothCore.setBlueTip('003'); //正在通讯
		bluetoothCore.writeDataToDeviceBySubPackage();
	},
	//蓝牙已断开后,不应该在提示其他异步的操作
	setBlueTip: (status, isError = false) => {
		if (!bluetoothCore.isStopExe && !bluetoothCore.isCancelOparate) {
			bluetoothCore.status = status;
			let newBluetoothStatus = Object.assign({}, bluetoothStatus, bluetoothCore
				.bluetoothStatus); // 有时候要自己定义通讯的提示文字
			let isSetTip = bluetoothCore.setTip[status] + ''; // 如果设置了false,则不显示,默认为显示
			if (isSetTip !== 'false') { //有些状态下,不需要告知通讯完成
				bluetoothCore.tip = newBluetoothStatus[status];
			}
			//如果再调用蓝牙API的时候,发生了错误,则看是否提示用户
			if (isError) {
				if (bluetoothCore.isErrorShowodal) {
					showModal(bluetoothCore.tip);
				}
				//如果再通讯成功之前就失败了,则认为此次通讯是不成功的
				if (bluetoothCore.communicationStatus == 0) {
					bluetoothCore.communicationStatus = -1;
				}
				bluetoothCore.errorCallback();
			}
			if (status == '006') { // 如果状态等于006,则停止执行,停止显示后面的内容
				bluetoothCore.isStopExe = true;
			}
		}

	},
	//获取在蓝牙模块生效期间所有已发现的蓝牙设备,包括已经和本机处于连接状态的设备
	getBluetoothDevices: () => {
		return new Promise((resolve) => {
			uni.getBluetoothDevices({
				success(res) {
					let devicesList = bluetoothCore.filterDevices(res.devices);
					resolve(devicesList);
				}
			})
		})
	},
	//解析报文,判断解析的是否是日志
	parsePacket(val, isLog = false) {
		if (bluetoothCore.isCancelOparate) return;
		bluetoothCore.communicationStatus = 1; //通讯成功标志
		bluetoothCore.setBlueTip('004'); // 通讯完成
		if (!isLog) {
			console.log(`正常通讯回复的报文数据-`, val);
			let result = bluetoothCore.parsePacketMethod(val);
			bluetoothCore.parsePacketCallback(result)
		} else {
			bluetoothCore.parseLogPacketMethod(val)
		}
	},
	async clickCreateBLEConnection() {
		let devicesList = await bluetoothCore.getBluetoothDevices();
		if (devicesList.length) { //判断已搜索的设备里是否有目标设备,如果有,则直接连接,如果没有,则先搜索在连接
			bluetoothCore.planConnectBluetoothDevice = Object.assign({}, bluetoothCore
				.planConnectBluetoothDevice, devicesList[0])

			bluetoothCore.createBLEConnection();
		} else {
			bluetoothCore.startBluetoothDevicesDiscovery();
		}
	},
	searchBluetooth(params) {
		bluetoothCore = Object.assign(bluetoothCore, params);
		if (!bluetoothCore.isInit) { // 如果没有进行初始化,则先进行初始化
			bluetoothCore.init();
		} else { //如果进行了初始化,则直接进行搜索
			bluetoothCore.startBluetoothDevicesDiscovery();
		}
	},
	//点击设备连接操作
	operateBluetooth(params) {
		console.log('params==', params)
		bluetoothCore.isStopExe = false;
		bluetoothCore.isCancelOparate = false;
		bluetoothCore = Object.assign(bluetoothCore, params);
		//先生成一个报文
		if (!bluetoothCore.isInit) { // 如果没有进行初始化,则先进行初始化
			bluetoothCore.init();
		} else { // 判断是否连接
			if (bluetoothCore.isConnected) { // 判断当前连接的车辆是否是现在的目标车辆
				if (bluetoothCore.connectedBluetoothDevice[bluetoothCore.filterProp] == bluetoothCore
					.planConnectBluetoothDevice[bluetoothCore.filterProp]) {
					//如果当前连接的设备是目标设备,则直接写入数据
					bluetoothCore.writePacket();
				} else { // 断开蓝牙,再连接目标设备
					bluetoothCore.closeBLEConnection(() => {
						bluetoothCore.clickCreateBLEConnection();
					})
				}
			} else {
				bluetoothCore.clickCreateBLEConnection();
			}
		}
	},
	// 初始化蓝牙模块
	init() {
		// 初始化蓝牙对象
		uni.openBluetoothAdapter({
			success(res) {
				bluetoothCore.isInit = true;
				bluetoothCore.openBluetoothAdapterCallback();
				console.log("打开蓝牙设配器成功", res)
			},
			fail(res) {
				console.log("打开蓝牙设配器失败", res)
				bluetoothCore.setBlueTip(res.errCode, true);
			},
			complete() {

			}
		})
	},
	openBluetoothAdapterCallback() {
		// 监听蓝牙适配器的变化
		uni.onBluetoothAdapterStateChange((res) => {
			console.log('蓝牙适配器状态变化', res);
		});

		// 监听搜索新设备
		uni.onBluetoothDeviceFound((res) => {
			//console.log('搜索到新设备', res);
			//1、有可能展示列表,有可能直接连接
			if (bluetoothCore.isCancelOparate) return;
			bluetoothCore.bluetoothDeviceFoundCallback(res.devices);
		});

		// 监听蓝牙连接状态
		uni.onBLEConnectionStateChange((res) => {
			console.log('蓝牙连接状态变化', res);
			bluetoothCore.isConnected = res.connected;
			bluetoothCore.bluetoothConnectedCallback(res.connected);
			if (!res.connected) {
				bluetoothCore.setBlueTip('006', true); //蓝牙已断开
			}
		});

		// 监听MTU的变化
		uni.onBLEMTUChange((res) => {
			console.log('MTU变化', res);
			//获取到的mtu为517,但是设置的时候只能为最大512
			bluetoothCore.mtu = res.mtu - 10;
		});

		bluetoothCore.startBluetoothDevicesDiscovery();
	},

   //过滤搜索到的设备,如果搜索了立马连接,则返回指定设备,否则就只是简单的搜索列表
	filterDevices: (devices) => {
		let targetDevices = [];
		targetDevices = devices.filter((item) => {
			let name = item.name;
			//console.log("搜索到的设备名称:",name)
			if (bluetoothCore.isImmediateConnect) { //返回指定的那一个
				return name.indexOf(bluetoothCore.planConnectBluetoothDevice[bluetoothCore
					.filterProp]) != -1;
			} else { //返回列表
				return name != "" && bluetoothCore.filterPattern.test(name);
			}
		});
		return targetDevices;
	},
	//监听搜索到的设备
	bluetoothDeviceFoundCallback(devices) {
		let targetDevices = bluetoothCore.filterDevices(devices);
		console.log('搜索到的目标设备', targetDevices)
		if (targetDevices.length) {
			if (bluetoothCore.isImmediateConnect) {
				//搜索到蓝牙设备后,则停止蓝牙搜索
				//清除蓝牙搜索的定时器
				clearTimeout(bluetoothCore.discoveryTimer);
				bluetoothCore.planConnectBluetoothDevice = Object.assign({}, bluetoothCore
					.planConnectBluetoothDevice, targetDevices[0]); //合并参数  
				bluetoothCore.bluetoothList = targetDevices;
				bluetoothCore.stopBluetoothDevicesDiscovery(() => {
					bluetoothCore.createBLEConnection();
				})

			} else {
				bluetoothCore.bluetoothList = uniqueArr([...bluetoothCore.bluetoothList, ...targetDevices]);
			}
		}
	},
	createBLEConnection() {
		bluetoothCore.setBlueTip('001');
		console.log('蓝牙连接')
		uni.createBLEConnection({
			deviceId: bluetoothCore.planConnectBluetoothDevice.deviceId,
			success(res) {
				bluetoothCore.setBlueTip('002'); //连接成功
				console.log(`与蓝牙设备${bluetoothCore.planConnectBluetoothDevice.name}创建连接成功`, res);
				bluetoothCore.isConnected = true;
				bluetoothCore.connectedBluetoothDevice = Object.assign({}, bluetoothCore
					.planConnectBluetoothDevice);
				//如果是安卓手机并且安卓系统 5.1 以上版本有效
				uni.getSystemInfo({
					success: function(res) {
						// res.platform 可以获取手机的操作系统类型,如 "android", "ios"
						// res.system 可以获取手机的操作系统版本,如 "Android 5.1.1"
						console.log("操作系统类型", res.platform);
						console.log("操作系统版本", res.system);
						if (res.platform === 'android' && compareVersion(res.system.split(' ')[
									1]
								.toString(), '5.1.0') >=
							0) {
							// 在安卓手机且系统版本大于等于 5.1.0 时执行相应的逻辑
							uni.setBLEMTU({
								deviceId: bluetoothCore.connectedBluetoothDevice
									.deviceId,
								mtu: bluetoothCore.mtu,
								success(res) {
									console.log('设置mtu成功', res)
								},
								fail(res) {
									console.log('设置mtu失败', res)
								},
								complete() {
									bluetoothCore.getBLEDeviceServices();
								}
							});
						} else {
							console.log('设置mtu不符合条件');
							bluetoothCore.getBLEDeviceServices();
						}
					}
				});
			},
			fail(res) {
				console.log('蓝牙连接失败', res);
				bluetoothCore.setBlueTip(res.errCode, true);
			}
		})
	},
	//ios必须要执行
	getBLEDeviceServices() {
		uni.getBLEDeviceServices({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			success(res) {
				console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}服务成功`, res);
				bluetoothCore.getBLEDeviceCharacteristics();
			}
		})
	},
	//ios必须要执行
	getBLEDeviceCharacteristics() {
		uni.getBLEDeviceCharacteristics({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			serviceId: bluetoothCore.SERVICEID,
			success: function(res) {
				console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}特征值成功`,
					res);
				bluetoothCore.notifyAndOnBLECharacteristicValueChange();
			},
			fail(res) {
				console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}特征值失败`,
					res);
			}
		})
	},

	//判断报文,如果字符串中含有帧头,则是命令回复,为了防止有分包的情况,设置isReceivePacketFinished为标识字段,否则为日志标志
	notifyValueCallback(val) {
		console.log('设备监听返回的报文', val)
		let start = val.substring(0, bluetoothCore.STARTPACKET.length);
		if (start == bluetoothCore.STARTPACKET) { //如果有报文开始帧,重置接收的报文
			bluetoothCore.respondPacket = "";
			bluetoothCore.isReceivePacketFinished = false;
		}
		bluetoothCore.respondPacket = bluetoothCore.respondPacket + val;
		let end = bluetoothCore.respondPacket.substring(bluetoothCore.respondPacket
			.length -
			bluetoothCore.ENDPACKET.length);

		if (bluetoothCore.isReceivePacketFinished) { //此段标志为日志报文
			bluetoothCore.parsePacket(val, true)
		}
		if (end == bluetoothCore.ENDPACKET) {//如果有报文结束帧,则重置接收的报文,把带有帧头帧尾的报文进行解析
			bluetoothCore.isReceivePacketFinished = true;
			let respondPacket = bluetoothCore.respondPacket;
			bluetoothCore.respondPacket = '';
			bluetoothCore.parsePacket(respondPacket, false);
		}

	},

	notifyAndOnBLECharacteristicValueChange() {
		//防止没有更换状态
		uni.notifyBLECharacteristicValueChange({
			state: true,
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			serviceId: bluetoothCore.SERVICEID,
			characteristicId: bluetoothCore.NOTIFYID,
			success(res) {
				if (bluetoothCore.isImmediateWrite) {//如果notify监听成功,则直接写报文
					bluetoothCore.writePacket();
				}
				console.log("开启notify通知模式成功", res);
				uni.onBLECharacteristicValueChange((res) => {
					let val = arrayBufferToHexString(res.value);
					bluetoothCore.notifyValueCallback(val);

				})
			},
			fail(res) {}
		})
	},
	writeBLECharacteristicValue(value, isLastPackage = true) {
		console.log('写入的数据', arrayBufferToHexString(value))
		uni.writeBLECharacteristicValue({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			serviceId: bluetoothCore.SERVICEID,
			characteristicId: bluetoothCore.WRITEID,
			value,
			success(res) {
				console.log("写入数据成功", res);
				//判断是否是最后一包,如果是最后一包,则开启通讯计时器
			},
			fail(res) {
				console.log("写入数据失败", res);
				bluetoothCore.setBlueTip(res.errCode, true);
			}

		})
	},

	//不分包发送数据
	writeDataToDevice() {
		let allBuffer = hexStringToArrayBuffer(bluetoothCore.sendPacket);
		bluetoothCore.writeBLECharacteristicValue(allBuffer);
	},
	//分包发送数据
	async writeDataToDeviceBySubPackage() {
		bluetoothCore.respondPacket = '';
		let bufferArray = getBufferArrayByMtu(bluetoothCore.sendPacket);
		for (let i = 0; i < bufferArray.length; i++) {
			await sleep(10); //同步延迟1ms
			bluetoothCore.writeBLECharacteristicValue(bufferArray[i], i == bufferArray.length - 1);
		}
	},
	startBluetoothDevicesDiscovery() {
		bluetoothCore.isDiscovering = true;
		bluetoothCore.setBlueTip('000'); //正在搜索蓝牙设备
		uni.startBluetoothDevicesDiscovery({
			services: [], //添加了SERVICEID搜索不到蓝牙
			allowDuplicatesKey: false, //是否允许重复上报同一设备。如果允许重复上报,则 uni.onBlueToothDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同。
			powerLevel: 'high',
			success() {
				console.log("开始蓝牙搜索成功");
				bluetoothCore.discoveryTimer = setTimeout(() => {
					bluetoothCore.stopBluetoothDevicesDiscovery();
				}, bluetoothCore.bluetoothDevicesDiscoveryTimeout)
			},
			fail(res) {
				console.log("开始蓝牙搜索失败:", res);
			}
		})
	},
	stopBluetoothDevicesDiscovery(callback = () => {}) {
		bluetoothCore.isDiscovering = false;
		//如果停止了,都没有搜索到蓝牙设备,则认为没有搜索到蓝牙设备
		if (!bluetoothCore.bluetoothList.length) {
			bluetoothCore.setBlueTip('005', true); //请检查设备是否开启蓝牙
		}
		uni.stopBluetoothDevicesDiscovery({
			success() {
				callback();
				console.log("停止蓝牙搜索成功");
			},
			fail(res) {
				console.log("停止蓝牙搜索失败:", res);
			},

		})
	},
	closeBLEConnection(callback = () => {}) {
		//关闭蓝牙连接
		uni.closeBLEConnection({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
		})
	},
	// 销毁蓝牙模块
	destroy() {
		bluetoothCore.isInit = false;
		// 取消搜索监听
		uni.offBluetoothDeviceFound();

		// 取消蓝牙连接状态的监听
		uni.offBLEConnectionStateChange();

		// 取消蓝牙适配器变化的监听
		uni.offBluetoothAdapterStateChange();

		// 取消MTU的监听
		uni.offBLEMTUChange();

		// 取消特征值变化的监听
		uni.offBLECharacteristicValueChange()
		//关闭蓝牙连接
		bluetoothCore.closeBLEConnection();

		//关闭蓝牙模块
		uni.closeBluetoothAdapter()

	}
}


export default bluetoothCore;


二、工具代码

tool.js

const iconv = require('iconv-lite');
//十六进制转字符串
export const hexToStr = function(hex, encoding) {
	var trimedStr = hex.trim();
	var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
	var len = rawStr.length;

	var curCharCode;
	var resultStr = [];
	for (var i = 0; i < len; i = i + 2) {
		curCharCode = parseInt(rawStr.substr(i, 2), 16);
		resultStr.push(curCharCode);
	}
	var val = "";
	var arr = resultStr;
	for (let i = 0; i < arr.length; i++) {
		val += String.fromCharCode(arr[i]);
	}
	return val;
}


//CRC16计算函数
export const calculateCRC16 = (data) => {
	// 初始化CRC16校验值
	let crc = 0xFFFF;

	// 遍历输入数据
	for (let i = 0; i < data.length; i++) {
		crc ^= (data.charCodeAt(i) & 0xFF);

		for (let j = 0; j < 8; j++) {
			if ((crc & 0x0001) !== 0) {
				crc >>= 1;
				crc ^= 0xA001;
			} else {
				crc >>= 1;
			}
		}
	}

	// 返回计算后的CRC16校验值
	return crc.toString(16).toUpperCase();
}

//十进制转十六进制
export const tenToHex = (num, digit = 4) => {
	const hex = num.toString(16);
	return hex.padStart(digit, 0);
}

export const showModal = (content, sureCallback = function() {}, showCancel = false,
	confirmText =
	"确定", cancelText = "取消", cancelCallback = function() {}) => {
	uni.showModal({
		title: '提示',
		content: content,
		showCancel: showCancel,
		confirmText: confirmText,
		cancelText: cancelText,
		success(res) {
			if (res.confirm) {
				sureCallback();
			} else if (res.cancel) {
				cancelCallback();
			}
		}
	});
}

//字符串转16进制
export const stringToHex = (str) =>{
	var val = "";
	for (var i = 0; i < str.length; i++) {
		if (val == "") {
			val = str.charCodeAt(i).toString(16); //获取字符的Unicode码然后转16进制
		} else {
			val += str.charCodeAt(i).toString(16); //获取字符的Unicode码然后转16进制再拼接,中间用逗号隔开
		}
	}
	return val;
}

export const sleep = (time) => {
	return new Promise(resolve => setTimeout(resolve, time));
}

//根据name过滤数据
export const uniqueArr = (arr) => {
	// 过滤掉重复的设备名称
	let uniqueArr = arr.filter((device, index, array) => {
		return array.findIndex(d => d.name === device.name) === index;
	})
	return uniqueArr;
}

export const arrayBufferToHexString = (buffer) => {
	const hexArr = Array.prototype.map.call(
		new Uint8Array(buffer),
		function(bit) {
			return ('00' + bit.toString(16)).slice(-2)
		}
	)
	return hexArr.join('').toUpperCase();
}

export let hexStringToArrayBuffer = (str) => {
	//十六进制转ArrayBuffer
	return new Uint8Array(str.match(/[\da-f]{2}/gi).map(function(h) {
		return parseInt(h, 16)
	})).buffer
}

//匹配版本号
export let compareVersion = (version1, version2) => {
	const v1 = version1.split('.').map(Number);
	const v2 = version2.split('.').map(Number);

	const len = Math.max(v1.length, v2.length);

	for (let i = 0; i < len; i++) {
		const num1 = i < v1.length ? v1[i] : 0;
		const num2 = i < v2.length ? v2[i] : 0;

		if (num1 > num2) {
			return 1;
		} else if (num1 < num2) {
			return -1;
		}
	}

	return 0;
}

//16进制转GB2312编码,可以转为汉字,编码可自己切换
 export let hexToGb2312 = (hexString)=> {
  const buffer = Buffer.from(hexString, 'hex');
  const decodedString = iconv.decode(buffer, 'GB2312');
  return decodedString;
}

三、通讯逻辑代码

util.js

import vm from '@/main.js'

import {
	tenToHex,
	calculateCRC16,
	hexToStr,
	hexToGb2312
} from './tool.js'
let packetNo = 0;

//根据状态的自定义描述
export let getBluetoothStatusDesc = (meterEcuid)=> {
	return {
		'000': '请靠近设备',
		'001': meterEcuid,
		'002': meterEcuid,
		'003': meterEcuid,
		'004': meterEcuid,
		'005': '搜索失败',
		'006': meterEcuid,
		"-1": meterEcuid,
		"0": "正常",
		"10000": "手机蓝牙未打开",
		"10001": "手机蓝牙未打开",
		"10002": meterEcuid,
		"10003": meterEcuid,
		"10004": meterEcuid,
		"10005": meterEcuid,
		"10006": meterEcuid,
		"10007": meterEcuid,
		"10008": meterEcuid,
		"10009": meterEcuid,
		"10012": meterEcuid,
		"10013": meterEcuid
	}
}

//根据状态的图片展示
export let bluetoothStatusLoadingImagesIndex = {
		'000': 0,//'正在搜索蓝牙设备',
		'001': 0,//'正在连接',
		'002': 1,//'连接成功',
		'003': 3,//'正在通讯',
		'004':1,// '通讯完成',
		'005':1,// '请检查设备是否开启蓝牙!',
		'006':2,// '蓝牙已断开',
		"-1": 2,//"已经连接",
		"0":0, //"正常",
		"10000": 2,//"开启蓝牙后,才可连接设备",
		"10001":2, //"当前蓝牙适配器不可用",
		"10002":2,// "没有找到指定设备",
		"10003":2,// "连接失败",
		"10004": 2,//"没有找到指定服务",
		"10005":2,// "没有找到指定特征值",
		"10006":2,// "当前连接已断开",
		"10007":2, //"当前特征值不支持此操作",
		"10008":2,// "其余所有系统上报的异常",
		"10009": 2,//"Android 系统特有,系统版本低于 4.3 不支持 BLE",
		"10012": 2,//"操作超时",
		"10013": 2,//"连接 deviceId 为空或者是格式不正确"
}

//获取下发的基本的报文信息
const getBaseCommandPacket = (deviceType, deviceNo, data) => {
	packetNo = packetNo + 1;
	let startHex = 'FAC5'; //帧头
	packetNo = packetNo > 32767 ? 1 : packetNo;
	let packetNoHex = tenToHex(packetNo); //数据包编号 //最大1-32767
	let deviceTypeHex = deviceType == 1 ? 'FF' : 'FF'; //设备类型 FF 表示小程序,01表示无限远传摄像水表
	let deviceNoBcd = deviceNo; //设备编号
	let dataLenHex = tenToHex(data.length / 2); //数据域长度 算长度-转16进制-补0成2位字节
	let dataHex = data; //数据域
	let validData = startHex + packetNoHex + deviceTypeHex + deviceNoBcd + dataLenHex + dataHex;
	//let CRC16Hex = calculateCRC16(validData); //校验
	let CRC16Hex = 'FFFF'; //校验
	let endHex = 'FBC6'; //帧尾
	let resultHex = validData + CRC16Hex + endHex

	return resultHex;
}


/*

*/

//获取数据域的报文
export const getDataCommandPacket = (deviceType = 1, deviceNo, commandCode, params) => {
	let data = '';
	if (commandCode == 'read-up-report-cycle') { //读上报周期
		data = '0604';
	} else if (commandCode == 'take-a-picture') { //拍照
		data = '10' + tenToHex(20, 2) + tenToHex(0, 2) + tenToHex(0) + tenToHex(240, 2) + tenToHex(320) + tenToHex(
			1, 2) + tenToHex(0, 2);
	} else if (commandCode == 'get-picture-packetnumber') { //获取照片数据包数
		data = '11';
	} else if (commandCode == 'get-picture-data') { //获取照片数据,需要传递参数
		data = '12' + tenToHex(params.picturePacketCount, 2) + tenToHex(params.picturePacketNo, 2);
	} else if (commandCode == 'open-debug') { //打开调试
		data = '4355';
	} else if (commandCode == 'start-network') { //开始联网
		data = '40';
	} else if (commandCode == 'stop-network') { //结束联网
		data = '41';
	} else if (commandCode == 'start-read-meterAmount') { //开始抄表
		data = '42';
	} else if (commandCode == 'close-debug') { //关闭调试
		data = '43AA';
	} else if (commandCode == 'read-meter') { //获取设备信息
		data = '01';
	} else if (commandCode == 'read-meter-domain-port') { //读域名和端口
		data = '0602';
	} else if (commandCode == 'set-meter-domain-port') { // 设置域名和端口 47.115.13.97: 24226
		data = '0502' + stringToHex(params.domainPort);
	}
	return getBaseCommandPacket(deviceType, deviceNo, data)
}

//解析报文工具方法
export const parsePacketMethod = (value) => {
	let len = value.length;
	let result = '';
	if (true) { //数据包编号和校验数据
		result = value.substring(24, len - 8);
		console.log('数据域的结果', result)
		return result;
	} else {
		uni.showModal({
			content: '校验不通过',
		});
		return null
	}
}

//不同命令的不同交互显示
const messageList = {
	'take-a-picture': '正在拍照',
	'get-picture-packetnumber': '获取图片数据',
	'get-picture-data': '获取图片数据',
	'open-debug': '打开调试',
	'close-debug': '关闭调试',
	'start-network': '开始联网',
	'stop-network': '结束联网',
	'start-read-meterAmount': '开始抄表'
}
let setSetTip = () => {
	// webview的堆栈
	const pages = getCurrentPages()
	// 当前页面
	const page = pages[pages.length - 1]; 
	console.log('当前页面',page);
	let fullPath = page.$page.fullPath;
	if (fullPath.indexOf('meterDetail') != -1) {
		return {
			'004': false
		};
	} else {
		return {}
	}
}

//下发命令 表号、命令编码,命名接收完毕后的回调,下发指令的参数,执行蓝牙的方法
export const sendCommand = (userOption) => {
	let defaultOption = {
		meterEcuid: '', //表号
		commandCode: '', //命令编码
		parsePacketCallback: (result) => {}, //命名接收完毕后的回调
		commandParams: {}, //下发指令的参数
		bluetoothConnectedCallback:(connected)=>{},
		errorCallback:()=>{},
		communicationStatus:0, // 通讯状态
		operateBluetooth: (params) => {} //执行蓝牙的方法
	}
	let option = Object.assign({}, defaultOption, userOption)
	console.log('执行的命令-', option.commandCode);
	console.log('命令执行参数', option);
	let sendPacket = getDataCommandPacket(3, option.meterEcuid, option.commandCode, option.commandParams);
	console.log('生成的报文', sendPacket)
	let params = {
		planConnectBluetoothDevice: {
			meterEcuid: option.meterEcuid
		},
		respondPacket: '',
		isImmediateConnect: true,
		isImmediateWrite: true,
		sendPacket: sendPacket,
		isErrorShowodal: false,
		setTip: setSetTip(),
		bluetoothConnectedCallback:(connected)=>{
			option.bluetoothConnectedCallback(connected)
		},
		errorCallback:()=>{
			option.errorCallback()
		},
		communicationStatus:0, // 通讯状态
		bluetoothStatus: {
			'003': messageList[option.commandCode] ? messageList[option.commandCode] : '正在通讯'
		},
		parsePacketCallback: (result) => {
			option.parsePacketCallback(result)
		},
	}; //初始化的参数
	option.operateBluetooth(params);
}

//解析日志报文
export const parseLogPacketMethod = (val) => {
	let result = hexToGb2312(val) + '\n';
	console.log('打开调试后解析的字符串', result);
	getApp().globalData.logAllText += result; //处理所有日志
	if (result.indexOf('LogStart') != -1) {
		getApp().globalData.isStartLog = true;
	} else if (result.indexOf('LogEnd') != -1) {
		getApp().globalData.isStartLog = false;
	}
	if (getApp().globalData.isStartLog && result.indexOf('LogStart') == -1 && result.indexOf(
			'LogEnd') == -1) { //处理中文展示
		let logChina = vm.$store.state.logChina;
		logChina += result;
		vm.$store.commit('setLogChina', logChina)
	}


}

四、全局注册

main.js文章来源地址https://www.toymoban.com/news/detail-843670.html

//默认蓝牙设备数据
Object.assign(bluetoothCore,{
	filterPattern: /^Y3[567].*/, //Y35主机、Y36分机、Y37一体式
	filterProp: 'meterEcuid',
	STARTPACKET:'FAC5',
	ENDPACKET:'FBC6',
	bluetoothDevicesDiscoveryTimeout: 5000, //多少秒后停止搜索蓝牙
	parsePacketMethod:parsePacketMethod,
	parseLogPacketMethod:parseLogPacketMethod,
	SERVICEID: "0000FFC0-0000-xxxx-8000-00805F9B34FB",
	NOTIFYID: "0000FFC1-0000-xxxx-00805F9B34FB",
	WRITEID: "0000FFC2-0000-xxxx-8000-00805F9B34FB",
})

五、使用

sendCommandFun('open-debug')
//像蓝牙发送指令
			sendCommandFun(commandCode) {
				this.commandCode = commandCode;
				//this.setTitle(commandCode);
				//如果蓝牙未连接,则弹出连接状态弹窗 并且调试未开启
				if (!bluetoothCore.isConnected) {
					this.isShowModal = true;
				}
				sendCommand({
					meterEcuid: this.meterEcuid,
					commandCode: commandCode,
					bluetoothConnectedCallback: (connected) => { //监听到蓝牙连接上,则关闭弹窗
						console.log('蓝牙连接上的回调', connected)
						if (connected) {
							this.isShowModal = false;
						}
					},
					operateBluetooth: (params) => {
						bluetoothCore.operateBluetooth(params)
					},
					errorCallback: () => {
						if ((commandCode == 'open-debug' || commandCode == 'close-debug') && bluetoothCore
							.communicationStatus == -1) {
							this.$store.commit('setIsStartLog', !this.isStartLog)
						}
					}
				})
			},

到了这里,关于uni-app 微信小程序蓝牙模块的解耦封装-持续更新的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • uni-app 微信小程序 激励视频广告

    封装激励视频-Ad.js 调用上面写的方法:

    2024年02月12日
    浏览(11)
  • uni-app微信小程序使用echarts

    前言:本来是使用的ucharts,但因为无法监听图例点击交互,满足不了需求,所以只能放弃。 首先,下载echart组件。可以先随便建个文件夹,然后 npm init。接着下载依赖 然后找到 node_modulesmpvue-echarts下的文件,如图 只留下src,其他的删掉(没有用到)。然后复制 mpvue-echart

    2024年02月10日
    浏览(11)
  • 【uni-app微信小程序】实现支付功能

    2024年02月13日
    浏览(12)
  • uni-app(微信小程序)获取当前位置uni.getLocation

     1、微信公众平台  开发  开发管理   2、开通之后到项目文件    3、下载腾讯地图插件并引入到文件中    

    2024年02月11日
    浏览(14)
  • [uni-app] 微信小程序 如何修改替换头像

     如下图所示,微信小程序中涉及到修改头像的交互 技术: 前端应用框架为uni-app UI框架为uView  思考: 1. 头像点击事件 click 2.从本地相册选择图片或使用相机拍照 uni.chooseImage(OBJECT) ,方法执行成功后根据success中返回的图片的本地文件路径列表 tempFilePaths,做操作 3.上传图片

    2024年02月11日
    浏览(15)
  • 微信小程序修改原生组件样式(uni-app)

    微信小程序修改原生组件样式 全局修改,直接将修改的样式写在全局的样式文件中; 特殊情况:修改swiper指示点样式时,需要包裹在swiper的样式选择器下才生效。 直接将下列代码放在全局样式中是不会生效的,需要加上swiper组件的元素选择器或swiper组件的其他样式名也可。

    2024年02月05日
    浏览(16)
  • [uni-app]设置运行到微信小程序

    1、设置微信小程序开发工具路径 2、检查微信小程序开发工具是否开启了服务端口 服务端口要是没有开启,会报 × initialize。 3、在uni-app开发工具中点击运行微信开发者工具,微信开发工具运行成功。

    2024年02月13日
    浏览(16)
  • uni-app微信小程序如何渲染markdown

    在开发个人网站微信登录平台易登微信小程序的时候,由于说明文档是用markdown格式来书写的,在网页上有各种markdown个人渲染引擎,比如这个markdown编辑器无敌了!。 但是在小程序上还是第一次渲染markdown,找了各种方案,但处处是坑,除此之外最后渲染出来的效果也惨不忍

    2024年02月16日
    浏览(9)
  • uni-app嵌入微信小程序原生代码

    使用uni-app有时需要用到微信小程序原生代码 解析: uni-app项目结构跟原生小程序的项目结构有着不一致的区别,如果说开发过程中必须要使用原生代码,就需要把原生代码作为组件的方式在uni-app项目中引入使用 官网为了应对这一个需求,就给出了以下方法,供开发者实现 wxcompone

    2024年02月05日
    浏览(13)
  • uni-app 微信小程序自定义导航栏

    上面的导航栏主要由状态栏(就是手机电量显示栏)和小程序的导航栏组成,android手机一般为48px,ios手机一般为44px 1、设置navigationStyle:custom 2、页面导航栏div 3、获取statusBarHeight高度 4、获取navTitleHeight的高度

    2024年02月14日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包