fix: 蓝牙协议接口编码实现。

This commit is contained in:
tianyongbao
2025-12-23 22:57:42 +08:00
parent 3b025dcba9
commit 068b43bea9
5 changed files with 412 additions and 158 deletions

BIN
mixerControlE-.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

BIN
mixerControlE.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
mixerControlE.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -902,9 +902,18 @@ const getBLEDeviceServices = async (deviceId) => {
let notifyCharacteristicId = '' // 可监听特征值 let notifyCharacteristicId = '' // 可监听特征值
let mainServiceId = '' // 主服务ID let mainServiceId = '' // 主服务ID
// 自动获取第一个服务的特征值(用于展示) // 查找协议规定的服务ID (0x00FF)
// 注意不同设备可能返回不同格式的UUID需要兼容匹配
if (res.services.length > 0) { 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) const chars = await getBLEDeviceCharacteristicsSync(deviceId, mainServiceId)
characteristics.value = chars characteristics.value = chars

View File

@@ -185,7 +185,7 @@ const channels = ref([
timerMode: 'Timer', timerMode: 'Timer',
timerValue: 'Not Set', timerValue: 'Not Set',
remainingTime: 0, // 剩余秒数 remainingTime: 0, // 剩余秒数
timerHours: 0 // 设置的小时 timerMinutes: 0 // 设置的分钟
}, },
{ {
id: 'CH2', id: 'CH2',
@@ -299,12 +299,12 @@ const loadChannelData = (channelId) => {
if (channel) { if (channel) {
// 恢复保存的数据,但状态默认为停止 // 恢复保存的数据,但状态默认为停止
channel.runValue = data.runValue || channel.runValue channel.runValue = data.runValue || channel.runValue
channel.timerHours = data.timerHours || 0 channel.timerMinutes = data.timerMinutes || 0
channel.remainingTime = 0 channel.remainingTime = 0
channel.status = 'stopped' channel.status = 'stopped'
if (channel.timerHours > 0) { if (channel.timerMinutes > 0) {
channel.timerValue = `Set ${channel.timerHours} hours` channel.timerValue = `Set ${channel.timerMinutes} min`
} }
} }
} }
@@ -320,7 +320,7 @@ const saveChannelData = (channelId) => {
if (channel) { if (channel) {
const data = { const data = {
runValue: channel.runValue, runValue: channel.runValue,
timerHours: channel.timerHours timerMinutes: channel.timerMinutes
} }
uni.setStorageSync(STORAGE_KEY_PREFIX + channelId, JSON.stringify(data)) uni.setStorageSync(STORAGE_KEY_PREFIX + channelId, JSON.stringify(data))
} }
@@ -412,32 +412,32 @@ const setTimer = (channelId) => {
uni.showModal({ uni.showModal({
title: 'Timer Setting', title: 'Timer Setting',
editable: true, editable: true,
placeholderText: 'Enter hours (0-99)', placeholderText: 'Enter minutes (0-9999)',
content: channel.timerHours > 0 ? String(channel.timerHours) : '', content: channel.timerMinutes > 0 ? String(channel.timerMinutes) : '',
cancelText: 'Cancel', cancelText: 'Cancel',
confirmText: 'OK', confirmText: 'OK',
success: (res) => { success: (res) => {
if (res.confirm && res.content) { if (res.confirm && res.content) {
const hours = parseInt(res.content) const minutes = parseInt(res.content)
if (isNaN(hours) || hours < 0 || hours > 99) { if (isNaN(minutes) || minutes < 0 || minutes > 9999) {
uni.showToast({ uni.showToast({
title: 'Please enter 0-99', title: 'Please enter 0-9999',
icon: 'none' icon: 'none'
}) })
return return
} }
channel.timerHours = hours channel.timerMinutes = minutes
channel.remainingTime = hours * 3600 // Convert to seconds (hours*3600) channel.remainingTime = minutes * 60 // Convert to seconds (minutes*60)
if (hours > 0) { if (minutes > 0) {
channel.timerValue = `Set ${hours} hours` channel.timerValue = `Set ${minutes} min`
} else { } else {
channel.timerValue = 'Not Set' channel.timerValue = 'Not Set'
} }
saveChannelData(channelId) saveChannelData(channelId)
sendBluetoothCommand('setTimer', channelId, hours) sendBluetoothCommand('setTimer', channelId, minutes)
uni.showToast({ uni.showToast({
title: `Timer Set Successfully`, title: `Timer Set Successfully`,
@@ -463,10 +463,10 @@ const startChannel = (channelId) => {
const channel = channels.value.find(c => c.id === channelId) const channel = channels.value.find(c => c.id === channelId)
if (channel) { if (channel) {
// 如果设置了定时,进入定时模式 // 如果设置了定时,进入定时模式
if (channel.timerHours > 0) { if (channel.timerMinutes > 0) {
channel.status = 'timing' channel.status = 'timing'
// 重置剩余时间 // 重置剩余时间
channel.remainingTime = channel.timerHours * 3600 channel.remainingTime = channel.timerMinutes * 60
startTimer(channelId) startTimer(channelId)
} else { } else {
channel.status = 'running' channel.status = 'running'
@@ -511,9 +511,24 @@ const batchStart = () => {
cancelText: 'Cancel', cancelText: 'Cancel',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
// 更新本地状态
selectedChannels.value.forEach(channelId => { 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({ uni.showToast({
title: `Started ${selectedChannels.value.length} channels`, title: `Started ${selectedChannels.value.length} channels`,
icon: 'success' icon: 'success'
@@ -540,9 +555,18 @@ const batchStop = () => {
cancelText: 'Cancel', cancelText: 'Cancel',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
// 更新本地状态
selectedChannels.value.forEach(channelId => { 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({ uni.showToast({
title: `Stopped ${selectedChannels.value.length} channels`, title: `Stopped ${selectedChannels.value.length} channels`,
icon: 'success' icon: 'success'
@@ -591,7 +615,7 @@ const stopTimer = (channelId) => {
} }
} }
// 发送蓝牙命令 // 发送蓝牙命令(根据协议规范)
const sendBluetoothCommand = (command, channelId, value) => { const sendBluetoothCommand = (command, channelId, value) => {
if (!connected.value) { if (!connected.value) {
uni.showToast({ uni.showToast({
@@ -607,43 +631,144 @@ const sendBluetoothCommand = (command, channelId, value) => {
} }
try { try {
// 构造简短的 JSON 命令(减少字节数) const channelIndex = channelId ? parseInt(channelId.replace('CH', '')) - 1 : 0
const commandData = {
c: command, // cmd 缩写为 c
n: channelId, // channel 缩写为 n
v: value || 0 // value 缩写为 v
}
const jsonStr = JSON.stringify(commandData) switch(command) {
console.log('Sending Bluetooth command:', jsonStr, `(${jsonStr.length} bytes)`) case 'start':
// 写入电机状态0xFF01
writeMotorStatus(channelIndex, 3) // 3=运行
break
// 检查是否需要分包 case 'stop':
if (jsonStr.length <= 20) { // 写入电机状态0xFF01
// 单包发送 writeMotorStatus(channelIndex, 1) // 1=停止
sendSinglePacket(jsonStr) break
} else {
// 分包发送 case 'batchStart':
sendMultiplePackets(jsonStr) // 批量启动 - 一次性发送所有选中通道的状态
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) { } catch (error) {
console.error('Failed to construct Bluetooth command:', error) console.error('Failed to send Bluetooth command:', error)
} }
} }
// 发送单个数据包 // 写入电机状态(0xFF01) - 格式:电机序号 + 状态值
const sendSinglePacket = (data) => { // 支持单个或批量操作
const buffer = stringToArrayBuffer(data) 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({ uni.writeBLECharacteristicValue({
deviceId: deviceId.value, deviceId: deviceId.value,
serviceId: serviceId.value, serviceId: serviceId.value,
characteristicId: characteristicId.value, characteristicId: characteristicUUID,
value: buffer, value: buffer,
success: (res) => { success: (res) => {
console.log('✅ Bluetooth data sent successfully') console.log(`✅ Successfully wrote to ${characteristicUUID}`)
}, },
fail: (err) => { fail: (err) => {
console.error('❌ Bluetooth data send failed:', err) console.error(`❌ Failed to write to ${characteristicUUID}:`, err)
uni.showToast({ uni.showToast({
title: 'Send Failed', title: 'Send Failed',
icon: 'none' icon: 'none'
@@ -652,39 +777,38 @@ const sendSinglePacket = (data) => {
}) })
} }
// 分包发送(超过 20 字节 // 读取电机所有属性0xFF06
const sendMultiplePackets = (data) => { const readMotorAllProperties = () => {
const maxPacketSize = 20 uni.readBLECharacteristicValue({
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, deviceId: deviceId.value,
serviceId: serviceId.value, serviceId: serviceId.value,
characteristicId: characteristicId.value, characteristicId: '0xFF06',
value: buffer, success: (res) => {
success: () => { console.log('✅ Successfully read all properties')
console.log(`✅ Packet ${index + 1}/${packets.length} sent successfully`)
}, },
fail: (err) => { fail: (err) => {
console.error(`❌ Packet ${index + 1}/${packets.length} send failed:`, err) console.error('❌ Failed to read all properties:', err)
} }
}) })
}, index * 100) }
// 读取电机定时剩余时间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辅助函数 // 字符串转 ArrayBuffer辅助函数
const stringToArrayBuffer = (str) => { const stringToArrayBuffer = (str) => {
const buffer = new ArrayBuffer(str.length) const buffer = new ArrayBuffer(str.length)
@@ -732,102 +856,220 @@ const initBluetoothCommunication = () => {
} }
}) })
// 监听特征值变化(接收数据) // 监听特征值变化(接收数据)- 启用所有相关特征值的通知
if (notifyCharacteristicId.value) { const characteristicsToNotify = [
{ id: '0xFF01', name: '电机状态' },
{ id: '0xFF02', name: '电机速度' },
{ id: '0xFF03', name: '运行方向' },
{ id: '0xFF04', name: '定时设置' },
{ id: '0xFF05', name: '剩余时间' },
{ id: '0xFF06', name: '所有属性' }
]
// 注册数据监听回调
uni.onBLECharacteristicValueChange((res) => { uni.onBLECharacteristicValueChange((res) => {
console.log('收到蓝牙数据:', res) console.log('收到蓝牙数据:', res)
// 根据特征值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 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) handleBluetoothData(res.value)
}
}) })
// 启用通知 // 为每个特征值启用通知
characteristicsToNotify.forEach((char, index) => {
setTimeout(() => {
uni.notifyBLECharacteristicValueChange({ uni.notifyBLECharacteristicValueChange({
deviceId: deviceId.value, deviceId: deviceId.value,
serviceId: serviceId.value, serviceId: serviceId.value,
characteristicId: notifyCharacteristicId.value, characteristicId: char.id,
state: true, state: true,
success: () => { success: () => {
console.log('启用蓝牙数据监听成功') console.log(`启用 ${char.name}(${char.id}) 数据监听成功`)
if (index === characteristicsToNotify.length - 1) {
hasRegisteredListener.value = true hasRegisteredListener.value = true
// 启动定时查询 // 启动定时查询
startDataQuery() startDataQuery()
}
}, },
fail: (err) => { fail: (err) => {
console.error('启用蓝牙数据监听失败:', err) console.warn(`启用 ${char.name}(${char.id}) 数据监听失败:`, err)
hasRegisteredListener.value = false
} }
}) })
} else { }, index * 100) // 间隔 100ms 避免冲突
hasRegisteredListener.value = true })
}
} }
// 处理接收到的蓝牙数据 // 处理接收到的蓝牙数据(根据协议规范解析)
const handleBluetoothData = (buffer) => { const handleBluetoothData = (buffer) => {
try { try {
// 将 ArrayBuffer 转换为字符串 const dataView = new Uint8Array(buffer)
const dataStr = arrayBufferToString(buffer) const dataLength = dataView.length
console.log('收到数据片段:', dataStr)
// 处理分包数据:将数据追加到缓冲区 console.log('收到蓝牙数据:', dataLength, '字节')
receiveBuffer.value += dataStr
// 尝试解析完整的 JSON 数据 // 根据数据长度判断是哪个特征值的数据
try { if (dataLength === 6) {
// 查找完整的 JSON 对象(以 { 开始,} 结束 // 可能是电机状态0xFF01或电机速度0xFF02或运行方向0xFF03
const jsonMatch = receiveBuffer.value.match(/\{[^}]*\}/) // 需要根据实际情况判断,这里先处理状态和速度
if (jsonMatch) { parseMotorStatusOrSpeed(dataView)
const jsonStr = jsonMatch[0] } else if (dataLength === 12) {
const data = JSON.parse(jsonStr) // 电机定时设置0xFF04或剩余时间0xFF05
// 注意:这里无法直接区分,需要通过上下文(最近的读取操作)来判断
// 解析成功,更新通道状态 // 建议在监听回调中记录特征值UUID
updateChannelFromDevice(data) parseMotorTimer(dataView, 'unknown') // 传入类型标识
} else if (dataLength === 42) {
// 清空已处理的数据(保留剩余未处理的数据 // 电机所有属性0xFF06
receiveBuffer.value = receiveBuffer.value.substring(jsonMatch.index + jsonStr.length) parseMotorAllProperties(dataView)
} else { } else {
// 如果缓冲区太大但没有完整 JSON可能是垃圾数据清空缓冲区 console.warn('未知的数据长度:', dataLength)
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 = ''
}
} }
} catch (error) { } catch (error) {
console.error('解析蓝牙数据失败:', error) console.error('解析蓝牙数据失败:', error)
receiveBuffer.value = '' // 出错时清空缓冲区
} }
} }
// 根据设备返回的数据更新通道状态 // 解析电机状态或速度6字节
const updateChannelFromDevice = (data) => { // 注意无法通过数值范围准确区分建议通过特征值ID区分
if (!data.channel) return const parseMotorStatusOrSpeed = (dataView) => {
// 暂时策略:如果所有值都 <= 3认为是状态否则认为是速度
const channel = channels.value.find(c => c.id === data.channel) let allSmall = true
if (channel) { for (let i = 0; i < 6; i++) {
// 更新通道状态(根据实际数据格式调整) if (dataView[i] > 3) {
if (data.status !== undefined) { allSmall = false
channel.status = data.status // running/stopped/timing break
} }
if (data.speed !== undefined) {
channel.runValue = Math.round(data.speed / 4) // 实际转速转回 0-100
}
if (data.remainingTime !== undefined) {
channel.remainingTime = data.remainingTime
} }
console.log(`通道 ${data.channel} 状态已更新:`, channel) 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)
} }
})
} }
// 解析电机定时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, '分钟')
}
}
}
})
}
// 解析电机所有属性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`
}
// 剩余时间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 = () => { const startDataQuery = () => {
// 如果已经在查询,先停止 // 如果已经在查询,先停止
@@ -855,7 +1097,7 @@ const stopDataQuery = () => {
} }
} }
// 查询所有通道数据 // 查询所有通道数据(包括状态、速度、剩余时间等)
const queryAllChannelsData = () => { const queryAllChannelsData = () => {
if (!connected.value || isQuerying.value) { if (!connected.value || isQuerying.value) {
return return
@@ -863,17 +1105,20 @@ const queryAllChannelsData = () => {
isQuerying.value = true isQuerying.value = true
// 依次查询每个通道(根据实际协议调整) // 优先查询完整属性0xFF06包含所有信息
channels.value.forEach((channel, index) => {
setTimeout(() => { setTimeout(() => {
sendBluetoothCommand('query', channel.id) sendBluetoothCommand('query', null)
}, index * 200) // 每个通道间隔 200ms避免数据冲突 }, 0)
})
// 备选单独查询剩余时间0xFF05
setTimeout(() => {
sendBluetoothCommand('queryRemainingTime', null)
}, 500)
// 查询完成后重置标志 // 查询完成后重置标志
setTimeout(() => { setTimeout(() => {
isQuerying.value = false isQuerying.value = false
}, channels.value.length * 200 + 500) }, 1000)
} }
// 页面卸载 // 页面卸载