一、核心代碼
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文章來源:http://www.zghlxwxcb.cn/news/detail-843670.html
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)!