| | |
| | | dId: '' ,//已连接的设备id, |
| | | devKwh: '', |
| | | platform: '', |
| | | screenOn: true |
| | | screenOn: true, |
| | | waitDallThenSend: false, // 标记:收到DALL清除所有信息指令回应后,再开始发送INST帧 |
| | | instTimer: null, // INST发送超时计时器:发送INST帧后等待回应,超过5秒未收到则判定发送失败 |
| | | instSendAborted: false // 标记:INST发送已失败/中止,收到迟到的回应时忽略,不再继续发送 |
| | | } |
| | | }, |
| | | onLoad() { |
| | |
| | | console.log('--------------', item); |
| | | |
| | | uni.showLoading({ |
| | | title: '发送中...', |
| | | title: '发送中0%', |
| | | mask: true |
| | | }); |
| | | |
| | |
| | | } |
| | | |
| | | if (this.ledData.length > 0) { |
| | | this.commandINST(this.ledData[this.ledCurrent]); |
| | | // 重置发送状态:清除残留计时器、重置失败标记 |
| | | this.clearInstTimer(); |
| | | this.instSendAborted = false; |
| | | // 向智能壳发送INST帧前,先发送一次清除所有信息指令(DALL),待智能壳回应后再开始发送 |
| | | this.waitDallThenSend = true; |
| | | this.commandDALL(); |
| | | } else { |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | |
| | | var that = this |
| | | uni.setBLEMTU({ |
| | | deviceId: this.deviceId, |
| | | mtu: 80, // 最大值一般 512,蓝牙 4.2 通常支持 247 |
| | | mtu: 512, // 最大值一般 512,蓝牙 4.2 通常支持 247 |
| | | success: (res) => { |
| | | console.log('设置MTU成功:', res); |
| | | uni.hideLoading(); |
| | |
| | | }, |
| | | |
| | | //向低功耗蓝牙设备特征值中写入二进制数据 |
| | | writeBLECharacteristicValue(buffer) { |
| | | //keepLoading: 发送INST帧(含DALL前置指令、结束帧)过程中保持进度loading,发送成功后不关闭 |
| | | writeBLECharacteristicValue(buffer, keepLoading = false) { |
| | | const that = this |
| | | uni.writeBLECharacteristicValue({ |
| | | deviceId: this.deviceId, |
| | |
| | | writeType: 'write', |
| | | success: (res) => { |
| | | //console.log('写入指令发送成功----', res); |
| | | uni.hideLoading(); |
| | | if (!keepLoading) { |
| | | uni.hideLoading(); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | //console.log('写入指令发送失败----', err); |
| | |
| | | this.writeBLECharacteristicValue(buffer) |
| | | }, 500); |
| | | }, |
| | | // 清除所有信息指令(DALL) |
| | | commandDALL() { |
| | | // 1.手机发指令:启动字节+帧头+包头段(0x43+简易绑定号+屏大小)+DALL+CRC校验码+帧尾。 |
| | | // 2.智能壳回指令:启动字节+帧头+包头段+DALL+返回标志+加密地址+CRC校验码+帧尾。 |
| | | // 构建指令数据 |
| | | const startBytes = new Array(10).fill(0x55); // 10个启动字节 |
| | | const frameHeader = 0x7E; // 帧头 |
| | | const packageType = 0x43; // 包类型 |
| | | // 使用新的简易绑定号生成函数 |
| | | const simpleBindId = this.generateSimpleBindId(); |
| | | |
| | | // 屏大小 (2 bytes) |
| | | const screenSize = [0x0C, 0x18]; |
| | | 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, true) |
| | | }, 50); |
| | | }, |
| | | // 启动INST发送超时计时器:发送INST帧后等待智能壳回应,超过5秒未收到则判定发送失败 |
| | | startInstTimer() { |
| | | this.clearInstTimer(); |
| | | this.instTimer = setTimeout(() => { |
| | | this.handleInstTimeout(); |
| | | }, 5000); |
| | | }, |
| | | // 清除INST发送超时计时器 |
| | | clearInstTimer() { |
| | | if (this.instTimer) { |
| | | clearTimeout(this.instTimer); |
| | | this.instTimer = null; |
| | | } |
| | | }, |
| | | // INST发送超时处理:停止发送并提示发送失败 |
| | | handleInstTimeout() { |
| | | this.instTimer = null; |
| | | this.instSendAborted = true; |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: '发送失败', |
| | | icon: 'none', |
| | | mask: true |
| | | }); |
| | | }, |
| | | //添加信息指令(INST) |
| | | commandINST(hexString) { |
| | | // 1.手机发指令:启动+帧头+包头段(0x43+简易绑定号+屏大小)+INST+信息属性+文件名+总帧数+当前帧*+块描述+块数据+CRC校验码+帧尾 |
| | |
| | | //console.log(this.ledCurrent, '----INST data----', this.hexArrayToHexString(data)) |
| | | //const buffer = new Uint8Array(data).buffer; |
| | | console.log('第几帧-------',this.ledCurrent) |
| | | setTimeout(() => { |
| | | // 根据总帧数和已发送数量更新发送进度(当前帧发送前,已发送ledCurrent帧),范围10%~99% |
| | | const sendPercent = Math.max(10, Math.min(99, Math.round(this.ledCurrent / this.ledTotal * 100))); |
| | | uni.showLoading({ |
| | | title: '发送中' + sendPercent + '%', |
| | | mask: true |
| | | }); |
| | | setTimeout(async () => { |
| | | //this.writeBLECharacteristicValue(buffer) |
| | | this.sendBLEDataInChunks( |
| | | this.hexArrayToHexString(data) |
| | | ) |
| | | const ok = await this.sendBLEDataInChunks(this.hexArrayToHexString(data)) |
| | | if (ok) { |
| | | // 数据发送成功后启动超时计时器,等待智能壳回应,超过5秒未收到则判定发送失败 |
| | | this.startInstTimer() |
| | | } |
| | | }, 50); |
| | | }, |
| | | // 发送结束针 |
| | |
| | | |
| | | const endbuffer = new Uint8Array(endData).buffer; |
| | | setTimeout(() => { |
| | | this.writeBLECharacteristicValue(endbuffer) |
| | | this.writeBLECharacteristicValue(endbuffer, true) |
| | | // 结束帧发送后启动超时计时器,等待智能壳回应,超过5秒未收到则判定发送失败 |
| | | this.startInstTimer() |
| | | }, 50); |
| | | }, |
| | | async sendBLEDataInChunks(hexStr) { |
| | |
| | | }) |
| | | }) |
| | | } catch (err) { |
| | | console.error(`🚫 发送中断,第 ${i + 1} 包发送失败`) |
| | | console.error(`🚫 发送中断,第 ${i + 1} 帧发送失败`, err) |
| | | uni.hideLoading(); |
| | | break |
| | | uni.showToast({ |
| | | title: `🚫 发送中断,第 ${i + 1} 帧发送失败`, |
| | | duration: 4000 |
| | | }); |
| | | return false |
| | | } |
| | | // 延迟下一包发送 |
| | | await new Promise(r => setTimeout(r, interval)) |
| | | } |
| | | //console.log("🎉 数据发送完成") |
| | | return true |
| | | }, |
| | | // 读取电量 |
| | | commandPOWR() { |
| | |
| | | } else if (resName == 'PLAY') { |
| | | |
| | | } else if (resName == 'INST') { |
| | | // 收到INST回应,清除发送超时计时器 |
| | | that.clearInstTimer(); |
| | | // 若已因超时中止发送,忽略迟到的回应,不再继续发送 |
| | | if (that.instSendAborted) { |
| | | return; |
| | | } |
| | | // 添加动画帧 |
| | | that.ledCurrent++ |
| | | if (that.ledCurrent < that.ledTotal) { |
| | |
| | | // }); |
| | | that.commandINST(that.ledData[that.ledCurrent]); |
| | | } else if (that.ledCurrent == that.ledTotal) { |
| | | // uni.showLoading({ |
| | | // title: '发送结束帧', |
| | | // mask: true |
| | | // }); |
| | | // 所有数据帧已发送完成,发送结束帧,进度显示100% |
| | | uni.showLoading({ |
| | | title: '发送中100%', |
| | | mask: true |
| | | }); |
| | | that.commandEnd() |
| | | } else { |
| | | // 结束帧应答(ledCurrent == ledTotal + 1),全部发送完成,隐藏loading |
| | | uni.hideLoading(); |
| | | } |
| | | |
| | | } else if (resName == 'DALL') { |
| | | |
| | | // 清除所有信息指令回应:返回标志0x30工作无异常,0x31工作有异常 |
| | | if (that.waitDallThenSend) { |
| | | that.waitDallThenSend = false; |
| | | // 收到DALL回应后,开始发送第一帧INST |
| | | that.commandINST(that.ledData[that.ledCurrent]); |
| | | } |
| | | } else if (resName == 'POWR') { |
| | | // 查询电量 |
| | | let capacityHex = result.slice(-8, -6); // "0a" |