diff --git a/mixerControlE-.png b/mixerControlE-.png new file mode 100644 index 0000000..6f767f8 Binary files /dev/null and b/mixerControlE-.png differ diff --git a/mixerControlE.jpg b/mixerControlE.jpg new file mode 100644 index 0000000..5d7f774 Binary files /dev/null and b/mixerControlE.jpg differ diff --git a/mixerControlE.png b/mixerControlE.png new file mode 100644 index 0000000..10d7c11 Binary files /dev/null and b/mixerControlE.png differ diff --git a/src/pages/bluetooth/bluetooth.vue b/src/pages/bluetooth/bluetooth.vue index 39084b3..d45304c 100644 --- a/src/pages/bluetooth/bluetooth.vue +++ b/src/pages/bluetooth/bluetooth.vue @@ -902,9 +902,18 @@ const getBLEDeviceServices = async (deviceId) => { let notifyCharacteristicId = '' // 可监听特征值 let mainServiceId = '' // 主服务ID - // 自动获取第一个服务的特征值(用于展示) + // 查找协议规定的服务ID (0x00FF) + // 注意:不同设备可能返回不同格式的UUID,需要兼容匹配 if (res.services.length > 0) { - mainServiceId = res.services[0].uuid + // 优先查找包含 "00FF" 或 "0xFF" 的服务UUID + const targetService = res.services.find(s => { + const uuid = s.uuid.toUpperCase() + return uuid.includes('00FF') || uuid.includes('0XFF') || uuid === '0X00FF' + }) + + // 如果找到目标服务,使用它;否则使用第一个服务 + mainServiceId = targetService ? targetService.uuid : res.services[0].uuid + console.log('使用的主服务UUID:', mainServiceId, targetService ? '(匹配协议0x00FF)' : '(默认第一个服务)') const chars = await getBLEDeviceCharacteristicsSync(deviceId, mainServiceId) characteristics.value = chars diff --git a/src/pages/mixer/mixer.vue b/src/pages/mixer/mixer.vue index 32221da..00159d1 100644 --- a/src/pages/mixer/mixer.vue +++ b/src/pages/mixer/mixer.vue @@ -185,7 +185,7 @@ const channels = ref([ timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, // 剩余秒数 - timerHours: 0 // 设置的小时数 + timerMinutes: 0 // 设置的分钟数 }, { id: 'CH2', @@ -299,12 +299,12 @@ const loadChannelData = (channelId) => { if (channel) { // 恢复保存的数据,但状态默认为停止 channel.runValue = data.runValue || channel.runValue - channel.timerHours = data.timerHours || 0 + channel.timerMinutes = data.timerMinutes || 0 channel.remainingTime = 0 channel.status = 'stopped' - if (channel.timerHours > 0) { - channel.timerValue = `Set ${channel.timerHours} hours` + if (channel.timerMinutes > 0) { + channel.timerValue = `Set ${channel.timerMinutes} min` } } } @@ -320,7 +320,7 @@ const saveChannelData = (channelId) => { if (channel) { const data = { runValue: channel.runValue, - timerHours: channel.timerHours + timerMinutes: channel.timerMinutes } uni.setStorageSync(STORAGE_KEY_PREFIX + channelId, JSON.stringify(data)) } @@ -412,32 +412,32 @@ const setTimer = (channelId) => { uni.showModal({ title: 'Timer Setting', editable: true, - placeholderText: 'Enter hours (0-99)', - content: channel.timerHours > 0 ? String(channel.timerHours) : '', + placeholderText: 'Enter minutes (0-9999)', + content: channel.timerMinutes > 0 ? String(channel.timerMinutes) : '', cancelText: 'Cancel', confirmText: 'OK', success: (res) => { if (res.confirm && res.content) { - const hours = parseInt(res.content) - if (isNaN(hours) || hours < 0 || hours > 99) { + const minutes = parseInt(res.content) + if (isNaN(minutes) || minutes < 0 || minutes > 9999) { uni.showToast({ - title: 'Please enter 0-99', + title: 'Please enter 0-9999', icon: 'none' }) return } - channel.timerHours = hours - channel.remainingTime = hours * 3600 // Convert to seconds (hours*3600) + channel.timerMinutes = minutes + channel.remainingTime = minutes * 60 // Convert to seconds (minutes*60) - if (hours > 0) { - channel.timerValue = `Set ${hours} hours` + if (minutes > 0) { + channel.timerValue = `Set ${minutes} min` } else { channel.timerValue = 'Not Set' } saveChannelData(channelId) - sendBluetoothCommand('setTimer', channelId, hours) + sendBluetoothCommand('setTimer', channelId, minutes) uni.showToast({ title: `Timer Set Successfully`, @@ -463,10 +463,10 @@ const startChannel = (channelId) => { const channel = channels.value.find(c => c.id === channelId) if (channel) { // 如果设置了定时,进入定时模式 - if (channel.timerHours > 0) { + if (channel.timerMinutes > 0) { channel.status = 'timing' // 重置剩余时间 - channel.remainingTime = channel.timerHours * 3600 + channel.remainingTime = channel.timerMinutes * 60 startTimer(channelId) } else { channel.status = 'running' @@ -511,9 +511,24 @@ const batchStart = () => { cancelText: 'Cancel', success: (res) => { if (res.confirm) { + // 更新本地状态 selectedChannels.value.forEach(channelId => { - startChannel(channelId) + const channel = channels.value.find(c => c.id === channelId) + if (channel) { + // 如果设置了定时,进入定时模式 + if (channel.timerMinutes > 0) { + channel.status = 'timing' + channel.remainingTime = channel.timerMinutes * 60 + startTimer(channelId) + } else { + channel.status = 'running' + } + } }) + + // 批量发送蓝牙命令(一次性发送所有选中通道的状态) + sendBluetoothCommand('batchStart', null, null) + uni.showToast({ title: `Started ${selectedChannels.value.length} channels`, icon: 'success' @@ -540,9 +555,18 @@ const batchStop = () => { cancelText: 'Cancel', success: (res) => { if (res.confirm) { + // 更新本地状态 selectedChannels.value.forEach(channelId => { - stopChannel(channelId) + const channel = channels.value.find(c => c.id === channelId) + if (channel) { + channel.status = 'stopped' + stopTimer(channelId) + } }) + + // 批量发送蓝牙命令(一次性发送所有选中通道的状态) + sendBluetoothCommand('batchStop', null, null) + uni.showToast({ title: `Stopped ${selectedChannels.value.length} channels`, icon: 'success' @@ -591,7 +615,7 @@ const stopTimer = (channelId) => { } } -// 发送蓝牙命令 +// 发送蓝牙命令(根据协议规范) const sendBluetoothCommand = (command, channelId, value) => { if (!connected.value) { uni.showToast({ @@ -607,43 +631,144 @@ const sendBluetoothCommand = (command, channelId, value) => { } try { - // 构造简短的 JSON 命令(减少字节数) - const commandData = { - c: command, // cmd 缩写为 c - n: channelId, // channel 缩写为 n - v: value || 0 // value 缩写为 v - } + const channelIndex = channelId ? parseInt(channelId.replace('CH', '')) - 1 : 0 - const jsonStr = JSON.stringify(commandData) - console.log('Sending Bluetooth command:', jsonStr, `(${jsonStr.length} bytes)`) - - // 检查是否需要分包 - if (jsonStr.length <= 20) { - // 单包发送 - sendSinglePacket(jsonStr) - } else { - // 分包发送 - sendMultiplePackets(jsonStr) + switch(command) { + case 'start': + // 写入电机状态(0xFF01) + writeMotorStatus(channelIndex, 3) // 3=运行 + break + + case 'stop': + // 写入电机状态(0xFF01) + writeMotorStatus(channelIndex, 1) // 1=停止 + break + + case 'batchStart': + // 批量启动 - 一次性发送所有选中通道的状态 + writeMotorStatus(0, 3, true, selectedChannels.value) + break + + case 'batchStop': + // 批量停止 - 一次性发送所有选中通道的状态 + writeMotorStatus(0, 1, true, selectedChannels.value) + break + + case 'setRunValue': + // 写入电机速度(0xFF02) + // 注意:速度范围是 0-100,不需要转换 + writeMotorSpeed(channelIndex, value) + break + + case 'setTimer': + // 写入电机定时设置(0xFF04),单位:分钟 + writeMotorTimer(channelIndex, value) // 直接使用分钟数 + break + + case 'query': + // 查询电机所有属性(0xFF06)- 通过读取实现 + readMotorAllProperties() + break + + case 'queryRemainingTime': + // 查询电机定时剩余时间(0xFF05) + readMotorRemainingTime() + break } } catch (error) { - console.error('Failed to construct Bluetooth command:', error) + console.error('Failed to send Bluetooth command:', error) } } -// 发送单个数据包 -const sendSinglePacket = (data) => { - const buffer = stringToArrayBuffer(data) +// 写入电机状态(0xFF01) - 格式:电机序号 + 状态值 +// 支持单个或批量操作 +const writeMotorStatus = (channelIndex, status, isBatch = false, batchChannels = []) => { + // 写格式:长度可变,每两个字节表示一组,采用电机序号+电机状态的方式 + // 单个:[1, 2] 表示设置第1个电机为状态2(定时) + // 批量:[1, 2, 2, 3, 3, 2] 表示设置第1个电机状态2,第2个电机状态3,第3个电机状态2 + + let channels = [] + if (isBatch && batchChannels.length > 0) { + // 批量操作 + channels = batchChannels.map(chId => { + const idx = parseInt(chId.replace('CH', '')) - 1 + return { index: idx, status: status } + }) + } else { + // 单个操作 + channels = [{ index: channelIndex, status: status }] + } + + const buffer = new ArrayBuffer(channels.length * 2) + const dataView = new Uint8Array(buffer) + + channels.forEach((ch, i) => { + dataView[i * 2] = ch.index + 1 // 电机序号(1-6) + dataView[i * 2 + 1] = ch.status // 状态值(0=初始化, 1=停止, 2=定时, 3=运行) + }) + + writeBLEData('0xFF01', buffer) +} + +// 写入电机速度(0xFF02) - 格式:电机序号 + 速度值 +// 支持单个或批量操作 +const writeMotorSpeed = (channelIndex, speed, isBatch = false, batchChannels = []) => { + // 写格式:长度可变,每两个字节表示一组,采用电机序号+电机速度的方式 + // 单个:[1, 50] 表示设置第1个电机速度50 + // 批量:[1, 50, 2, 60] 表示设置第1个电机速度50,第2个电机速度60 + + let channels = [] + if (isBatch && batchChannels.length > 0) { + // 批量操作 - 使用各通道自己的速度 + channels = batchChannels.map(chId => { + const idx = parseInt(chId.replace('CH', '')) - 1 + const ch = channels.value[idx] + return { index: idx, speed: ch.runValue } + }) + } else { + // 单个操作 + channels = [{ index: channelIndex, speed: speed }] + } + + const buffer = new ArrayBuffer(channels.length * 2) + const dataView = new Uint8Array(buffer) + + channels.forEach((ch, i) => { + dataView[i * 2] = ch.index + 1 // 电机序号(1-6) + dataView[i * 2 + 1] = Math.min(100, Math.max(0, ch.speed)) // 速度值(0-100) + }) + + writeBLEData('0xFF02', buffer) +} + +// 写入电机定时设置(0xFF04) - 格式:电机序号 + 定时时间(小端序) +const writeMotorTimer = (channelIndex, minutes) => { + // 写格式:长度可变,每3个字节表示一组,采用电机序号+电机定时时间的方式 + // 例如:[1, 2, 5] 表示第1个电机定时1282分钟(2 + 5*256 = 1282) + const buffer = new ArrayBuffer(3) + const dataView = new Uint8Array(buffer) + + dataView[0] = channelIndex + 1 // 电机序号(1-6) + dataView[1] = minutes & 0xFF // 定时时间低位 + dataView[2] = (minutes >> 8) & 0xFF // 定时时间高位 + + writeBLEData('0xFF04', buffer) +} + +// 通用 BLE 写入函数 +const writeBLEData = (characteristicUUID, buffer) => { + console.log(`Writing to ${characteristicUUID}, ${buffer.byteLength} bytes`) uni.writeBLECharacteristicValue({ deviceId: deviceId.value, serviceId: serviceId.value, - characteristicId: characteristicId.value, + characteristicId: characteristicUUID, value: buffer, success: (res) => { - console.log('✅ Bluetooth data sent successfully') + console.log(`✅ Successfully wrote to ${characteristicUUID}`) }, fail: (err) => { - console.error('❌ Bluetooth data send failed:', err) + console.error(`❌ Failed to write to ${characteristicUUID}:`, err) uni.showToast({ title: 'Send Failed', icon: 'none' @@ -652,39 +777,38 @@ const sendSinglePacket = (data) => { }) } -// 分包发送(超过 20 字节) -const sendMultiplePackets = (data) => { - const maxPacketSize = 20 - const packets = [] - - // 将数据分割成多个包 - for (let i = 0; i < data.length; i += maxPacketSize) { - packets.push(data.slice(i, i + maxPacketSize)) - } - - console.log(`📦 Data needs to be sent in ${packets.length} packets`) - - // 依次发送每个包(间隔 100ms) - packets.forEach((packet, index) => { - setTimeout(() => { - const buffer = stringToArrayBuffer(packet) - - uni.writeBLECharacteristicValue({ - deviceId: deviceId.value, - serviceId: serviceId.value, - characteristicId: characteristicId.value, - value: buffer, - success: () => { - console.log(`✅ Packet ${index + 1}/${packets.length} sent successfully`) - }, - fail: (err) => { - console.error(`❌ Packet ${index + 1}/${packets.length} send failed:`, err) - } - }) - }, index * 100) +// 读取电机所有属性(0xFF06) +const readMotorAllProperties = () => { + uni.readBLECharacteristicValue({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: '0xFF06', + success: (res) => { + console.log('✅ Successfully read all properties') + }, + fail: (err) => { + console.error('❌ Failed to read all properties:', err) + } }) } +// 读取电机定时剩余时间(0xFF05) +const readMotorRemainingTime = () => { + uni.readBLECharacteristicValue({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: '0xFF05', + success: (res) => { + console.log('✅ Successfully read remaining time') + }, + fail: (err) => { + console.error('❌ Failed to read remaining time:', err) + } + }) +} + +// 注:sendSinglePacket 和 sendMultiplePackets 已被新的协议方式替代,保留作为备用 + // 字符串转 ArrayBuffer(辅助函数) const stringToArrayBuffer = (str) => { const buffer = new ArrayBuffer(str.length) @@ -732,102 +856,220 @@ const initBluetoothCommunication = () => { } }) - // 监听特征值变化(接收数据) - if (notifyCharacteristicId.value) { - uni.onBLECharacteristicValueChange((res) => { - console.log('收到蓝牙数据:', res) - handleBluetoothData(res.value) - }) + // 监听特征值变化(接收数据)- 启用所有相关特征值的通知 + const characteristicsToNotify = [ + { id: '0xFF01', name: '电机状态' }, + { id: '0xFF02', name: '电机速度' }, + { id: '0xFF03', name: '运行方向' }, + { id: '0xFF04', name: '定时设置' }, + { id: '0xFF05', name: '剩余时间' }, + { id: '0xFF06', name: '所有属性' } + ] + + // 注册数据监听回调 + uni.onBLECharacteristicValueChange((res) => { + console.log('收到蓝牙数据:', res) - // 启用通知 - uni.notifyBLECharacteristicValueChange({ - deviceId: deviceId.value, - serviceId: serviceId.value, - characteristicId: notifyCharacteristicId.value, - state: true, - success: () => { - console.log('启用蓝牙数据监听成功') - hasRegisteredListener.value = true - // 启动定时查询 - startDataQuery() - }, - fail: (err) => { - console.error('启用蓝牙数据监听失败:', err) - hasRegisteredListener.value = false + // 根据特征值ID处理数据 + const dataView = new Uint8Array(res.value) + const dataLength = dataView.length + const charId = res.characteristicId + + if (dataLength === 6) { + // 6字节数据,根据特征值ID区分 + if (charId === '0xFF01') { + // 电机状态 + channels.value.forEach((channel, index) => { + const status = dataView[index] + const statusMap = { 0: 'stopped', 1: 'stopped', 2: 'timing', 3: 'running' } + channel.status = statusMap[status] || 'stopped' + console.log(`${channel.name} 状态:`, channel.status) + }) + } else if (charId === '0xFF02') { + // 电机速度 + channels.value.forEach((channel, index) => { + const speed = dataView[index] + channel.runValue = speed + console.log(`${channel.name} 速度:`, speed) + }) + } else if (charId === '0xFF03') { + // 运行方向 + channels.value.forEach((channel, index) => { + const direction = dataView[index] + console.log(`${channel.name} 方向:`, direction === 0 ? '正转' : '反转') + }) + } else { + // 未知特征值,使用智能判断 + parseMotorStatusOrSpeed(dataView) } - }) - } else { - hasRegisteredListener.value = true - } + } else if (dataLength === 12) { + // 12字节数据,根据特征值ID区分 + if (charId === '0xFF04') { + parseMotorTimer(dataView, 'setup') // 定时设置 + } else if (charId === '0xFF05') { + parseMotorTimer(dataView, 'remaining') // 剩余时间 + } else { + parseMotorTimer(dataView, 'unknown') // 未知,智能判断 + } + } else if (dataLength === 42) { + // 42字节数据 - 所有属性 + parseMotorAllProperties(dataView) + } else { + // 其他长度数据,使用通用处理 + handleBluetoothData(res.value) + } + }) + + // 为每个特征值启用通知 + characteristicsToNotify.forEach((char, index) => { + setTimeout(() => { + uni.notifyBLECharacteristicValueChange({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: char.id, + state: true, + success: () => { + console.log(`启用 ${char.name}(${char.id}) 数据监听成功`) + if (index === characteristicsToNotify.length - 1) { + hasRegisteredListener.value = true + // 启动定时查询 + startDataQuery() + } + }, + fail: (err) => { + console.warn(`启用 ${char.name}(${char.id}) 数据监听失败:`, err) + } + }) + }, index * 100) // 间隔 100ms 避免冲突 + }) } -// 处理接收到的蓝牙数据 +// 处理接收到的蓝牙数据(根据协议规范解析) const handleBluetoothData = (buffer) => { try { - // 将 ArrayBuffer 转换为字符串 - const dataStr = arrayBufferToString(buffer) - console.log('收到数据片段:', dataStr) + const dataView = new Uint8Array(buffer) + const dataLength = dataView.length - // 处理分包数据:将数据追加到缓冲区 - receiveBuffer.value += dataStr + console.log('收到蓝牙数据:', dataLength, '字节') - // 尝试解析完整的 JSON 数据 - try { - // 查找完整的 JSON 对象(以 { 开始,} 结束) - const jsonMatch = receiveBuffer.value.match(/\{[^}]*\}/) - if (jsonMatch) { - const jsonStr = jsonMatch[0] - const data = JSON.parse(jsonStr) - - // 解析成功,更新通道状态 - updateChannelFromDevice(data) - - // 清空已处理的数据(保留剩余未处理的数据) - receiveBuffer.value = receiveBuffer.value.substring(jsonMatch.index + jsonStr.length) - } else { - // 如果缓冲区太大但没有完整 JSON,可能是垃圾数据,清空缓冲区 - if (receiveBuffer.value.length > 200) { - console.warn('缓冲区数据过大且无法解析,清空缓冲区:', receiveBuffer.value) - receiveBuffer.value = '' - } - } - } catch (e) { - // JSON 解析失败,可能是数据还不完整,等待更多数据 - console.log('等待更多数据片段..., 当前缓冲区:', receiveBuffer.value) - - // 如果缓冲区太大,清空它 - if (receiveBuffer.value.length > 200) { - console.warn('缓冲区溢出,清空:', receiveBuffer.value) - receiveBuffer.value = '' - } + // 根据数据长度判断是哪个特征值的数据 + if (dataLength === 6) { + // 可能是电机状态(0xFF01)或电机速度(0xFF02)或运行方向(0xFF03) + // 需要根据实际情况判断,这里先处理状态和速度 + parseMotorStatusOrSpeed(dataView) + } else if (dataLength === 12) { + // 电机定时设置(0xFF04)或剩余时间(0xFF05) + // 注意:这里无法直接区分,需要通过上下文(最近的读取操作)来判断 + // 建议:在监听回调中记录特征值UUID + parseMotorTimer(dataView, 'unknown') // 传入类型标识 + } else if (dataLength === 42) { + // 电机所有属性(0xFF06) + parseMotorAllProperties(dataView) + } else { + console.warn('未知的数据长度:', dataLength) } } catch (error) { console.error('解析蓝牙数据失败:', error) - receiveBuffer.value = '' // 出错时清空缓冲区 } } -// 根据设备返回的数据更新通道状态 -const updateChannelFromDevice = (data) => { - if (!data.channel) return +// 解析电机状态或速度(6字节) +// 注意:无法通过数值范围准确区分,建议通过特征值ID区分 +const parseMotorStatusOrSpeed = (dataView) => { + // 暂时策略:如果所有值都 <= 3,认为是状态;否则认为是速度 + let allSmall = true + for (let i = 0; i < 6; i++) { + if (dataView[i] > 3) { + allSmall = false + break + } + } - const channel = channels.value.find(c => c.id === data.channel) - if (channel) { - // 更新通道状态(根据实际数据格式调整) - if (data.status !== undefined) { - channel.status = data.status // running/stopped/timing + channels.value.forEach((channel, index) => { + const value = dataView[index] + + if (allSmall) { + // 认为是电机状态 + const statusMap = { 0: 'stopped', 1: 'stopped', 2: 'timing', 3: 'running' } + channel.status = statusMap[value] || 'stopped' + console.log(`${channel.name} 状态:`, channel.status) + } else { + // 认为是电机速度(0-100) + channel.runValue = value + console.log(`${channel.name} 速度:`, value) } - if (data.speed !== undefined) { - channel.runValue = Math.round(data.speed / 4) // 实际转速转回 0-100 + }) +} + +// 解析电机定时(12字节) +// type: 'setup' = 定时设置(0xFF04), 'remaining' = 剩余时间(0xFF05), 'unknown' = 未知 +const parseMotorTimer = (dataView, type = 'unknown') => { + channels.value.forEach((channel, index) => { + // 低位在前,高位在后(小端序) + const minutes = dataView[index * 2] | (dataView[index * 2 + 1] << 8) + + if (minutes > 0) { + if (type === 'remaining') { + // 明确是剩余时间(0xFF05) + channel.remainingTime = minutes * 60 // 转为秒 + console.log(`${channel.name} 剩余时间:`, minutes, '分钟') + } else if (type === 'setup') { + // 明确是定时设置(0xFF04) + channel.timerMinutes = minutes + channel.timerValue = `Set ${minutes} min` + console.log(`${channel.name} 定时设置:`, minutes, '分钟') + } else { + // 未知类型,根据当前状态智能判断(兼容旧逻辑) + if (channel.status === 'timing' && channel.remainingTime > 0) { + // 如果正在定时中且已有剩余时间,优先更新剩余时间 + channel.remainingTime = minutes * 60 + console.log(`${channel.name} 剩余时间(推测):`, minutes, '分钟') + } else { + // 否则当作定时设置 + channel.timerMinutes = minutes + channel.timerValue = `Set ${minutes} min` + console.log(`${channel.name} 定时设置(推测):`, minutes, '分钟') + } + } } - if (data.remainingTime !== undefined) { - channel.remainingTime = data.remainingTime + }) +} + +// 解析电机所有属性(42字节) +const parseMotorAllProperties = (dataView) => { + // 格式:6字节状态 + 6字节速度 + 6字节方向 + 12字节定时设置 + 12字节剩余时间 + + channels.value.forEach((channel, index) => { + // 电机状态(0-5字节) + const status = dataView[index] + const statusMap = { 0: 'stopped', 1: 'stopped', 2: 'timing', 3: 'running' } + channel.status = statusMap[status] || 'stopped' + + // 电机速度(6-11字节) + const speed = dataView[6 + index] + channel.runValue = speed + + // 电机方向(12-17字节) + const direction = dataView[12 + index] + // 可以根据需要存储方向信息 + + // 定时设置(18-29字节) + const timerMinutes = dataView[18 + index * 2] | (dataView[18 + index * 2 + 1] << 8) + if (timerMinutes > 0) { + channel.timerMinutes = timerMinutes + channel.timerValue = `Set ${timerMinutes} min` } - console.log(`通道 ${data.channel} 状态已更新:`, channel) - } + // 剩余时间(30-41字节) + const remainingMinutes = dataView[30 + index * 2] | (dataView[30 + index * 2 + 1] << 8) + channel.remainingTime = remainingMinutes * 60 // 转为秒 + + console.log(`${channel.name} 完整数据 - 状态:${channel.status}, 速度:${speed}, 方向:${direction}, 定时:${timerMinutes}分钟, 剩余:${remainingMinutes}分钟`) + }) } +// 注:updateChannelFromDevice 已被新的协议解析方式替代 + // 启动定时查询所有通道数据 const startDataQuery = () => { // 如果已经在查询,先停止 @@ -855,7 +1097,7 @@ const stopDataQuery = () => { } } -// 查询所有通道数据 +// 查询所有通道数据(包括状态、速度、剩余时间等) const queryAllChannelsData = () => { if (!connected.value || isQuerying.value) { return @@ -863,17 +1105,20 @@ const queryAllChannelsData = () => { isQuerying.value = true - // 依次查询每个通道(根据实际协议调整) - channels.value.forEach((channel, index) => { - setTimeout(() => { - sendBluetoothCommand('query', channel.id) - }, index * 200) // 每个通道间隔 200ms,避免数据冲突 - }) + // 优先查询完整属性(0xFF06),包含所有信息 + setTimeout(() => { + sendBluetoothCommand('query', null) + }, 0) + + // 备选:单独查询剩余时间(0xFF05) + setTimeout(() => { + sendBluetoothCommand('queryRemainingTime', null) + }, 500) // 查询完成后重置标志 setTimeout(() => { isQuerying.value = false - }, channels.value.length * 200 + 500) + }, 1000) } // 页面卸载