国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

uni-app 微信小程序藍牙模塊的解耦封裝-持續(xù)更新

這篇具有很好參考價值的文章主要介紹了uni-app 微信小程序藍牙模塊的解耦封裝-持續(xù)更新。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、核心代碼

core.js

import {
	showModal,
	stringToHex,
	sleep,
	uniqueArr,
	arrayBufferToHexString,
	hexStringToArrayBuffer,
	compareVersion
} from './tool.js'
//buffer分包,根據(jù)MTU的長度進行分包,藍牙分發(fā)官方建議20個字節(jié)一包,但是隨著硬件的發(fā)展,可設置一次傳輸?shù)淖畲髥卧?/span>
//根據(jù)MTU的大小,把所發(fā)數(shù)據(jù)進行分包處理
const getBufferArrayByMtu = (data) => {
	let writePacketLen = bluetoothCore.mtu;
	let num = 0;
	if (typeof data == "undefined") return [];
	let allBuffer = hexStringToArrayBuffer(data); //16進制字符串轉(zhuǎn)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位數(shù)是自己定義的,4位數(shù)是藍牙功能確定的,因為頁面有交互,故定義一些字段進行交互提示
export const bluetoothStatus = {
	'000': '正在搜索藍牙設備',
	'001': '正在連接',
	'002': '連接成功',
	'003': '正在通訊',
	'004': '通訊完成',
	'005': '請檢查設備是否開啟藍牙!',
	'006': '藍牙已斷開',
	"-1": "已經(jīng)連接",
	"0": "正常",
	"10000": "開啟藍牙后,才可連接設備",
	"10001": "開啟藍牙后,才可連接設備",
	"10002": "沒有找到指定設備",
	"10003": "連接失敗",
	"10004": "沒有找到指定服務",
	"10005": "沒有找到指定特征值",
	"10006": "當前連接已斷開",
	"10007": "當前特征值不支持此操作",
	"10008": "其余所有系統(tǒng)上報的異常",
	"10009": "Android 系統(tǒng)特有,系統(tǒng)版本低于 4.3 不支持 BLE",
	"10012": "操作超時",
	"10013": "連接 deviceId 為空或者是格式不正確"
}



//核心的重要的bluetoothCore對象
let bluetoothCore = {
	bluetoothStatus: {}, //有時候需要自己定義通訊的提示文字
	errorCallback: () => {}, //通訊過程中出現(xiàn)錯誤的回調(diào)處理方法
	isErrorShowodal: true, //錯誤消息是否用showModal展示,因為有些錯誤不需要展示,或者已經(jīng)展示在了其他的地方
	tip: '', //展示在頁面上的信息,包括正式進行的提示和藍牙反饋的錯誤提示
	status: '', //需要像頁面展示的藍牙狀態(tài)
	SERVICEID: "0000FEE7-0000-2222-8000-00805F9B34FB",
	NOTIFYID: "0000FEC1-0000-2222-8000-00805F9B34FB",
	WRITEID: "0000FEC1-0000-2222-8000-00805F9B34FB",
	STARTPACKET: '54424F582C435143542C', //報文回復的開始標志
	ENDPACKET: '2C54424F58454E44', //報文回復的結束標志
	planConnectBluetoothDevice: {}, //計劃去連接的藍牙設備
	connectedBluetoothDevice: {}, //已經(jīng)連接上的藍牙設備
	bluetoothList: [], //展示的藍牙列表
	isDiscovering: true, //0為未在搜索,1為正在搜索
	isImmediateConnect: true, //是否直接連接
	isImmediateWrite: true, //是否立即寫數(shù)據(jù)
	filterProp: 'name', // 藍牙過濾的屬性名稱
	filterPattern: /^\d{10}$/, // 過濾藍牙列表的正則表達式
	isConnected: false, //設備是否連接,實際的連接狀態(tài)
	sendPacket: '', //當前發(fā)送的報文
	respondPacket: '', //監(jiān)聽到的組合報文
	isInit: false, //是否進行了初始化,只進行初始化一次就可以了
	mtu: 20,
	setTip: {}, //哪一步不需要設置提示 {'004':false}
	discoveryTimer: null, // 藍牙搜索停止的計時器
	bluetoothDevicesDiscoveryTimeout: 10000, //多少秒后停止搜索藍牙
	isStopExe: false, //藍牙斷開后,是否停止執(zhí)行
	isCancelOparate: false, //是否中途取消操作,取消操作后,在異步方法中停止執(zhí)行
	isReceivePacketFinished: false, //是否此次接報文完畢,如果接收報文完畢,其他接收的就是日志,此字段表示,在日志開始的時候,可以發(fā)送指令,指令正?;貜陀虚_始和結束標志,但是日志報文沒有
	communicationStatus: 0, //每次通訊前設置為0,通訊成功為1,通訊成功之前就失敗為-1,有可能在此次通訊中斷開了藍牙,要把有些狀態(tài)還原,比如開關狀態(tài)
	parsePacketCallback: (result) => {}, //報文解析完后的回調(diào)
	//解析報文的方式,每種藍牙設備的協(xié)議方式不一樣,故解析也不一樣
	parsePacketMethod: (value) => {

	},
	//執(zhí)行藍牙的取消操作,由于藍牙異步操作,在以下幾個重要地方做攔截操作,搜索監(jiān)聽中、下發(fā)指令中、解析指令中
	cancelOparate: () => {
		bluetoothCore.isCancelOparate = true;
		bluetoothCore.status = '000';
		bluetoothCore.tip = '正在搜索藍牙設備';
	},
	//解析日志報文不需要判斷首幀和尾幀,直接解析收到的字符串
	parseLogPacketMethod: () => {

	},
	//藍牙連接后的回調(diào)
	bluetoothConnectedCallback: (connected) => {

	},
	//下發(fā)指令,調(diào)用這個方法
	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') { //有些狀態(tài)下,不需要告知通訊完成
				bluetoothCore.tip = newBluetoothStatus[status];
			}
			//如果再調(diào)用藍牙API的時候,發(fā)生了錯誤,則看是否提示用戶
			if (isError) {
				if (bluetoothCore.isErrorShowodal) {
					showModal(bluetoothCore.tip);
				}
				//如果再通訊成功之前就失敗了,則認為此次通訊是不成功的
				if (bluetoothCore.communicationStatus == 0) {
					bluetoothCore.communicationStatus = -1;
				}
				bluetoothCore.errorCallback();
			}
			if (status == '006') { // 如果狀態(tài)等于006,則停止執(zhí)行,停止顯示后面的內(nèi)容
				bluetoothCore.isStopExe = true;
			}
		}

	},
	//獲取在藍牙模塊生效期間所有已發(fā)現(xiàn)的藍牙設備,包括已經(jīng)和本機處于連接狀態(tài)的設備
	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(`正常通訊回復的報文數(shù)據(jù)-`, 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) { // 判斷當前連接的車輛是否是現(xiàn)在的目標車輛
				if (bluetoothCore.connectedBluetoothDevice[bluetoothCore.filterProp] == bluetoothCore
					.planConnectBluetoothDevice[bluetoothCore.filterProp]) {
					//如果當前連接的設備是目標設備,則直接寫入數(shù)據(jù)
					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() {
		// 監(jiān)聽藍牙適配器的變化
		uni.onBluetoothAdapterStateChange((res) => {
			console.log('藍牙適配器狀態(tài)變化', res);
		});

		// 監(jiān)聽搜索新設備
		uni.onBluetoothDeviceFound((res) => {
			//console.log('搜索到新設備', res);
			//1、有可能展示列表,有可能直接連接
			if (bluetoothCore.isCancelOparate) return;
			bluetoothCore.bluetoothDeviceFoundCallback(res.devices);
		});

		// 監(jiān)聽藍牙連接狀態(tài)
		uni.onBLEConnectionStateChange((res) => {
			console.log('藍牙連接狀態(tài)變化', res);
			bluetoothCore.isConnected = res.connected;
			bluetoothCore.bluetoothConnectedCallback(res.connected);
			if (!res.connected) {
				bluetoothCore.setBlueTip('006', true); //藍牙已斷開
			}
		});

		// 監(jiān)聽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;
	},
	//監(jiān)聽搜索到的設備
	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]); //合并參數(shù)  
				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}創(chuàng)建連接成功`, res);
				bluetoothCore.isConnected = true;
				bluetoothCore.connectedBluetoothDevice = Object.assign({}, bluetoothCore
					.planConnectBluetoothDevice);
				//如果是安卓手機并且安卓系統(tǒng) 5.1 以上版本有效
				uni.getSystemInfo({
					success: function(res) {
						// res.platform 可以獲取手機的操作系統(tǒng)類型,如 "android", "ios"
						// res.system 可以獲取手機的操作系統(tǒng)版本,如 "Android 5.1.1"
						console.log("操作系統(tǒng)類型", res.platform);
						console.log("操作系統(tǒng)版本", res.system);
						if (res.platform === 'android' && compareVersion(res.system.split(' ')[
									1]
								.toString(), '5.1.0') >=
							0) {
							// 在安卓手機且系統(tǒng)版本大于等于 5.1.0 時執(zhí)行相應的邏輯
							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必須要執(zhí)行
	getBLEDeviceServices() {
		uni.getBLEDeviceServices({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			success(res) {
				console.log(`獲取${bluetoothCore.connectedBluetoothDevice.deviceId}服務成功`, res);
				bluetoothCore.getBLEDeviceCharacteristics();
			}
		})
	},
	//ios必須要執(zhí)行
	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('設備監(jiān)聽返回的報文', 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() {
		//防止沒有更換狀態(tài)
		uni.notifyBLECharacteristicValueChange({
			state: true,
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			serviceId: bluetoothCore.SERVICEID,
			characteristicId: bluetoothCore.NOTIFYID,
			success(res) {
				if (bluetoothCore.isImmediateWrite) {//如果notify監(jiān)聽成功,則直接寫報文
					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('寫入的數(shù)據(jù)', arrayBufferToHexString(value))
		uni.writeBLECharacteristicValue({
			deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
			serviceId: bluetoothCore.SERVICEID,
			characteristicId: bluetoothCore.WRITEID,
			value,
			success(res) {
				console.log("寫入數(shù)據(jù)成功", res);
				//判斷是否是最后一包,如果是最后一包,則開啟通訊計時器
			},
			fail(res) {
				console.log("寫入數(shù)據(jù)失敗", res);
				bluetoothCore.setBlueTip(res.errCode, true);
			}

		})
	},

	//不分包發(fā)送數(shù)據(jù)
	writeDataToDevice() {
		let allBuffer = hexStringToArrayBuffer(bluetoothCore.sendPacket);
		bluetoothCore.writeBLECharacteristicValue(allBuffer);
	},
	//分包發(fā)送數(shù)據(jù)
	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;
		// 取消搜索監(jiān)聽
		uni.offBluetoothDeviceFound();

		// 取消藍牙連接狀態(tài)的監(jiān)聽
		uni.offBLEConnectionStateChange();

		// 取消藍牙適配器變化的監(jiān)聽
		uni.offBluetoothAdapterStateChange();

		// 取消MTU的監(jiān)聽
		uni.offBLEMTUChange();

		// 取消特征值變化的監(jiān)聽
		uni.offBLECharacteristicValueChange()
		//關閉藍牙連接
		bluetoothCore.closeBLEConnection();

		//關閉藍牙模塊
		uni.closeBluetoothAdapter()

	}
}


export default bluetoothCore;


二、工具代碼

tool.js

const iconv = require('iconv-lite');
//十六進制轉(zhuǎn)字符串
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計算函數(shù)
export const calculateCRC16 = (data) => {
	// 初始化CRC16校驗值
	let crc = 0xFFFF;

	// 遍歷輸入數(shù)據(jù)
	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();
}

//十進制轉(zhuǎn)十六進制
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();
			}
		}
	});
}

//字符串轉(zhuǎn)16進制
export const stringToHex = (str) =>{
	var val = "";
	for (var i = 0; i < str.length; i++) {
		if (val == "") {
			val = str.charCodeAt(i).toString(16); //獲取字符的Unicode碼然后轉(zhuǎn)16進制
		} else {
			val += str.charCodeAt(i).toString(16); //獲取字符的Unicode碼然后轉(zhuǎn)16進制再拼接,中間用逗號隔開
		}
	}
	return val;
}

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

//根據(jù)name過濾數(shù)據(jù)
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) => {
	//十六進制轉(zhuǎn)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進制轉(zhuǎn)GB2312編碼,可以轉(zhuǎn)為漢字,編碼可自己切換
 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;

//根據(jù)狀態(tài)的自定義描述
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
	}
}

//根據(jù)狀態(tài)的圖片展示
export let bluetoothStatusLoadingImagesIndex = {
		'000': 0,//'正在搜索藍牙設備',
		'001': 0,//'正在連接',
		'002': 1,//'連接成功',
		'003': 3,//'正在通訊',
		'004':1,// '通訊完成',
		'005':1,// '請檢查設備是否開啟藍牙!',
		'006':2,// '藍牙已斷開',
		"-1": 2,//"已經(jīng)連接",
		"0":0, //"正常",
		"10000": 2,//"開啟藍牙后,才可連接設備",
		"10001":2, //"當前藍牙適配器不可用",
		"10002":2,// "沒有找到指定設備",
		"10003":2,// "連接失敗",
		"10004": 2,//"沒有找到指定服務",
		"10005":2,// "沒有找到指定特征值",
		"10006":2,// "當前連接已斷開",
		"10007":2, //"當前特征值不支持此操作",
		"10008":2,// "其余所有系統(tǒng)上報的異常",
		"10009": 2,//"Android 系統(tǒng)特有,系統(tǒng)版本低于 4.3 不支持 BLE",
		"10012": 2,//"操作超時",
		"10013": 2,//"連接 deviceId 為空或者是格式不正確"
}

//獲取下發(fā)的基本的報文信息
const getBaseCommandPacket = (deviceType, deviceNo, data) => {
	packetNo = packetNo + 1;
	let startHex = 'FAC5'; //幀頭
	packetNo = packetNo > 32767 ? 1 : packetNo;
	let packetNoHex = tenToHex(packetNo); //數(shù)據(jù)包編號 //最大1-32767
	let deviceTypeHex = deviceType == 1 ? 'FF' : 'FF'; //設備類型 FF 表示小程序,01表示無限遠傳攝像水表
	let deviceNoBcd = deviceNo; //設備編號
	let dataLenHex = tenToHex(data.length / 2); //數(shù)據(jù)域長度 算長度-轉(zhuǎn)16進制-補0成2位字節(jié)
	let dataHex = data; //數(shù)據(jù)域
	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;
}


/*

*/

//獲取數(shù)據(jù)域的報文
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') { //獲取照片數(shù)據(jù)包數(shù)
		data = '11';
	} else if (commandCode == 'get-picture-data') { //獲取照片數(shù)據(jù),需要傳遞參數(shù)
		data = '12' + tenToHex(params.picturePacketCount, 2) + tenToHex(params.picturePacketNo, 2);
	} else if (commandCode == 'open-debug') { //打開調(diào)試
		data = '4355';
	} else if (commandCode == 'start-network') { //開始聯(lián)網(wǎng)
		data = '40';
	} else if (commandCode == 'stop-network') { //結束聯(lián)網(wǎng)
		data = '41';
	} else if (commandCode == 'start-read-meterAmount') { //開始抄表
		data = '42';
	} else if (commandCode == 'close-debug') { //關閉調(diào)試
		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) { //數(shù)據(jù)包編號和校驗數(shù)據(jù)
		result = value.substring(24, len - 8);
		console.log('數(shù)據(jù)域的結果', result)
		return result;
	} else {
		uni.showModal({
			content: '校驗不通過',
		});
		return null
	}
}

//不同命令的不同交互顯示
const messageList = {
	'take-a-picture': '正在拍照',
	'get-picture-packetnumber': '獲取圖片數(shù)據(jù)',
	'get-picture-data': '獲取圖片數(shù)據(jù)',
	'open-debug': '打開調(diào)試',
	'close-debug': '關閉調(diào)試',
	'start-network': '開始聯(lián)網(wǎng)',
	'stop-network': '結束聯(lián)網(wǎng)',
	'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 {}
	}
}

//下發(fā)命令 表號、命令編碼,命名接收完畢后的回調(diào),下發(fā)指令的參數(shù),執(zhí)行藍牙的方法
export const sendCommand = (userOption) => {
	let defaultOption = {
		meterEcuid: '', //表號
		commandCode: '', //命令編碼
		parsePacketCallback: (result) => {}, //命名接收完畢后的回調(diào)
		commandParams: {}, //下發(fā)指令的參數(shù)
		bluetoothConnectedCallback:(connected)=>{},
		errorCallback:()=>{},
		communicationStatus:0, // 通訊狀態(tài)
		operateBluetooth: (params) => {} //執(zhí)行藍牙的方法
	}
	let option = Object.assign({}, defaultOption, userOption)
	console.log('執(zhí)行的命令-', option.commandCode);
	console.log('命令執(zhí)行參數(shù)', 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, // 通訊狀態(tài)
		bluetoothStatus: {
			'003': messageList[option.commandCode] ? messageList[option.commandCode] : '正在通訊'
		},
		parsePacketCallback: (result) => {
			option.parsePacketCallback(result)
		},
	}; //初始化的參數(shù)
	option.operateBluetooth(params);
}

//解析日志報文
export const parseLogPacketMethod = (val) => {
	let result = hexToGb2312(val) + '\n';
	console.log('打開調(diào)試后解析的字符串', 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文章來源地址http://www.zghlxwxcb.cn/news/detail-843670.html

//默認藍牙設備數(shù)據(jù)
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')
//像藍牙發(fā)送指令
			sendCommandFun(commandCode) {
				this.commandCode = commandCode;
				//this.setTitle(commandCode);
				//如果藍牙未連接,則彈出連接狀態(tài)彈窗 并且調(diào)試未開啟
				if (!bluetoothCore.isConnected) {
					this.isShowModal = true;
				}
				sendCommand({
					meterEcuid: this.meterEcuid,
					commandCode: commandCode,
					bluetoothConnectedCallback: (connected) => { //監(jiān)聽到藍牙連接上,則關閉彈窗
						console.log('藍牙連接上的回調(diào)', 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 微信小程序藍牙模塊的解耦封裝-持續(xù)更新的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【uni-app微信小程序】實現(xiàn)支付功能

    實現(xiàn)微信支付功能需要在小程序后臺配置支付相關信息,并且在前端代碼中調(diào)用微信支付API進行支付操作。好的, uni-app微信小程序?qū)崿F(xiàn)支付功能整體流程 大致如下: 注冊微信公眾平臺,并完成開發(fā)者資質(zhì)認證; 在微信商戶平臺注冊商戶賬號,并完成商戶資質(zhì)認證; 在商戶

    2024年02月13日
    瀏覽(114)
  • uni-app微信小程序使用echarts

    uni-app微信小程序使用echarts

    前言:本來是使用的ucharts,但因為無法監(jiān)聽圖例點擊交互,滿足不了需求,所以只能放棄。 首先,下載echart組件??梢韵入S便建個文件夾,然后 npm init。接著下載依賴 然后找到 node_modulesmpvue-echarts下的文件,如圖 只留下src,其他的刪掉(沒有用到)。然后復制 mpvue-echart

    2024年02月10日
    瀏覽(95)
  • uni-app 微信小程序 激勵視頻廣告

    封裝激勵視頻-Ad.js 調(diào)用上面寫的方法:

    2024年02月12日
    瀏覽(99)
  • uni-app(微信小程序)獲取當前位置uni.getLocation

    uni-app(微信小程序)獲取當前位置uni.getLocation

    ?1、微信公眾平臺? 開發(fā)? 開發(fā)管理? ?2、開通之后到項目文件 ? ?3、下載騰訊地圖插件并引入到文件中 ? ?

    2024年02月11日
    瀏覽(101)
  • [Uni-app] 微信小程序的圓環(huán)進度條

    [Uni-app] 微信小程序的圓環(huán)進度條

    效果圖: 組件完整代碼如下: 調(diào)用頁面:

    2024年04月29日
    瀏覽(22)
  • uni-app做微信小程序的分包處理

    uni-app做微信小程序的分包處理

    我們的都知道微信小程序有隨即隨用,用完即走的優(yōu)點,并且它開發(fā)門檻低,但是它也有一個致命的缺點,就是代碼包體積的限制,這一缺點讓小程序的開發(fā)有了一定的限制,現(xiàn)在有一方法可以減少代碼包的體積,能夠讓小程序的功能得到一定的擴展,這一方法就是——分包

    2023年04月08日
    瀏覽(90)
  • uni-app嵌入微信小程序原生代碼

    uni-app嵌入微信小程序原生代碼

    使用uni-app有時需要用到微信小程序原生代碼 解析: uni-app項目結構跟原生小程序的項目結構有著不一致的區(qū)別,如果說開發(fā)過程中必須要使用原生代碼,就需要把原生代碼作為組件的方式在uni-app項目中引入使用 官網(wǎng)為了應對這一個需求,就給出了以下方法,供開發(fā)者實現(xiàn) wxcompone

    2024年02月05日
    瀏覽(97)
  • uni-app 微信小程序自定義導航欄

    uni-app 微信小程序自定義導航欄

    上面的導航欄主要由狀態(tài)欄(就是手機電量顯示欄)和小程序的導航欄組成,android手機一般為48px,ios手機一般為44px 1、設置navigationStyle:custom 2、頁面導航欄div 3、獲取statusBarHeight高度 4、獲取navTitleHeight的高度

    2024年02月14日
    瀏覽(95)
  • 微信小程序修改原生組件樣式(uni-app)

    微信小程序修改原生組件樣式 全局修改,直接將修改的樣式寫在全局的樣式文件中; 特殊情況:修改swiper指示點樣式時,需要包裹在swiper的樣式選擇器下才生效。 直接將下列代碼放在全局樣式中是不會生效的,需要加上swiper組件的元素選擇器或swiper組件的其他樣式名也可。

    2024年02月05日
    瀏覽(104)
  • [uni-app]設置運行到微信小程序

    [uni-app]設置運行到微信小程序

    1、設置微信小程序開發(fā)工具路徑 2、檢查微信小程序開發(fā)工具是否開啟了服務端口 服務端口要是沒有開啟,會報 × initialize。 3、在uni-app開發(fā)工具中點擊運行微信開發(fā)者工具,微信開發(fā)工具運行成功。

    2024年02月13日
    瀏覽(95)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包