<template>
|
<view>
|
<uv-navbar title="添加设备" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="#3B74EE"
|
titleStyle="color:#fff"></uv-navbar>
|
|
<view class="page_bg" v-if="searchIng">
|
<view class="searching_ico">
|
<image src="/static/img/ico_1.png" mode="widthFix"></image>
|
</view>
|
|
<view class="searching_text" v-if="isBluetooth && isLocation">
|
<view class="name">
|
搜索中
|
</view>
|
<view class="text">
|
持续自动搜索中...
|
</view>
|
</view>
|
|
<view class="searching_text" v-if="!isBluetooth">
|
<view class="name">
|
蓝牙未开启
|
</view>
|
<view class="text">
|
请手动开启系统蓝牙
|
</view>
|
</view>
|
|
<view class="searching_text" v-if="!isLocation">
|
<view class="name">
|
定位服务未开启
|
</view>
|
<view class="text">
|
请手动开启系统定位服务
|
</view>
|
</view>
|
|
</view>
|
|
<view class="bluetooth" v-if="!searchIng">
|
<view class="bluetooth_title">
|
<image class="ico" src="/static/img/ico_2.png" mode="widthFix"></image>
|
<text>已找到设备</text>
|
</view>
|
<view class="bluetooth_list">
|
<view class="item" v-for="(item,index) in bluetoothList" :key="index">
|
<image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
|
<view class="info">
|
<view class="name">
|
{{item.name}}
|
</view>
|
<view class="btn" v-if="!isLink">
|
<uv-button type="primary" shape="circle" text="连接" size="small"
|
@click="createBLEConnection(item)"></uv-button>
|
</view>
|
<!-- <view class="btn" v-if="!isLink">
|
<uv-button type="primary" shape="circle" text="连接" size="small"
|
@click="createBLEConnection(item.deviceId)"></uv-button>
|
</view> -->
|
<!-- <view class="btn">
|
<uv-button type="primary" shape="circle" text="LIST" size="small"
|
@click="commandLIST()"></uv-button>
|
</view> -->
|
<!-- <view class="btn">
|
<uv-button type="primary" shape="circle" text="播放LED" size="small"
|
@click="this.$refs.popupLED.open();"></uv-button>
|
</view> -->
|
<view class="btn" v-if="isLink">
|
<uv-button type="primary" shape="circle" text="发送LED" size="small"
|
@click="this.$refs.popupTemplete.open();"></uv-button>
|
</view>
|
<!-- <view class="btn">
|
<uv-button type="primary" shape="circle" text="获取画板数据" size="small"
|
@click="getDrawData()"></uv-button>
|
</view> -->
|
<!-- <view class="btn">
|
<uv-button type="primary" shape="circle" text="删除全部" size="small"
|
@click="commandDALL()"></uv-button>
|
</view> -->
|
<!-- <view class="btn" v-if="isLink">
|
<uv-button type="primary" shape="circle" text="调节亮度" size="small"
|
@click="this.$refs.popupBRIG.open();"></uv-button>
|
</view> -->
|
|
</view>
|
</view>
|
</view>
|
</view>
|
|
|
<!-- 设置密码弹出框 -->
|
<uv-popup ref="popupPwd" @change="changePopupPwd" mode="center" round="30rpx" :closeable="false"
|
:closeOnClickOverlay="false">
|
<view class="popup_pwd">
|
<view class="title">请输入设备绑定的密码</view>
|
<view class="cont">
|
<uv-input v-model="password" type="number" maxlength="4" placeholder="请输入4位数密码" />
|
</view>
|
<view class="btns">
|
<uv-button type="primary" :disabled="password.length !== 4" @click="confirmPwd">确定</uv-button>
|
</view>
|
</view>
|
</uv-popup>
|
|
<!-- 设置名称出框 -->
|
<uv-popup ref="popupName" @change="changePopupName" mode="center" round="30rpx" :closeable="false"
|
:closeOnClickOverlay="false">
|
<view class="popup_pwd">
|
<view class="title">请输入设备名称</view>
|
<view class="cont">
|
<uv-input v-model="deviceName" placeholder="请输入设备名称" />
|
</view>
|
<view class="btns">
|
<uv-button type="primary" :disabled="deviceName == ''" @click="confirmName">确定</uv-button>
|
</view>
|
</view>
|
</uv-popup>
|
|
<!-- 播放设备中文件列表 -->
|
<uv-popup ref="popupLED" mode="center" round="30rpx">
|
<view class="popup_pwd">
|
<view class="title">发送LED</view>
|
<view class="cont cont_led">
|
<view class="led_item" v-for="(item,index) in fileNames" :key="index">
|
<view class="name">
|
{{item.fileName}}
|
</view>
|
<!-- <view class="name">
|
{{item.nameHex}}
|
</view> -->
|
<uv-button type="primary" @click="commandDELL(item.nameHex)">删除</uv-button>
|
<uv-button type="primary" @click="commandPLAY(item.nameHex)">播放</uv-button>
|
</view>
|
</view>
|
<view class="btns">
|
<uv-button type="primary" plain @click="this.$refs.popupLED.close();">关闭</uv-button>
|
</view>
|
</view>
|
</uv-popup>
|
|
<!-- 调节亮度 -->
|
<uv-popup ref="popupBRIG" mode="center" round="30rpx">
|
<view class="popup_pwd">
|
<view class="title">调节亮度</view>
|
<view class="cont">
|
<uv-slider v-model="brightness" :min="0" :max="9" :step="1" block-size="24" block-color="#2979ff"
|
@changing="changeBrightness"></uv-slider>
|
</view>
|
<view class="btns_two">
|
<view class="btn2">
|
<uv-button type="primary" plain @click="this.$refs.popupBRIG.close();">关闭</uv-button>
|
</view>
|
<view class="btn2">
|
<uv-button type="primary" @click="commandBRIG()">确定</uv-button>
|
</view>
|
</view>
|
</view>
|
</uv-popup>
|
|
<!-- 模版列表 -->
|
<uv-popup ref="popupTemplete" mode="center" round="30rpx">
|
<view class="popup_pwd">
|
<view class="title">发送LED</view>
|
<view class="cont cont_led">
|
<view class="led_item" v-for="(item,index) in templeteData" :key="index">
|
<view class="name">
|
{{item.name}}
|
</view>
|
<view class="name">
|
{{item.screen}}
|
</view>
|
<uv-button type="primary" shape="circle" size="small"
|
@click="commandSendTemplete(item)">播放</uv-button>
|
</view>
|
</view>
|
<view class="btns">
|
<uv-button type="primary" plain @click="this.$refs.popupTemplete.close();">关闭</uv-button>
|
</view>
|
</view>
|
</uv-popup>
|
|
</view>
|
</template>
|
|
<script>
|
import {
|
equipmentAdd,
|
templeteList
|
} from '@/api/api.js'
|
export default {
|
data() {
|
return {
|
searchIng: true,
|
bluetoothList: [], //搜索到的蓝牙设备列表
|
deviceId: '', //设备id
|
serviceId: '', //服务uuid
|
characteristicId1: '', //特征uuid
|
characteristicId2: '',
|
characteristicId3: '',
|
showPwdPopup: false,
|
password: '', //设置的密码
|
checId: '', //智能壳的ID
|
batchNumber: '', //生产顺序号
|
fileNames: [], //文件名称列表
|
brightness: 3, //亮度
|
nameIndex: 1,
|
name: '',
|
|
ledName: '', // 名称
|
screen: '',
|
ledTotal: 3, // 总帧数
|
ledCurrent: 0, // 当前帧数
|
ledData: [],
|
isLink: false,
|
deviceName: '',
|
versions: '',
|
isBluetooth: true,
|
lastBluetooth: null,
|
isLocation: true,
|
platform: ''
|
}
|
},
|
onLoad() {
|
console.log('onLoad-----')
|
|
},
|
onShow() {
|
const systemInfo = uni.getSystemInfoSync();
|
if (systemInfo.platform === 'ios') {
|
// iOS 设备
|
this.platform = 'ios'
|
} else if (systemInfo.platform === 'android') {
|
// Android 设备
|
this.platform = 'android'
|
}
|
console.log('onShow-----')
|
this.openBluetooth()
|
this.getTempleteList()
|
//this.checkBluetoothEnabled()
|
},
|
onHide() {
|
console.log('onHide----aaa-')
|
uni.$off('BLECharacteristic')
|
this.closeBLEConnection()
|
this.closeBluetooth()
|
this.stopBluetoothDevices()
|
},
|
onUnload() {
|
uni.$off('BLECharacteristic')
|
console.log('onUnload-----aaa')
|
this.closeBLEConnection()
|
this.closeBluetooth()
|
this.stopBluetoothDevices()
|
},
|
methods: {
|
// 获取模版列表
|
getTempleteList() {
|
templeteList({
|
userId: uni.getStorageSync('userId'),
|
pageNum: 1,
|
pageSize: 10
|
}).then(res => {
|
//console.log('模版列表-------', res.rows)
|
this.templeteData = res.rows
|
})
|
},
|
// 添加设备
|
postEquipmentAdd() {
|
|
//console.log('this.checId---', this.checId)
|
equipmentAdd({
|
userId: uni.getStorageSync('userId'),
|
code: this.platform,
|
password: this.password,
|
name: this.deviceName, //设备名称
|
checId: this.checId, //智能壳的ID
|
deviceId: this.deviceId, //设备id
|
serviceId: this.serviceId, //服务id
|
characteristicId1: this.characteristicId1, //特征id
|
characteristicId2: this.characteristicId2,
|
characteristicId3: this.characteristicId3,
|
versions: this.versions
|
}).then(res => {
|
console.log(res)
|
uni.showToast({
|
title: res.msg,
|
duration: 2000,
|
icon: 'none'
|
});
|
if (res.code == 200) {
|
this.closeBLEConnection()
|
setTimeout(() => {
|
uni.switchTab({
|
url: '/pages/index/index'
|
});
|
}, 300);
|
}
|
})
|
},
|
changeBrightness(value) {
|
//console.log('changeBrightness----', value)
|
//this.brightness = value
|
},
|
// 设置名称
|
confirmName() {
|
this.$refs.popupName.close();
|
this.commandQVER()
|
uni.showLoading({
|
title: '添加中...',
|
mask: true
|
});
|
//this.postEquipmentAdd()
|
},
|
changePopupName(e) {
|
console.log(e)
|
},
|
changePopupPwd(e) {
|
console.log(e.show)
|
if (e.show) {
|
this.password = ''
|
} else {
|
//this.closeBLEConnection()
|
}
|
},
|
// 初始化蓝牙
|
openBluetooth() {
|
uni.openBluetoothAdapter({
|
success: (res) => {
|
console.log('蓝牙初始化成功');
|
// uni.onBluetoothAdapterStateChange(function (res) {
|
// console.log('监听蓝牙适配器状态变化事件------', res)
|
// })
|
this.isBluetooth = true
|
this.startBluetoothDevices()
|
},
|
fail: (err) => {
|
//console.log('蓝牙初始化失败----', err);
|
if (err.code == 10001) {
|
console.log('蓝牙开关未开启----', err);
|
this.isBluetooth = false
|
|
this.lastBluetooth = false
|
uni.onBluetoothAdapterStateChange(res => {
|
console.log('蓝牙状态变化:', res.available)
|
|
// 仅当状态从 false 变成 true 时执行一次
|
if (this.lastBluetooth == false && res.available == true) {
|
console.log('蓝牙开启,执行 openBluetooth()')
|
this.openBluetooth()
|
}
|
this.lastBluetooth = res.available
|
this.isBluetooth = res.available
|
})
|
}
|
},
|
complete: () => {
|
|
}
|
});
|
},
|
// 关闭蓝牙
|
closeBluetooth() {
|
uni.closeBluetoothAdapter({
|
success: (res) => {
|
console.log(res)
|
}
|
})
|
},
|
//开始搜寻附近的蓝牙外围设备
|
startBluetoothDevices() {
|
const that = this
|
uni.startBluetoothDevicesDiscovery({
|
success: (res) => {
|
console.log('开始扫描设备', res);
|
that.onBluetoothDeviceFound();
|
},
|
fail: (err) => {
|
console.log('扫描失败', err);
|
if(err.code == 10016){
|
//that.isLocation = false
|
const timerLocation = setInterval(function() {
|
const systemSetting = uni.getSystemSetting()
|
that.isLocation = systemSetting.locationEnabled
|
console.log("执行一次",this.isLocation,systemSetting.locationEnabled);
|
if(that.isLocation){
|
clearInterval(timerLocation);
|
that.openBluetooth()
|
}
|
}, 500);
|
}
|
}
|
});
|
},
|
// 停止搜寻附近的蓝牙外围设备
|
stopBluetoothDevices() {
|
uni.stopBluetoothDevicesDiscovery({
|
success: (res) => {
|
console.log(res)
|
},
|
fail: (err) => {
|
console.log(err)
|
}
|
});
|
},
|
// 监听寻找到新设备的事件
|
onBluetoothDeviceFound() {
|
uni.onBluetoothDeviceFound(res => {
|
//console.log('找到新设备', res);
|
res.devices.forEach((device) => {
|
if (device.name.includes('CH9140')) {
|
this.bluetoothList.push(device);
|
console.log('找到新设备----', this.bluetoothList);
|
this.stopBluetoothDevices()
|
setTimeout(() => {
|
this.searchIng = false
|
}, 500);
|
}
|
});
|
});
|
},
|
// 连接低功耗蓝牙设备
|
createBLEConnection(item) {
|
console.log('连接de蓝牙设备--------', item)
|
const that = this
|
uni.showLoading({
|
title: '连接中...',
|
mask: true
|
});
|
that.name = item.name
|
that.deviceId = item.deviceId
|
uni.createBLEConnection({
|
deviceId: that.deviceId,
|
success: (res) => {
|
console.log('连接低功耗蓝牙设备', res);
|
setTimeout(() => {
|
//that.setBLEMTU()
|
that.getServices();
|
}, 500);
|
},
|
fail: (err) => {
|
console.log('连接失败', err);
|
this.closeBLEConnection()
|
uni.hideLoading();
|
uni.showToast({
|
title: '连接失败',
|
icon: 'none',
|
mask: true
|
});
|
}
|
});
|
},
|
// 断开与低功耗蓝牙设备的连接
|
closeBLEConnection() {
|
uni.closeBLEConnection({
|
deviceId: this.deviceId,
|
success(res) {
|
console.log(res)
|
}
|
})
|
},
|
setBLEMTU() {
|
uni.setBLEMTU({
|
deviceId: this.deviceId,
|
mtu: 80, // 最大值一般 512,蓝牙 4.2 通常支持 247
|
success: (res) => {
|
console.log('设置MTU成功:', res);
|
},
|
fail: (err) => {
|
console.error('设置MTU失败:', err);
|
}
|
});
|
},
|
// 获取蓝牙设备所有服务
|
getServices() {
|
console.log(this.deviceId)
|
uni.getBLEDeviceServices({
|
deviceId: this.deviceId,
|
success: (res) => {
|
console.log('服务列表成功', res);
|
this.serviceId = res.services.find(item => item.uuid.toLowerCase().includes('fff0'))?.uuid;
|
this.getCharacteristics();
|
},
|
fail: (err) => {
|
console.error('服务列表失败:', err);
|
uni.hideLoading();
|
uni.showToast({
|
title: '服务列表失败',
|
icon: 'none',
|
mask: true
|
});
|
}
|
});
|
},
|
// 获取蓝牙设备某个服务中所有特征值(characteristic)
|
getCharacteristics() {
|
uni.getBLEDeviceCharacteristics({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
success: (res) => {
|
console.log('特征列表', res);
|
this.characteristicId1 = res.characteristics[0].uuid
|
this.characteristicId2 = res.characteristics[1].uuid
|
this.characteristicId3 = res.characteristics[2].uuid
|
this.startNotify()
|
setTimeout(() => {
|
this.setBaud()
|
}, 500);
|
}
|
});
|
},
|
// 设置波特率
|
setBaud() {
|
var that = this
|
var hexStr = "06000900009600000801009F"
|
uni.writeBLECharacteristicValue({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
characteristicId: this.characteristicId3,
|
value: this.hexStringToArrayBuffer(hexStr),
|
writeType: 'write',
|
success: (res) => {
|
console.log('设置波特率成功----', res);
|
that.setBLEMTU()
|
that.commandCHEC()
|
},
|
fail: (err) => {
|
console.log('设置波特率失败----', err);
|
}
|
});
|
},
|
// 开始监听
|
startNotify() {
|
const that = this
|
uni.notifyBLECharacteristicValueChange({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
characteristicId: this.characteristicId1,
|
state: true,
|
success: () => {
|
console.log('开始监听------');
|
uni.$off('BLECharacteristic');
|
uni.$on('BLECharacteristic',function(res){
|
console.log('BLECharacteristic---------------cc', res)
|
const resData = that.ab2hex(res.value);
|
const result = that.removeStartBytes(resData)
|
const resName = that.getCommandType(result);
|
console.log("回调返回数据-------add", resData);
|
console.log('result-----------add', result)
|
console.log('resName----------add', resName)
|
uni.hideLoading();
|
if (resName == 'CHEC') {
|
that.parseBLECHECResponse(result)
|
} else if (resName == 'BIND') {
|
that.$refs.popupName.open();
|
} else if (resName == 'LIST') {
|
that.parseFileList(result)
|
} else if (resName == 'BRIG') {
|
|
} else if (resName == 'PLAY') {
|
|
} else if (resName == 'INST') {
|
// 添加动画帧
|
that.ledCurrent++
|
if (that.ledCurrent < that.ledTotal) {
|
uni.showLoading({
|
title: '发送中' + that.ledCurrent,
|
mask: true
|
});
|
that.commandINST(that.ledData[that.ledCurrent]);
|
} else if (that.ledCurrent == that.ledTotal) {
|
uni.showLoading({
|
title: '发送结束帧',
|
mask: true
|
});
|
that.commandEnd()
|
}
|
|
} else if (resName == 'DALL') {
|
|
} else if (resName == 'QVER') {
|
// 查询版本信息
|
that.versions = parseInt(result.slice(-8, -6), 16);
|
that.postEquipmentAdd()
|
} else {
|
that.isLink == false
|
uni.showToast({
|
title: '返回数据错误',
|
duration: 2000,
|
icon: 'none'
|
});
|
// 关闭蓝牙设备
|
that.closeBLEConnection()
|
}
|
})
|
//uni.onBLECharacteristicValueChange(res => {
|
// const resData = that.ab2hex(res.value);
|
// const result = that.removeStartBytes(resData)
|
// const resName = that.getCommandType(result);
|
// console.log("回调返回数据-------add", resData);
|
// console.log('result-----------add', result)
|
// console.log('resName----------add', resName)
|
// uni.hideLoading();
|
// if (resName == 'CHEC') {
|
// that.parseBLECHECResponse(result)
|
// } else if (resName == 'BIND') {
|
// that.$refs.popupName.open();
|
// } else if (resName == 'LIST') {
|
// that.parseFileList(result)
|
// } else if (resName == 'BRIG') {
|
|
// } else if (resName == 'PLAY') {
|
|
// } else if (resName == 'INST') {
|
// // 添加动画帧
|
// that.ledCurrent++
|
// if (that.ledCurrent < that.ledTotal) {
|
// uni.showLoading({
|
// title: '发送中' + that.ledCurrent,
|
// mask: true
|
// });
|
// that.commandINST(that.ledData[that.ledCurrent]);
|
// } else if (that.ledCurrent == that.ledTotal) {
|
// uni.showLoading({
|
// title: '发送结束帧',
|
// mask: true
|
// });
|
// that.commandEnd()
|
// }
|
|
// } else if (resName == 'DALL') {
|
|
// } else if (resName == 'QVER') {
|
// // 查询版本信息
|
// that.versions = parseInt(result.slice(-8, -6), 16);
|
// that.postEquipmentAdd()
|
// } else {
|
// that.isLink == false
|
// uni.showToast({
|
// title: '返回数据错误',
|
// duration: 2000,
|
// icon: 'none'
|
// });
|
// // 关闭蓝牙设备
|
// that.closeBLEConnection()
|
// }
|
//});
|
}
|
});
|
},
|
// 判断并去除10个0x55启动字节(20个hex字符)
|
removeStartBytes(hexStr) {
|
// 10个0x55 = 20个hex字符
|
const startBytes = '55'.repeat(10); // '55555555555555555555'
|
if (hexStr.startsWith(startBytes)) {
|
return hexStr.slice(20); // 去掉前20个字符
|
}
|
return hexStr;
|
},
|
// 返回指令类型 判断
|
getCommandType(hexString) {
|
// 判断是否有 返回标志 单字节。0(0x30),工作无异常;1(0x31)工作有异常
|
// 判断是否包含返回标志 30 或 31
|
// 查找30或31的位置
|
let returnFlag = '';
|
let cmd = '';
|
let packageType = hexString.slice(2, 4);
|
|
if (packageType == '41') {
|
returnFlag = hexString.slice(12, 14);
|
const before8Chars = hexString.slice(4, 12);
|
cmd = before8Chars.match(/.{2}/g)
|
.map(h => String.fromCharCode(parseInt(h, 16)))
|
.join('');
|
return cmd;
|
} else if (packageType == '43') {
|
returnFlag = hexString.slice(28, 30);
|
const before8Chars = hexString.slice(20, 28);
|
cmd = before8Chars.match(/.{2}/g)
|
.map(h => String.fromCharCode(parseInt(h, 16)))
|
.join('');
|
return cmd;
|
}
|
console.log('returnFlag------', returnFlag)
|
},
|
// 查询智能壳ID
|
// 构建指令数据
|
commandCHEC() {
|
//555555555555555555557e41434845430095977e
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x43, 0x48, 0x45, 0x43]; // "CHEC"的ASCII码
|
const reserved = 0x00; // 保留字节
|
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command,
|
reserved
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('CHEC data----AAAA--------', this.hexArrayToHexString(data))
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
// 加密绑定号 发送绑定号
|
commandBIND() {
|
console.log('checId---', this.checId)
|
// 59 5C 37 39 71 4D 56 65 5A 37 12 18 07 06 2A 18 36 1E 1E 06
|
// 7e 41 42 49 4e 44 6d 71 41 45 95 4e 61 78 9e 39 49 12 15 13 71 42 94 50 50 17 7c c9 7e
|
//帧头+0x41+ BIND +加密绑定号+CRC校验码+帧尾
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x42, 0x49, 0x4E, 0x44]; // "BIND"的ASCII码
|
const reserved = this.replaceScreenSizeInID(this.checId, this.password); // 密码
|
console.log('reserved----111', reserved)
|
const reservedId = this.encryptSmartShellID(reserved)
|
console.log('reservedid--222', reservedId)
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command,
|
...this.hexStringToArray(reservedId)
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('BIND data----BBBB--------', this.hexArrayToHexString(data))
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
//关屏指令(BLAC)
|
commandBLAC() {
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x42, 0x49, 0x4E, 0x44]; // "BLAC"的ASCII码
|
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('data----', data)
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
// 查询版本信息
|
commandQVER() {
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x51, 0x56, 0x45, 0x52]; // "QVER"的ASCII码
|
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('QVER----', data)
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
//查询智能壳已存信息序列(LIST)
|
commandLIST() {
|
//1.手机发指令:启动字节+帧头+包头段(0x43+简易绑定号+屏大小)+LIST+信息属性+CRC校验码+帧尾。
|
//2.智能壳指令:启动字节+帧头+包头段+LIST+返回标志+加密地址+文件数+[信息名1+信息名2+...+信息名n]+CRC校验码+帧尾。
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x43; // 包类型
|
// 使用新的简易绑定号生成函数
|
const simpleBindId = this.generateSimpleBindId();
|
|
// 屏大小 (2 bytes): 从ID中获取
|
const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
|
const packageHeader = [packageType, ...simpleBindId, ...screenSize];
|
const command = [0x4C, 0x49, 0x53, 0x54]; // "LIST"的ASCII码
|
const infoAttribute = 0x00; // 信息属性: 0=一般信息
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
...packageHeader,
|
...command,
|
infoAttribute
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('LIST data----', this.hexArrayToHexString(data))
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
//指定节目显示(PLAY)
|
commandPLAY(nameHex) {
|
console.log('nameHex----', nameHex, typeof nameHex)
|
//1.手机发指令:启动字节+帧头+0x41+ PLAY +信息属性+文件名+CRC校验码+帧尾。
|
//2.智能壳指令:启动字节+帧头+0x41+ PLAY +返回标志+CRC校验码+帧尾
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x50, 0x4C, 0x41, 0x59]; // "PLAY"的ASCII码
|
const infoAttribute = 0x00; // 信息属性: 0=一般信息
|
//const fileName = [0x42, 0x48] //文件名称
|
const fileName = this.hexStringToArray(nameHex)
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command,
|
infoAttribute,
|
...fileName
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('PLAY data----', data)
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
// 调节亮度
|
commandBRIG() {
|
console.log('brightness----', this.brightness)
|
//1.手机发指令:启动字节+帧头+0x41+ BRIG +亮度+CRC校验码+帧尾。
|
//2.智能壳回指令:启动字节+帧头+0x41+ BRIG +返回标志+CRC校验码+帧尾。
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x41; // 包类型
|
const command = [0x42, 0x52, 0x49, 0x47]; // "BRIG"的ASCII码
|
|
//亮度:单字节,0,1,2,3。。。ASIC码
|
const brightness = this.stringToASCIIByteArray(this.brightness.toString())
|
//const brightness = 0x32
|
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
packageType,
|
...command,
|
brightness
|
];
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('BRIG data----', data)
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
|
},
|
commandSendTemplete(item) {
|
console.log('--------------', item)
|
uni.showLoading({
|
title: '发送中...',
|
mask: true
|
});
|
const name = 'A' + this.nameIndex++
|
this.ledName = name
|
this.screen = item.screen
|
this.ledTotal = item.fpsTotal
|
this.ledCurrent = 0
|
this.ledData = JSON.parse(item.fpsData)
|
this.commandINST(this.ledData[this.ledCurrent]);
|
},
|
// 获取画板数据
|
getDrawData() {
|
uni.showLoading({
|
title: '发送中...',
|
mask: true
|
});
|
const name = 'A' + this.nameIndex++
|
this.ledName = name
|
console.log('name--------', name)
|
// uni.getStorage({
|
// key: 'pixelData',
|
// success: (res) => {
|
// this.commandINST(res.data)
|
// }
|
// });
|
this.commandINST(this.ledData[this.ledCurrent]);
|
},
|
//添加信息指令(INST)
|
commandINST(hexString) {
|
// 1.手机发指令:启动+帧头+包头段(0x43+简易绑定号+屏大小)+INST+信息属性+文件名+总帧数+当前帧*+块描述+块数据+CRC校验码+帧尾
|
//注意:当前帧如果是0xff,则表示是结束帧,后面四个字节,第一个字节(表示速度),第二个字节(播放次数,0表示一直播放),后面二个字节保留;其他数字表示表示当前是第几帧。
|
//2.智能壳指令:启动+帧头+包头段+INST+返回标志+加密地址+CRC校验码+帧尾
|
// 构建指令数据
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x43; // 包类型
|
// 使用新的简易绑定号生成函数
|
const simpleBindId = this.generateSimpleBindId();
|
|
// 屏大小 (2 bytes): 从ID中获取
|
const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
|
const packageHeader = [packageType, ...simpleBindId, ...screenSize];
|
const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
|
const infoAttribute = 0x02; // 信息属性: 0=一般信息
|
|
const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
|
//console.log('fileName----', this.ledName)
|
// 总帧数
|
const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0')
|
.toUpperCase()) //[0x01]; //this.hexStringToArray('01')
|
//console.log('totalFrame----', totalFrame)
|
// 当前帧
|
const currentFrame = this.hexStringToArray(this.ledCurrent.toString(16).padStart(2, '0')
|
.toUpperCase()) //0x00
|
|
// 块描述
|
const tblockDesc = 0x00
|
|
// 块数据
|
const blockDescription = this.hexStringToArray(hexString)
|
//const blockString = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF000000000000000000000000000000000000000000000000000000000000FF0000000000000000000000000000000000000000000000FF0000000000000000FF0000FF00000000000000000000000000000000FF0000FF000000000000000000FF0000000000FF0000FF00FF0000FF0000FF000000000000000000FF00FF0000FF000000000000000000FF00000000000000000000FF000000FF0000FF0000FF0000FF0000000000FF0000000000000000000000FF0000000000000000000000FF0000FF0000000000000000FF0000FF0000000000000000000000000000FF00FF000000000000000000000000FF00FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF0000000000000000FF0000FF000000000000000000000000FF0000000000FF0000000000000000000000000000000000000000FF0000000000000000000000000000000000FF0000000000000000000000000000000000FF00000000000000000000000000FF0000FF000000FF0000FF0000000000000000000000FF00000000000000FF0000FF000000FF00FF0000FF000000000000FF0000000000000000000000000000000000000000000000FF0000000000FF0000000000000000000000FF0000000000000000000000000000000000000000FF0000000000000000FF0000000000000000FF0000000000000000FF00000000000000FF0000FF0000FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF000000000000000000000000000000000000FF0000FF0000000000000000FF0000000000000000FF0000000000000000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
//const blockDescription = this.hexStringToArray(blockString)
|
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
...packageHeader,
|
...command,
|
infoAttribute,
|
...fileName,
|
...totalFrame,
|
...currentFrame,
|
tblockDesc,
|
...blockDescription
|
];
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log(this.ledCurrent, '----INST data----', this.hexArrayToHexString(data))
|
//const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
//this.writeBLECharacteristicValue(buffer)
|
this.sendBLEDataInChunks(
|
this.hexArrayToHexString(data)
|
)
|
}, 1000);
|
},
|
// 发送结束针
|
commandEnd() {
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x43; // 包类型
|
// 使用新的简易绑定号生成函数
|
const simpleBindId = this.generateSimpleBindId();
|
|
// 屏大小 (2 bytes): 从ID中获取
|
const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
|
const packageHeader = [packageType, ...simpleBindId, ...screenSize];
|
const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
|
const infoAttribute = 0x02; // 信息属性: 0=一般信息
|
|
const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
|
//console.log('fileName----', this.ledName)
|
// 总帧数
|
const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0')
|
.toUpperCase()) //[0x01]; //this.hexStringToArray('01')
|
//console.log('totalFrame----', totalFrame)
|
|
//结束幕:
|
const ffdata = this.hexStringToArray('ff10000000')
|
const endData = [
|
...startBytes,
|
frameHeader,
|
...packageHeader,
|
...command,
|
infoAttribute,
|
...fileName,
|
...totalFrame,
|
...ffdata
|
]
|
// 计算CRC校验码(从包头段开始计算)
|
const endcrc = this.calcCRC16(endData.slice(11));
|
|
// 添加CRC和帧尾
|
endData.push((endcrc >> 8) & 0xFF); // CRC高8位
|
endData.push(endcrc & 0xFF); // CRC低8位
|
endData.push(frameHeader); // 帧尾
|
console.log('INST endData----', this.hexArrayToHexString(endData))
|
|
const endbuffer = new Uint8Array(endData).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(endbuffer)
|
}, 1000);
|
},
|
async sendBLEDataInChunks(hexStr) {
|
const maxBytes = 60
|
const interval = 50
|
const chunks = this.splitHexData(hexStr, maxBytes)
|
|
console.log(`📦 总共需要发送 ${chunks.length} 包,每包 ${maxBytes} 字节,间隔 ${interval}ms`)
|
|
for (let i = 0; i < chunks.length; i++) {
|
const buffer = this.hexToBuffer(chunks[i])
|
try {
|
await new Promise((resolve, reject) => {
|
uni.writeBLECharacteristicValue({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
characteristicId: this.characteristicId2,
|
value: buffer,
|
success: (res) => {
|
console.log(`✅ 第 ${i + 1} 包发送成功`, res)
|
resolve()
|
},
|
fail: (err) => {
|
console.error(`❌ 第 ${i + 1} 包发送失败`, err)
|
reject(err)
|
}
|
})
|
})
|
} catch (err) {
|
console.error(`🚫 发送中断,第 ${i + 1} 包发送失败`)
|
break
|
}
|
// 延迟下一包发送
|
await new Promise(r => setTimeout(r, interval))
|
}
|
console.log("🎉 数据发送完成")
|
// setTimeout(() => {
|
// uni.showLoading({
|
// title: '发送中...',
|
// mask: true
|
// });
|
// this.commandEnd()
|
// }, 500)
|
},
|
// 删除单个
|
commandDELL(nameHex) {
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x43; // 包类型
|
// 使用新的简易绑定号生成函数
|
const simpleBindId = this.generateSimpleBindId();
|
|
// 屏大小 (2 bytes): 从ID中获取
|
const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
|
const packageHeader = [packageType, ...simpleBindId, ...screenSize];
|
const command = [0x44, 0x45, 0x4C, 0x4C]; // "DELL"的ASCII码
|
const infoAttribute = 0x00; // 信息属性: 0=一般信息
|
//const fileName = [0x42, 0x48] //文件名称
|
const fileName = this.hexStringToArray(nameHex)
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
...packageHeader,
|
...command,
|
infoAttribute,
|
...fileName
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('DELL data----', this.hexArrayToHexString(data))
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
// 删除全部
|
commandDALL() {
|
const startBytes = new Array(10).fill(0x55); // 10个启动字节
|
const frameHeader = 0x7E; // 帧头
|
const packageType = 0x43; // 包类型
|
// 使用新的简易绑定号生成函数
|
const simpleBindId = this.generateSimpleBindId();
|
|
// 屏大小 (2 bytes): 从ID中获取
|
const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
|
const packageHeader = [packageType, ...simpleBindId, ...screenSize];
|
const command = [0x44, 0x41, 0x4C, 0x4C]; // "DALL"的ASCII码
|
// 组合数据
|
const data = [
|
...startBytes,
|
frameHeader,
|
...packageHeader,
|
...command
|
];
|
|
// 计算CRC校验码(从包头段开始计算)
|
const crc = this.calcCRC16(data.slice(11));
|
|
// 添加CRC和帧尾
|
data.push((crc >> 8) & 0xFF); // CRC高8位
|
data.push(crc & 0xFF); // CRC低8位
|
data.push(frameHeader); // 帧尾
|
console.log('DALL data----', this.hexArrayToHexString(data))
|
|
const buffer = new Uint8Array(data).buffer;
|
setTimeout(() => {
|
this.writeBLECharacteristicValue(buffer)
|
}, 1000);
|
},
|
|
// 生成简易绑定号
|
generateSimpleBindId() {
|
if (!this.checId || !this.password) {
|
//console.error("生成简易绑定号失败:checId或password不可用");
|
uni.hideLoading();
|
uni.showToast({
|
title: '请先连接设备',
|
icon: 'none',
|
mask: true
|
});
|
return;
|
}
|
// 从checId中提取生产顺序号(最后4字节,即8个字符)
|
const productionNumber = this.checId.slice(-8); // 生产顺序号
|
// 生产顺序号+密码
|
const simpleBindData = productionNumber + this.password;
|
// 用加密数字加密
|
const encryptedSimpleBind = this.encryptSmartShellID(simpleBindData);
|
// 取前6字节作为简易绑定号
|
const simpleBindId = this.hexStringToArray(encryptedSimpleBind.slice(0, 12));
|
//console.log('简易绑定号生成----', simpleBindId);
|
return simpleBindId;
|
},
|
|
//向低功耗蓝牙设备特征值中写入二进制数据
|
writeBLECharacteristicValue(buffer) {
|
const that = this
|
uni.writeBLECharacteristicValue({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
characteristicId: this.characteristicId2,
|
value: buffer,
|
writeType: 'write',
|
success: (res) => {
|
console.log('指令发送成功2----', res);
|
},
|
fail: (err) => {
|
console.log('指令发送失败2----', err);
|
uni.hideLoading();
|
uni.showToast({
|
title: '指令发送失败',
|
icon: 'none',
|
mask: true
|
});
|
}
|
});
|
},
|
//向低功耗蓝牙设备特征值中写入二进制数据2222222
|
writeBLEValue(buffer) {
|
const that = this
|
uni.writeBLECharacteristicValue({
|
deviceId: this.deviceId,
|
serviceId: this.serviceId,
|
characteristicId: this.characteristicId3,
|
value: buffer,
|
writeType: 'write',
|
success: (res) => {
|
console.log('指令发送成功----3', res);
|
|
},
|
fail: (err) => {
|
console.log('指令发送失败----3', err);
|
}
|
});
|
},
|
// 将十六进制字符串转为 ArrayBuffer
|
hexToBuffer(hexStr) {
|
const typedArray = new Uint8Array(hexStr.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)))
|
return typedArray.buffer
|
},
|
|
// 将大十六进制数据切分为每段 maxBytes 大小(单位:字节)
|
splitHexData(hexStr, maxBytes = 100) {
|
const result = []
|
for (let i = 0; i < hexStr.length; i += maxBytes * 2) {
|
result.push(hexStr.slice(i, i + maxBytes * 2))
|
}
|
return result
|
},
|
|
// 获取文件名称文件数量
|
parseFileList(hexString) {
|
// 文件数占1字节(2个十六进制字符
|
const fileCountHex = hexString.slice(32, 34);
|
const fileCount = parseInt(fileCountHex, 16);
|
console.log('文件数:', fileCount);
|
|
// 获取文件名称列表
|
const fileNames = [];
|
let nameStartIndex = 34; // 文件数后开始
|
|
for (let i = 0; i < fileCount; i++) {
|
// 每个信息名占2个字节(4个十六进制字符)
|
const nameHex = hexString.slice(nameStartIndex, nameStartIndex + 4);
|
const nameBytes = nameHex.match(/.{2}/g);
|
if (nameBytes && nameBytes.length === 2) {
|
// 将两个字节转换为ASCII字符
|
const char1 = String.fromCharCode(parseInt(nameBytes[0], 16));
|
const char2 = String.fromCharCode(parseInt(nameBytes[1], 16));
|
const fileName = char1 + char2;
|
fileNames.push({
|
fileName: fileName,
|
nameHex: nameHex
|
});
|
}
|
|
nameStartIndex += 4; // 移动到下一个文件名
|
}
|
console.log('文件名称列表:', fileNames);
|
this.fileNames = fileNames
|
},
|
// 指令ASCII码转换
|
stringToASCIIByteArray(str) {
|
return str.split('').map(char => char.charCodeAt(0));
|
},
|
//16进制数组 转 16进制字符串
|
hexArrayToHexString(hexArray) {
|
return hexArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
},
|
// 16进制字符串转16进制数组
|
hexStringToArray(hexStr) {
|
if (!hexStr) return [];
|
return hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16));
|
},
|
// 16进制数据转二进数据
|
hexStringToArrayBuffer(hexStr) {
|
const bytes = new Uint8Array(hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16)));
|
return bytes.buffer;
|
},
|
// 二进数据转16进制数据
|
ab2hex(buffer) {
|
const hexArr = Array.prototype.map.call(
|
new Uint8Array(buffer),
|
function(bit) {
|
return ('00' + bit.toString(16)).slice(-2)
|
}
|
)
|
return hexArr.join('')
|
},
|
calcCRC16(data) {
|
// 创建查表
|
const table = this.buildCRC16Table();
|
let crc = 0;
|
// 计算CRC
|
for (let i = 0; i < data.length; i++) {
|
crc = ((crc << 8) & 0xFF00) ^ table[((crc >> 8) ^ data[i]) & 0xFF];
|
}
|
return crc & 0xFFFF;
|
},
|
buildCRC16Table() {
|
const table = new Array(256);
|
const poly = 0x1021; // CRC-CCITT多项式
|
for (let i = 0; i < 256; i++) {
|
let nData = i << 8;
|
let nAccum = 0;
|
for (let j = 0; j < 8; j++) {
|
if ((nData ^ nAccum) & 0x8000) {
|
nAccum = (nAccum << 1) ^ poly;
|
} else {
|
nAccum <<= 1;
|
}
|
nData <<= 1;
|
}
|
table[i] = nAccum & 0xFFFF;
|
}
|
return table;
|
},
|
|
// 设置密码
|
confirmPwd() {
|
console.log("✅ 用户密码:", this.password);
|
this.$refs.popupPwd.close();
|
this.commandBIND()
|
uni.showLoading({
|
title: '绑定中...',
|
mask: true
|
});
|
},
|
// 密码转16进制数组
|
passwordToHexArray(pwd) {
|
if (!/^\d{4}$/.test(pwd)) throw new Error('密码必须是4位数字');
|
return [
|
parseInt(pwd.slice(0, 2), 10),
|
parseInt(pwd.slice(2, 4), 10)
|
];
|
},
|
// 密码置换掉ID号中"屏的大小",加密下发
|
replaceScreenSizeInID(originalHexString, password4Digit) {
|
// 密码4个数字,每二个合起来一个字节,如3472变成 0x34 0x72
|
const high = password4Digit.slice(0, 2); // "34"
|
const low = password4Digit.slice(2, 4); // "72"
|
|
// 替换屏幕大小字段(第 10、11 个字节,即第20-23个字符)
|
const before = originalHexString.slice(0, 20);
|
const after = originalHexString.slice(24);
|
|
return before + high + low + after;
|
},
|
// 提取智能壳加密ID
|
parseBLECHECResponse(hexStr) {
|
const encryptedID = hexStr.slice(14, -6);
|
console.log('加密id-------', typeof encryptedID, encryptedID);
|
if(!encryptedID){
|
// uni.showToast({
|
// title: '请设置手电筒模式并在手电筒闪烁时点击确认',
|
// icon: 'none',
|
// mask: true,
|
// duration: 2000
|
// });
|
this.closeBLEConnection()
|
//this.openBluetooth()
|
uni.showModal({
|
title: '提示',
|
content: '请将设备设置手电筒模式并在手电筒闪烁时点击确认',
|
showCancel: false,
|
confirmText: '关闭',
|
success: function (res) {
|
if (res.confirm) {
|
console.log('用户点击确定');
|
}
|
}
|
});
|
return
|
}
|
const decryptId = this.decryptSmartShellID(encryptedID)
|
console.log('解密id-------', typeof decryptId, decryptId);
|
this.checId = decryptId
|
this.$refs.popupPwd.open();
|
},
|
// 解密函数
|
decryptSmartShellID(encryptedHex) {
|
const key = '2021101236011119680215001413714294505017';
|
const decrypted = [];
|
for (let i = 0; i < 20; i++) {
|
const byteHex = encryptedHex.substr(i * 2, 2);
|
const encryptedByte = parseInt(byteHex, 16);
|
const keyHex = key.substr(i * 2, 2)
|
const keyByte = parseInt(keyHex, 16);
|
const decryptedByte = (encryptedByte - keyByte);
|
decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
|
}
|
return decrypted.join('');
|
},
|
// 加密函数
|
encryptSmartShellID(encryptedHex) {
|
const key = '2021101236011119680215001413714294505017';
|
const decrypted = [];
|
for (let i = 0; i < 20; i++) {
|
const byteHex = encryptedHex.substr(i * 2, 2);
|
const encryptedByte = parseInt(byteHex, 16);
|
const keyHex = key.substr(i * 2, 2)
|
const keyByte = parseInt(keyHex, 16);
|
const decryptedByte = (encryptedByte + keyByte);
|
decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
|
}
|
return decrypted.join('');
|
},
|
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.btns_two {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 0 40rpx;
|
margin-top: 50rpx;
|
|
.btn2 {
|
width: 47%;
|
}
|
}
|
|
.cont_led {
|
max-height: 500rpx;
|
overflow-y: auto;
|
}
|
|
.popup_pwd {
|
width: 660rpx;
|
box-sizing: border-box;
|
padding: 0 50rpx 50rpx;
|
|
.led_item {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 10rpx 0;
|
|
.name {
|
font-size: 30rpx;
|
color: #000000;
|
line-height: 50rpx;
|
width: 100%;
|
box-sizing: border-box;
|
padding-right: 40rpx;
|
}
|
|
.btn {
|
flex-shrink: 0;
|
width: 120rpx;
|
}
|
}
|
|
.title {
|
text-align: center;
|
font-weight: 500;
|
font-size: 32rpx;
|
color: rgba(0, 0, 0, 0.9);
|
line-height: 42rpx;
|
padding: 50rpx 0 40rpx;
|
}
|
|
.btns {
|
margin-top: 40rpx;
|
}
|
}
|
|
.bluetooth {
|
padding: 20rpx 30rpx 0;
|
|
.bluetooth_list {
|
.item {
|
background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
|
box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.2), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
|
border-radius: 20rpx;
|
border: 2rpx solid #FFFFFF;
|
padding: 22rpx;
|
display: flex;
|
align-items: stretch;
|
justify-content: space-between;
|
margin-top: 24rpx;
|
|
.ph {
|
display: block;
|
flex-shrink: 0;
|
width: 230rpx;
|
height: 170rpx;
|
border-radius: 20rpx;
|
margin-right: 24rpx;
|
}
|
|
.info {
|
width: 100%;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
.name {
|
font-weight: 500;
|
font-size: 27rpx;
|
color: #000000;
|
line-height: 50rpx;
|
}
|
|
.btn {
|
width: 200rpx;
|
margin-top: 20rpx;
|
}
|
}
|
}
|
}
|
|
.bluetooth_title {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-top: 20rpx;
|
|
text {
|
display: block;
|
width: 100%;
|
font-weight: 500;
|
font-size: 34rpx;
|
color: rgba(0, 0, 0, 0.7);
|
line-height: 38rpx;
|
}
|
|
.ico {
|
flex-shrink: 0;
|
width: 40rpx;
|
margin-right: 10rpx;
|
}
|
}
|
}
|
|
.searching_text {
|
text-align: center;
|
background: url('/static/img/bg_3.png') no-repeat center top;
|
background-size: 100% auto;
|
box-sizing: border-box;
|
padding-top: 140rpx;
|
margin-top: -100rpx;
|
height: 300rpx;
|
|
.name {
|
font-weight: 500;
|
font-size: 36rpx;
|
color: #000000;
|
line-height: 34rpx;
|
}
|
|
.text {
|
font-size: 26rpx;
|
color: rgba(0, 0, 0, 0.5);
|
line-height: 34rpx;
|
margin-top: 30rpx;
|
}
|
}
|
|
.searching_ico {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding-top: 100rpx;
|
}
|
|
.page_bg {
|
width: 100%;
|
background: linear-gradient(3deg, #72E5FF 0%, #3B74EE 98%, #3B74EE 100%);
|
}
|
|
.page_bg1 {
|
width: 100%;
|
min-height: calc(100vh - var(--window-bottom));
|
background: url('/static/img/bg_2.jpg') no-repeat center top;
|
background-size: 100% auto;
|
}
|
</style>
|