From 654b3278d9b90cdd3ed8740b2027bff31d0deac1 Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Mon, 29 Dec 2025 15:09:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/bluetooth/bluetooth.vue | 194 ++++--- src/pages/mixer/mixer.vue | 819 ++++++++++++++++++++++++------ 2 files changed, 772 insertions(+), 241 deletions(-) diff --git a/src/pages/bluetooth/bluetooth.vue b/src/pages/bluetooth/bluetooth.vue index d45304c..6b235df 100644 --- a/src/pages/bluetooth/bluetooth.vue +++ b/src/pages/bluetooth/bluetooth.vue @@ -44,9 +44,7 @@ - - - + @@ -267,7 +265,7 @@ const openBluetoothAdapter = () => { console.log('蓝牙适配器初始化成功', res) bluetoothEnabled.value = true uni.showToast({ - title: '蓝牙已开启', + title: 'Bluetooth Enabled', icon: 'success' }) @@ -284,8 +282,8 @@ const openBluetoothAdapter = () => { if (err.errCode === 10001) { // 蓝牙未开启 uni.showModal({ - title: '提示', - content: '请在手机设置中开启蓝牙', + title: 'Notice', + content: 'Please enable Bluetooth in phone settings', showCancel: false }) } else if (err.errCode === -1) { @@ -569,7 +567,7 @@ const tryAutoReconnectLastDevice = async () => { uni.hideLoading() uni.showToast({ - title: '搜索失败,请手动操作', + title: 'Search Failed, Manual Operation Required', icon: 'none', duration: 2000 }) @@ -585,7 +583,7 @@ const tryAutoReconnectLastDevice = async () => { const startBluetoothSearch = async () => { if (!bluetoothEnabled.value) { uni.showToast({ - title: '请先开启蓝牙', + title: 'Please Enable Bluetooth First', icon: 'none' }) return @@ -610,7 +608,7 @@ const startBluetoothSearch = async () => { success: (res) => { console.log('开始搜索蓝牙设备', res) uni.showToast({ - title: '开始搜索设备', + title: 'Start Searching Devices', icon: 'success' }) // 监听设备发现 @@ -621,14 +619,14 @@ const startBluetoothSearch = async () => { isSearching.value = false // 根据错误码提供详细提示 - let errorMsg = '搜索失败,请重试' + let errorMsg = 'Search Failed, Please Retry' if (err.errCode === 10001) { - errorMsg = '搜索失败:未开启蓝牙适配器' + errorMsg = 'Search Failed: Bluetooth Adapter Not Enabled' } else if (err.errCode === 10002) { - errorMsg = '搜索失败:未找到蓝牙设备' + errorMsg = 'Search Failed: No Bluetooth Device Found' } else if (err.errCode === 10004) { - errorMsg = '搜索失败:请检查是否已开启位置服务(GPS)' + errorMsg = 'Search Failed: Please Check if Location Service (GPS) is Enabled' // 可能是GPS未开启导致的 guideToEnableGPS() return @@ -650,7 +648,7 @@ const stopBluetoothSearch = () => { console.log('停止搜索蓝牙设备', res) isSearching.value = false uni.showToast({ - title: '已停止搜索', + title: 'Search Stopped', icon: 'success' }) }, @@ -697,7 +695,7 @@ const connectDevice = (device) => { // 如果正在连接中,不允许再次连接 if (connectingDeviceId.value) { uni.showToast({ - title: '正在连接中,请稍候', + title: 'Connecting, Please Wait', icon: 'none' }) return @@ -706,7 +704,7 @@ const connectDevice = (device) => { // 如果已经连接了同一个设备,不需要重复连接 if (connectedDeviceId.value === device.deviceId) { uni.showToast({ - title: '该设备已连接', + title: 'Device Already Connected', icon: 'none' }) return @@ -898,10 +896,20 @@ const getBLEDeviceServices = async (deviceId) => { // 计算所有服务的特征值总数 let totalCount = 0 - let writeCharacteristicId = '' // 可写特征值 - let notifyCharacteristicId = '' // 可监听特征值 + let writeCharacteristicId = '' // 通用可写特征值 + let notifyCharacteristicId = '' // 通用可监听特征值 let mainServiceId = '' // 主服务ID + // 🔧 收集所有协议特征值的128位UUID(用于mixer.vue) + const protocolCharacteristics = { + status: '', // 0xFF01 - 电机状态 + speed: '', // 0xFF02 - 电机速度 + direction: '', // 0xFF03 - 运行方向 + timer: '', // 0xFF04 - 定时设置 + remaining: '', // 0xFF05 - 剩余时间 + allProps: '' // 0xFF06 - 所有属性 + } + // 查找协议规定的服务ID (0x00FF) // 注意:不同设备可能返回不同格式的UUID,需要兼容匹配 if (res.services.length > 0) { @@ -925,22 +933,45 @@ const getBLEDeviceServices = async (deviceId) => { // 找出可以监听的特征值 serviceChars.forEach(characteristic => { - // 查找可写特征值(优先选择 write) + const charUUID = characteristic.uuid.toUpperCase() + + // 🔧 识别协议规定的特征值(通过包含匹配128位UUID) + if (charUUID.includes('FF01') || charUUID.includes('0XFF01')) { + protocolCharacteristics.status = characteristic.uuid + console.log('✅ 找到电机状态特征值(0xFF01):', characteristic.uuid) + } else if (charUUID.includes('FF02') || charUUID.includes('0XFF02')) { + protocolCharacteristics.speed = characteristic.uuid + console.log('✅ 找到电机速度特征值(0xFF02):', characteristic.uuid) + } else if (charUUID.includes('FF03') || charUUID.includes('0XFF03')) { + protocolCharacteristics.direction = characteristic.uuid + console.log('✅ 找到运行方向特征值(0xFF03):', characteristic.uuid) + } else if (charUUID.includes('FF04') || charUUID.includes('0XFF04')) { + protocolCharacteristics.timer = characteristic.uuid + console.log('✅ 找到定时设置特征值(0xFF04):', characteristic.uuid) + } else if (charUUID.includes('FF05') || charUUID.includes('0XFF05')) { + protocolCharacteristics.remaining = characteristic.uuid + console.log('✅ 找到剩余时间特征值(0xFF05):', characteristic.uuid) + } else if (charUUID.includes('FF06') || charUUID.includes('0XFF06')) { + protocolCharacteristics.allProps = characteristic.uuid + console.log('✅ 找到所有属性特征值(0xFF06):', characteristic.uuid) + } + + // 查找通用可写特征值(优先选择 write) if (characteristic.properties.write && !writeCharacteristicId) { writeCharacteristicId = characteristic.uuid if (!mainServiceId) mainServiceId = service.uuid - console.log('找到可写特征值(write):', writeCharacteristicId) + console.log('找到通用可写特征值(write):', writeCharacteristicId) } else if (characteristic.properties.writeNoResponse && !writeCharacteristicId) { writeCharacteristicId = characteristic.uuid if (!mainServiceId) mainServiceId = service.uuid - console.log('找到可写特征值(writeNoResponse):', writeCharacteristicId) + console.log('找到通用可写特征值(writeNoResponse):', writeCharacteristicId) } // 查找可监听特征值 if (characteristic.properties.notify || characteristic.properties.indicate) { if (!notifyCharacteristicId) { notifyCharacteristicId = characteristic.uuid - console.log('找到可监听特征值:', notifyCharacteristicId) + console.log('找到通用可监听特征值:', notifyCharacteristicId) } notifyCharacteristics.value.push({ @@ -957,13 +988,29 @@ const getBLEDeviceServices = async (deviceId) => { totalCharacteristicsCount.value = totalCount console.log(`总共 ${totalCount} 个特征值`) + + // 🔧 打印所有协议特征值 + console.log('📋 协议特征值UUID汇总:') + console.log(' 0xFF01 (状态):', protocolCharacteristics.status || '未找到') + console.log(' 0xFF02 (速度):', protocolCharacteristics.speed || '未找到') + console.log(' 0xFF03 (方向):', protocolCharacteristics.direction || '未找到') + console.log(' 0xFF04 (定时):', protocolCharacteristics.timer || '未找到') + console.log(' 0xFF05 (剩余):', protocolCharacteristics.remaining || '未找到') + console.log(' 0xFF06 (全部):', protocolCharacteristics.allProps || '未找到') } - // 返回蓝牙信息(用于跳转) + // 返回蓝牙信息(用于跳转),包含所有协议特征值 return { serviceId: mainServiceId, characteristicId: writeCharacteristicId, - notifyCharacteristicId: notifyCharacteristicId + notifyCharacteristicId: notifyCharacteristicId, + // 🔧 添加所有协议特征值的128位UUID + statusCharId: protocolCharacteristics.status, + speedCharId: protocolCharacteristics.speed, + directionCharId: protocolCharacteristics.direction, + timerCharId: protocolCharacteristics.timer, + remainingCharId: protocolCharacteristics.remaining, + allPropsCharId: protocolCharacteristics.allProps } } catch (err) { console.error('获取服务列表失败', err) @@ -989,9 +1036,6 @@ const getBLEDeviceCharacteristicsSync = (deviceId, serviceId) => { }) } -// 预览控制页面(模拟设备数据) - - // 构造跳转到 mixer 页面的 URL(带蓝牙信息) const buildMixerPageUrl = (device, bleInfo) => { const params = { @@ -1010,6 +1054,28 @@ const buildMixerPageUrl = (device, bleInfo) => { params.notifyCharacteristicId = bleInfo.notifyCharacteristicId } + // 🔧 添加所有协议特征值的128位UUID + if (bleInfo && bleInfo.statusCharId) { + params.statusCharId = bleInfo.statusCharId + } + if (bleInfo && bleInfo.speedCharId) { + params.speedCharId = bleInfo.speedCharId + } + if (bleInfo && bleInfo.directionCharId) { + params.directionCharId = bleInfo.directionCharId + } + if (bleInfo && bleInfo.timerCharId) { + params.timerCharId = bleInfo.timerCharId + } + if (bleInfo && bleInfo.remainingCharId) { + params.remainingCharId = bleInfo.remainingCharId + } + if (bleInfo && bleInfo.allPropsCharId) { + params.allPropsCharId = bleInfo.allPropsCharId + } + + console.log('🔗 跳转参数:', params) + // 拼接 URL const queryString = Object.keys(params) .map(key => `${key}=${params[key]}`) @@ -1065,7 +1131,7 @@ const onBLECharacteristicValueChange = () => { // 显示接收到的数据 uni.showToast({ - title: `收到数据: ${displayData.slice(0, 20)}${displayData.length > 20 ? '...' : ''}`, + title: `Received Data: ${displayData.slice(0, 20)}${displayData.length > 20 ? '...' : ''}`, icon: 'none', duration: 2000 }) @@ -1076,7 +1142,7 @@ const onBLECharacteristicValueChange = () => { const writeBLEData = async (data) => { if (!connectedDeviceId.value) { uni.showToast({ - title: '请先连接设备', + title: 'Please Connect Device First', icon: 'none' }) return @@ -1084,14 +1150,14 @@ const writeBLEData = async (data) => { if (!services.value || services.value.length === 0) { uni.showToast({ - title: '设备服务未就绪,请稍后重试', + title: 'Device Service Not Ready, Please Retry Later', icon: 'none' }) return } uni.showLoading({ - title: '正在发送数据...', + title: 'Sending Data...', mask: true }) @@ -1129,8 +1195,8 @@ const writeBLEData = async (data) => { if (!writeCharacteristic) { uni.showModal({ - title: '无法发送', - content: '该设备没有可写入的特征值\n\n可能原因:\n1. 设备不支持写入操作\n2. 设备固件限制\n\n建议:查看设备文档或联系设备厂商', + title: 'Unable to Send', + content: 'This device has no writable characteristic\n\nPossible reasons:\n1. Device does not support write operation\n2. Device firmware restriction\n\nSuggestion: Check device documentation or contact manufacturer', showCancel: false }) return @@ -1148,8 +1214,8 @@ const writeBLEData = async (data) => { // 检查数据长度(蓝牙一般限制在20字节以内) if (buffer.byteLength > 20) { uni.showModal({ - title: '数据过长', - content: `数据长度${buffer.byteLength}字节,超过蓝牙限制(20字节)\n\n是否分包发送?`, + title: 'Data Too Long', + content: `Data length ${buffer.byteLength} bytes exceeds Bluetooth limit (20 bytes)\n\nSend in chunks?`, success: async (res) => { if (res.confirm) { await sendDataInChunks(data, writeServiceId, writeCharacteristic.uuid) @@ -1172,7 +1238,7 @@ const writeBLEData = async (data) => { addToHistory('send', data, hexString) uni.showToast({ - title: `发送成功: ${data}`, + title: `Send Success: ${data}`, icon: 'success', duration: 2000 }) @@ -1180,26 +1246,26 @@ const writeBLEData = async (data) => { fail: (err) => { console.error('写入数据失败', err) - let errorMsg = '发送失败' + let errorMsg = 'Send Failed' let errorDetail = '' if (err.errCode === 10008) { - errorMsg = '发送失败:数据格式错误' - errorDetail = '请检查输入的数据格式' + errorMsg = 'Send Failed: Data Format Error' + errorDetail = 'Please check input data format' } else if (err.errCode === 10007) { - errorMsg = '发送失败:特征值不支持写入' - errorDetail = '该特征值不支持写入操作' + errorMsg = 'Send Failed: Characteristic Not Writable' + errorDetail = 'This characteristic does not support write operation' } else if (err.errCode === 10006) { - errorMsg = '发送失败:设备连接已断开' - errorDetail = '请重新连接设备' + errorMsg = 'Send Failed: Device Connection Lost' + errorDetail = 'Please reconnect device' } else if (err.errMsg) { - errorMsg = '发送失败' + errorMsg = 'Send Failed' errorDetail = err.errMsg } uni.showModal({ title: errorMsg, - content: errorDetail + `\n\n错误码: ${err.errCode || '未知'}`, + content: errorDetail + `\n\nError Code: ${err.errCode || 'Unknown'}`, showCancel: false }) } @@ -1208,7 +1274,7 @@ const writeBLEData = async (data) => { uni.hideLoading() console.error('发送数据异常', err) uni.showToast({ - title: '发送失败', + title: 'Send Failed', icon: 'none', duration: 2000 }) @@ -1230,7 +1296,7 @@ const sendDataInChunks = async (data, serviceId, characteristicId) => { console.log(`数据分为 ${chunks.length} 包发送`) uni.showLoading({ - title: `发送中 0/${chunks.length}`, + title: `Sending 0/${chunks.length}`, mask: true }) @@ -1245,7 +1311,7 @@ const sendDataInChunks = async (data, serviceId, characteristicId) => { success: () => { console.log(`第 ${i + 1}/${chunks.length} 包发送成功`) uni.showLoading({ - title: `发送中 ${i + 1}/${chunks.length}`, + title: `Sending ${i + 1}/${chunks.length}`, mask: true }) resolve() @@ -1269,7 +1335,7 @@ const sendDataInChunks = async (data, serviceId, characteristicId) => { addToHistory('send', data, hexString) uni.showToast({ - title: `分包发送成功 (${chunks.length}包)`, + title: `Chunked Send Success (${chunks.length} chunks)`, icon: 'success', duration: 2000 }) @@ -1277,7 +1343,7 @@ const sendDataInChunks = async (data, serviceId, characteristicId) => { uni.hideLoading() console.error('分包发送失败', err) uni.showToast({ - title: '分包发送失败', + title: 'Chunked Send Failed', icon: 'none', duration: 2000 }) @@ -1288,7 +1354,7 @@ const sendDataInChunks = async (data, serviceId, characteristicId) => { const readBLEData = async () => { if (!connectedDeviceId.value || services.value.length === 0) { uni.showToast({ - title: '请先连接设备', + title: 'Please Connect Device First', icon: 'none' }) return @@ -1305,7 +1371,7 @@ const readBLEData = async () => { // 执行读取操作 const performRead = async () => { uni.showLoading({ - title: '正在读取数据...', + title: 'Reading Data...', mask: true }) @@ -1350,7 +1416,7 @@ const performRead = async () => { if (totalReadable === 0) { uni.showToast({ - title: '该设备没有可读特征值\n请等待设备推送数据', + title: 'No Readable Characteristic\nWait for Device Push Data', icon: 'none', duration: 2500 }) @@ -1374,7 +1440,7 @@ const performRead = async () => { uni.hideLoading() console.error('读取数据失败', err) uni.showToast({ - title: '读取数据失败', + title: 'Read Data Failed', icon: 'none', duration: 2000 }) @@ -1456,7 +1522,7 @@ const onBLEConnectionStateChange = () => { if (!res.connected && res.deviceId === connectedDeviceId.value) { cleanupConnection() uni.showToast({ - title: '设备已断开', + title: 'Device Disconnected', icon: 'none' }) } @@ -1470,7 +1536,7 @@ const sendCustomData = () => { if (!trimmedData) { uni.showToast({ - title: '请输入数据', + title: 'Please Input Data', icon: 'none' }) return @@ -1700,22 +1766,6 @@ page { } } -.preview-button-wrapper { - padding: 0 20rpx; - margin-bottom: 20rpx; - - :deep(.u-button) { - width: 100%; - height: 80rpx; - border-radius: 16rpx; - font-size: 28rpx; - font-weight: 600; - display: flex; - align-items: center; - justify-content: center; - } -} - .device-list { background: rgba(255, 255, 255, 0.95); margin: 20rpx; diff --git a/src/pages/mixer/mixer.vue b/src/pages/mixer/mixer.vue index 00159d1..62f1f35 100644 --- a/src/pages/mixer/mixer.vue +++ b/src/pages/mixer/mixer.vue @@ -75,7 +75,7 @@ backgroundColor="#e5e7eb" block-size="20" /> - 400 + 100 @@ -155,8 +155,22 @@ const connected = ref(false) const deviceId = ref('') const deviceName = ref('') const serviceId = ref('') // 蓝牙服务 UUID -const characteristicId = ref('') // 蓝牙特征值 UUID -const notifyCharacteristicId = ref('') // 监听特征值 UUID(用于接收数据) + +// 🔧 修改:支持多个特征值UUID +const characteristicIds = ref({ + write: '', // 通用可写特征值(从bluetooth.vue传递) + notify: '', // 通知特征值(从bluetooth.vue传递) + status: '0xFF01', // 电机状态 + speed: '0xFF02', // 电机速度 + direction: '0xFF03', // 运行方向 + timer: '0xFF04', // 定时设置 + remaining: '0xFF05', // 剩余时间 + allProps: '0xFF06' // 所有属性 +}) + +// ⚠️ 兼容旧代码,保留单个characteristicId(指向通用可写特征值) +const characteristicId = computed(() => characteristicIds.value.write) +const notifyCharacteristicId = computed(() => characteristicIds.value.notify) // 通道选择 const selectedChannels = ref([]) @@ -180,7 +194,7 @@ const channels = ref([ status: 'stopped', // running, stopped, timing mode: 'number', // color, number currentValue: '0-400', - runValue: 60, + runValue: 0, controlType: 'speed', // speed, status timerMode: 'Timer', timerValue: 'Not Set', @@ -193,12 +207,12 @@ const channels = ref([ status: 'stopped', mode: 'number', currentValue: '0-400', - runValue: 65, + runValue: 0, controlType: 'speed', timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, - timerHours: 0 + timerMinutes: 0 }, { id: 'CH3', @@ -206,12 +220,12 @@ const channels = ref([ status: 'stopped', mode: 'number', currentValue: '0-400', - runValue: 70, + runValue: 0, controlType: 'speed', timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, - timerHours: 0 + timerMinutes: 0 }, { id: 'CH4', @@ -219,12 +233,12 @@ const channels = ref([ status: 'stopped', mode: 'number', currentValue: '0-400', - runValue: 75, + runValue: 0, controlType: 'speed', timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, - timerHours: 0 + timerMinutes: 0 }, { id: 'CH5', @@ -232,12 +246,12 @@ const channels = ref([ status: 'stopped', mode: 'number', currentValue: '0-400', - runValue: 80, + runValue: 0, controlType: 'speed', timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, - timerHours: 0 + timerMinutes: 0 }, { id: 'CH6', @@ -245,12 +259,12 @@ const channels = ref([ status: 'stopped', mode: 'number', currentValue: '0-400', - runValue: 50, + runValue: 0, controlType: 'speed', timerMode: 'Timer', timerValue: 'Not Set', remainingTime: 0, - timerHours: 0 + timerMinutes: 0 } ]) @@ -263,15 +277,97 @@ onLoad((options) => { // 获取蓝牙服务和特征值信息 if (options.serviceId) { + // ✅ 直接使用从蓝牙设备获取的原始 UUID(重要!) + // 不要转换格式,保持设备返回的原始格式(可能是128位或16位) serviceId.value = options.serviceId + + console.log('✅ 接收到 ServiceID:', options.serviceId) + console.log(' UUID 长度:', options.serviceId.length, '字符') + console.log(' UUID 格式:', options.serviceId.length > 10 ? '128位完整格式' : '16位短格式') + + // 检查是否包含协议规定的 00FF 标识 + const upperServiceId = options.serviceId.toUpperCase() + if (upperServiceId.includes('00FF') || upperServiceId.includes('0XFF')) { + console.log('✅ ServiceID 符合协议(包含 00FF)') + } else { + console.warn('⚠️ ServiceID 可能不符合协议规范') + console.warn(' 期望包含: 00FF 或 0XFF') + console.warn(' 实际接收:', options.serviceId) + } + } else { + // 如果没有传递 serviceId,使用默认值(注意:这可能导致写入失败) + serviceId.value = '0x00FF' + console.error('❌ 未接收到 serviceId,使用默认值可能无法正常通信') + console.error(' 建议:检查蓝牙连接页面是否正确传递了 serviceId') } + + // 🔧 接收多个特征值UUID if (options.characteristicId) { - characteristicId.value = options.characteristicId + // 通用可写特征值(从bluetooth.vue传递) + characteristicIds.value.write = options.characteristicId + console.log('✅ 接收到通用可写特征值:', options.characteristicId) + } else { + console.warn('⚠️ 未接收到通用可写特征值') } + if (options.notifyCharacteristicId) { - notifyCharacteristicId.value = options.notifyCharacteristicId + // 通知特征值(从bluetooth.vue传递) + characteristicIds.value.notify = options.notifyCharacteristicId + console.log('✅ 接收到通知特征值:', options.notifyCharacteristicId) } + // 🔧 接收所有协议特征值的128位UUID(优先使用设备返回的完整UUID) + if (options.statusCharId) { + characteristicIds.value.status = options.statusCharId + console.log('✅ 接收到状态特征值(0xFF01):', options.statusCharId) + } + + if (options.speedCharId) { + characteristicIds.value.speed = options.speedCharId + console.log('✅ 接收到速度特征值(0xFF02):', options.speedCharId) + } + + if (options.directionCharId) { + characteristicIds.value.direction = options.directionCharId + console.log('✅ 接收到方向特征值(0xFF03):', options.directionCharId) + } + + if (options.timerCharId) { + characteristicIds.value.timer = options.timerCharId + console.log('✅ 接收到定时特征值(0xFF04):', options.timerCharId) + } + + if (options.remainingCharId) { + characteristicIds.value.remaining = options.remainingCharId + console.log('✅ 接收到剩余时间特征值(0xFF05):', options.remainingCharId) + } + + if (options.allPropsCharId) { + characteristicIds.value.allProps = options.allPropsCharId + console.log('✅ 接收到所有属性特征值(0xFF06):', options.allPropsCharId) + } + + // 检查是否所有特征值都已接收 + const hasAllCharIds = options.statusCharId && options.speedCharId && options.timerCharId && options.allPropsCharId + if (!hasAllCharIds) { + console.warn('⚠️ 部分协议特征值未接收,将使用默认16位UUID兼容') + console.warn(' 如果设备只支持128位UUID,可能造成通信失败') + } + + // 打印完整的蓝牙配置信息 + console.log('📱 ========== 蓝牙配置信息 ==========') + console.log(' deviceId:', deviceId.value) + console.log(' deviceName:', deviceName.value) + console.log(' serviceId:', serviceId.value, `(${serviceId.value?.length || 0} chars)`) + console.log(' 通用可写特征值:', characteristicIds.value.write, `(${characteristicIds.value.write?.length || 0} chars)`) + console.log(' 通知特征值:', characteristicIds.value.notify) + console.log(' 电机状态特征值:', characteristicIds.value.status) + console.log(' 电机速度特征值:', characteristicIds.value.speed) + console.log(' 定时设置特征值:', characteristicIds.value.timer) + console.log(' 剩余时间特征值:', characteristicIds.value.remaining) + console.log(' 所有属性特征值:', characteristicIds.value.allProps) + console.log('=======================================') + // 初始化蓝牙通信 initBluetoothCommunication() } @@ -393,15 +489,22 @@ const showDeviceInfo = () => { const updateRunValue = (channelId, value) => { const channel = channels.value.find(c => c.id === channelId) if (channel) { - channel.runValue = value - saveChannelData(channelId) + // ✅ 不立即修改状态,只发送命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新速度 sendBluetoothCommand('setRunValue', channelId, value) + + // 显示发送中提示 + uni.showToast({ + title: `${channel.name} Speed Command Sent`, + icon: 'none', + duration: 1000 + }) } } -// 计算实际转速(0-100 映射到 0-400) +// 计算实际转速(直接显示 0-100) const calculateSpeed = (value) => { - return Math.round(value * 4) + return Math.round(value) } // 设置定时 @@ -427,21 +530,14 @@ const setTimer = (channelId) => { return } - channel.timerMinutes = minutes - channel.remainingTime = minutes * 60 // Convert to seconds (minutes*60) - - if (minutes > 0) { - channel.timerValue = `Set ${minutes} min` - } else { - channel.timerValue = 'Not Set' - } - - saveChannelData(channelId) + // ✅ 不立即修改状态,只发送命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新定时设置 sendBluetoothCommand('setTimer', channelId, minutes) uni.showToast({ - title: `Timer Set Successfully`, - icon: 'success' + title: `${channel.name} Timer Command Sent`, + icon: 'none', + duration: 1500 }) } } @@ -462,20 +558,15 @@ const handleChannelStop = (channelId) => { const 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' - } - + // ✅ 不立即修改状态,只发送命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新状态 sendBluetoothCommand('start', channelId) + + // 显示发送中提示 uni.showToast({ - title: `${channel.name} Started`, - icon: 'success' + title: `${channel.name} Start Command Sent`, + icon: 'none', + duration: 1500 }) } } @@ -484,12 +575,15 @@ const startChannel = (channelId) => { const stopChannel = (channelId) => { const channel = channels.value.find(c => c.id === channelId) if (channel) { - channel.status = 'stopped' - stopTimer(channelId) + // ✅ 不立即修改状态,只发送命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新状态 sendBluetoothCommand('stop', channelId) + + // 显示发送中提示 uni.showToast({ - title: `${channel.name} Stopped`, - icon: 'success' + title: `${channel.name} Stop Command Sent`, + icon: 'none', + duration: 1500 }) } } @@ -511,27 +605,14 @@ const batchStart = () => { cancelText: 'Cancel', success: (res) => { if (res.confirm) { - // 更新本地状态 - selectedChannels.value.forEach(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' - } - } - }) - - // 批量发送蓝牙命令(一次性发送所有选中通道的状态) + // ✅ 不立即修改状态,只发送批量启动命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新状态 sendBluetoothCommand('batchStart', null, null) uni.showToast({ - title: `Started ${selectedChannels.value.length} channels`, - icon: 'success' + title: `Batch Start Sent (${selectedChannels.value.length} channels)`, + icon: 'none', + duration: 1500 }) } } @@ -555,21 +636,14 @@ const batchStop = () => { cancelText: 'Cancel', success: (res) => { if (res.confirm) { - // 更新本地状态 - selectedChannels.value.forEach(channelId => { - const channel = channels.value.find(c => c.id === channelId) - if (channel) { - channel.status = 'stopped' - stopTimer(channelId) - } - }) - - // 批量发送蓝牙命令(一次性发送所有选中通道的状态) + // ✅ 不立即修改状态,只发送批量停止命令 + // 等待蓝牙设备反馈后,通过 onBLECharacteristicValueChange 监听器更新状态 sendBluetoothCommand('batchStop', null, null) uni.showToast({ - title: `Stopped ${selectedChannels.value.length} channels`, - icon: 'success' + title: `Batch Stop Sent (${selectedChannels.value.length} channels)`, + icon: 'none', + duration: 1500 }) } } @@ -667,12 +741,14 @@ const sendBluetoothCommand = (command, channelId, value) => { case 'query': // 查询电机所有属性(0xFF06)- 通过读取实现 - readMotorAllProperties() + // value 参数控制是否显示确认框:true=显示,false/undefined=不显示 + readMotorAllProperties(value === true) break case 'queryRemainingTime': // 查询电机定时剩余时间(0xFF05) - readMotorRemainingTime() + // value 参数控制是否显示确认框:true=显示,false/undefined=不显示 + readMotorRemainingTime(value === true) break } } catch (error) { @@ -707,7 +783,11 @@ const writeMotorStatus = (channelIndex, status, isBatch = false, batchChannels = dataView[i * 2 + 1] = ch.status // 状态值(0=初始化, 1=停止, 2=定时, 3=运行) }) - writeBLEData('0xFF01', buffer) + // 📝 使用专用的状态特征值UUID(0xFF01) + // 优先使用协议规定的特征值,如果设备返回了完整UUID则使用完整格式 + const writeCharId = characteristicIds.value.status || characteristicIds.value.write || '0xFF01' + console.log('📝 写入电机状态,使用特征值:', writeCharId) + writeBLEData(writeCharId, buffer) } // 写入电机速度(0xFF02) - 格式:电机序号 + 速度值 @@ -738,7 +818,10 @@ const writeMotorSpeed = (channelIndex, speed, isBatch = false, batchChannels = [ dataView[i * 2 + 1] = Math.min(100, Math.max(0, ch.speed)) // 速度值(0-100) }) - writeBLEData('0xFF02', buffer) + // 📝 使用专用的速度特征值UUID(0xFF02) + const writeCharId = characteristicIds.value.speed || characteristicIds.value.write || '0xFF02' + console.log('📝 写入电机速度,使用特征值:', writeCharId) + writeBLEData(writeCharId, buffer) } // 写入电机定时设置(0xFF04) - 格式:电机序号 + 定时时间(小端序) @@ -752,59 +835,323 @@ const writeMotorTimer = (channelIndex, minutes) => { dataView[1] = minutes & 0xFF // 定时时间低位 dataView[2] = (minutes >> 8) & 0xFF // 定时时间高位 - writeBLEData('0xFF04', buffer) + // 📝 使用专用的定时特征值UUID(0xFF04) + const writeCharId = characteristicIds.value.timer || characteristicIds.value.write || '0xFF04' + console.log('📝 写入电机定时,使用特征值:', writeCharId) + writeBLEData(writeCharId, buffer) } // 通用 BLE 写入函数 const writeBLEData = (characteristicUUID, buffer) => { - console.log(`Writing to ${characteristicUUID}, ${buffer.byteLength} bytes`) + // ⚠️ 关键诊断点:检查所有参数是否有效 + console.log('========== writeBLEData 调用诊断 ==========') + console.log('1. deviceId:', deviceId.value, '是否有效:', !!deviceId.value) + console.log('2. serviceId:', serviceId.value, '是否有效:', !!serviceId.value, '长度:', serviceId.value?.length) + console.log('3. characteristicUUID:', characteristicUUID, '是否有效:', !!characteristicUUID, '长度:', characteristicUUID?.length) + console.log('4. buffer 大小:', buffer?.byteLength, 'bytes') + console.log('5. 连接状态:', connected.value ? '已连接' : '❌ 未连接') - uni.writeBLECharacteristicValue({ - deviceId: deviceId.value, - serviceId: serviceId.value, - characteristicId: characteristicUUID, - value: buffer, + // 验证必需参数 + if (!deviceId.value) { + console.error('❌ 错误:deviceId 为空,无法写入数据') + uni.showModal({ + title: '写入失败', + content: 'deviceId 为空,请重新连接设备', + showCancel: false + }) + return + } + + if (!serviceId.value) { + console.error('❌ 错误:serviceId 为空,无法写入数据') + uni.showModal({ + title: '写入失败', + content: 'serviceId 为空,请检查蓝牙连接', + showCancel: false + }) + return + } + + if (!characteristicUUID) { + console.error('❌ 错误:characteristicUUID 为空,无法写入数据') + uni.showModal({ + title: '写入失败', + content: '特征值UUID为空,请检查蓝牙配置', + showCancel: false + }) + return + } + + // 将 buffer 转换为可读格式用于显示 + const dataView = new Uint8Array(buffer) + const hexArray = Array.from(dataView).map(b => '0x' + b.toString(16).toUpperCase().padStart(2, '0')) + const decArray = Array.from(dataView) + + // 构建详细的数据信息 + const dataInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +(${serviceId.value?.length} chars) + +Characteristic: ${characteristicUUID} +(${characteristicUUID?.length} chars) + +Data Length: ${buffer.byteLength} bytes + +Hex: +${hexArray.join(' ')} + +Decimal: +${decArray.join(', ')} + +Binary: +${Array.from(dataView).map(b => b.toString(2).padStart(8, '0')).join(' ')}` + + console.log(`📤 准备发送蓝牙数据:\n${dataInfo}`) + console.log('==========================================') + + // 显示确认对话框 + uni.showModal({ + title: 'Confirm Send BLE Data', + content: dataInfo, + confirmText: 'Send', + cancelText: 'Cancel', success: (res) => { - console.log(`✅ Successfully wrote to ${characteristicUUID}`) - }, - fail: (err) => { - console.error(`❌ Failed to write to ${characteristicUUID}:`, err) - uni.showToast({ - title: 'Send Failed', - icon: 'none' - }) + if (res.confirm) { + // 用户确认,执行真正的发送 + console.log(`✅ 用户确认发送,开始写入数据到 ${characteristicUUID}`) + + uni.writeBLECharacteristicValue({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: characteristicUUID, + value: buffer, + success: (res) => { + console.log(`✅ Successfully wrote to ${characteristicUUID}`) + uni.showToast({ + title: 'Send Success', + icon: 'success', + duration: 1500 + }) + }, + fail: (err) => { + console.error(`❌ Failed to write to ${characteristicUUID}:`, err) + + // 构建错误信息 + const errorInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +Characteristic: ${characteristicUUID} + +Error Code: ${err.errCode || 'Unknown'} +Error Message: ${err.errMsg || 'Unknown error'} + +Full Error: +${JSON.stringify(err, null, 2)}` + + console.error('发送失败详情:', errorInfo) + + // 弹出详细错误信息 + uni.showModal({ + title: 'Send Failed', + content: errorInfo, + showCancel: false, + confirmText: 'OK' + }) + } + }) + } else { + // 用户取消 + console.log('❌ 用户取消发送') + uni.showToast({ + title: 'Send Cancelled', + icon: 'none', + duration: 1000 + }) + } } }) } // 读取电机所有属性(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) - } - }) +// showConfirm: 是否显示确认对话框(初始化时显示,定时查询时不显示) +const readMotorAllProperties = (showConfirm = false) => { + // 📝 使用专用的所有属性特征值UUID(0xFF06) + const readCharId = characteristicIds.value.allProps || '0xFF06' + + const executeRead = () => { + uni.readBLECharacteristicValue({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: readCharId, + success: (res) => { + console.log(`✅ Successfully read all properties from ${readCharId}`) + if (showConfirm) { + uni.showToast({ + title: 'Read Success', + icon: 'success', + duration: 1500 + }) + } + }, + fail: (err) => { + console.error(`❌ Failed to read all properties from ${readCharId}:`, err) + if (showConfirm) { + // 构建错误信息 + const errorInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +Characteristic: ${readCharId} + +Error Code: ${err.errCode || 'Unknown'} +Error Message: ${err.errMsg || 'Unknown error'} + +Full Error: +${JSON.stringify(err, null, 2)}` + + console.error('读取失败详情:', errorInfo) + + // 弹出详细错误信息 + uni.showModal({ + title: 'Read Failed', + content: errorInfo, + showCancel: false, + confirmText: 'OK' + }) + } + } + }) + } + + if (showConfirm) { + // 显示确认对话框 + const readInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +Characteristic: ${readCharId} +Operation: Read Data +Description: Read all motor properties + +Query data: +- Status of 6 channels +- Speed of 6 channels +- Direction of 6 channels +- Timer settings of 6 channels +- Remaining time of 6 channels + +Expected: 42 bytes` + + console.log(`📥 准备读取蓝牙数据:\n${readInfo}`) + + uni.showModal({ + title: 'Confirm Read BLE Data', + content: readInfo, + confirmText: 'Read', + cancelText: 'Cancel', + success: (res) => { + if (res.confirm) { + console.log('✅ 用户确认读取,开始读取所有属性') + executeRead() + } else { + console.log('❌ 用户取消读取') + uni.showToast({ + title: 'Read Cancelled', + icon: 'none', + duration: 1000 + }) + } + } + }) + } else { + // 静默读取(不显示确认框) + executeRead() + } } // 读取电机定时剩余时间(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) - } - }) +// showConfirm: 是否显示确认对话框(初始化时显示,定时查询时不显示) +const readMotorRemainingTime = (showConfirm = false) => { + // 📝 使用专用的剩余时间特征值UUID(0xFF05) + const readCharId = characteristicIds.value.remaining || '0xFF05' + + const executeRead = () => { + uni.readBLECharacteristicValue({ + deviceId: deviceId.value, + serviceId: serviceId.value, + characteristicId: readCharId, + success: (res) => { + console.log(`✅ Successfully read remaining time from ${readCharId}`) + if (showConfirm) { + uni.showToast({ + title: 'Read Success', + icon: 'success', + duration: 1500 + }) + } + }, + fail: (err) => { + console.error(`❌ Failed to read remaining time from ${readCharId}:`, err) + if (showConfirm) { + // 构建错误信息 + const errorInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +Characteristic: ${readCharId} + +Error Code: ${err.errCode || 'Unknown'} +Error Message: ${err.errMsg || 'Unknown error'} + +Full Error: +${JSON.stringify(err, null, 2)}` + + console.error('读取失败详情:', errorInfo) + + // 弹出详细错误信息 + uni.showModal({ + title: 'Read Failed', + content: errorInfo, + showCancel: false, + confirmText: 'OK' + }) + } + } + }) + } + + if (showConfirm) { + // 显示确认对话框 + const readInfo = `Device ID: ${deviceId.value} +Service ID: ${serviceId.value} +Characteristic: ${readCharId} +Operation: Read Data +Description: Read timer remaining time + +Query data: +- Remaining time of 6 channels (minutes) + +Expected: 12 bytes +Format: 2 bytes per channel (little-endian)` + + console.log(`📥 准备读取蓝牙数据:\n${readInfo}`) + + uni.showModal({ + title: 'Confirm Read BLE Data', + content: readInfo, + confirmText: 'Read', + cancelText: 'Cancel', + success: (res) => { + if (res.confirm) { + console.log('✅ 用户确认读取,开始读取剩余时间') + executeRead() + } else { + console.log('❌ 用户取消读取') + uni.showToast({ + title: 'Read Cancelled', + icon: 'none', + duration: 1000 + }) + } + } + }) + } else { + // 静默读取(不显示确认框) + executeRead() + } } // 注:sendSinglePacket 和 sendMultiplePackets 已被新的协议方式替代,保留作为备用 @@ -839,6 +1186,25 @@ const initBluetoothCommunication = () => { return } + // 设置蓝牙 MTU 大小为 500 + uni.setBLEMTU({ + deviceId: deviceId.value, + mtu: 500, + success: (res) => { + console.log('✅ 设置蓝牙 MTU 成功:', res.mtu) + uni.showToast({ + title: `MTU Set: ${res.mtu}`, + icon: 'success', + duration: 1500 + }) + }, + fail: (err) => { + console.warn('⚠️ 设置蓝牙 MTU 失败:', err) + // MTU 设置失败不影响正常使用,只是传输效率可能降低 + console.log('将使用默认 MTU 大小') + } + }) + // 监听蓝牙设备连接状态 uni.onBLEConnectionStateChange((res) => { console.log('蓝牙连接状态变化:', res) @@ -857,62 +1223,111 @@ const initBluetoothCommunication = () => { }) // 监听特征值变化(接收数据)- 启用所有相关特征值的通知 + // 🔧 使用 characteristicIds 中定义的特征值UUID const characteristicsToNotify = [ - { id: '0xFF01', name: '电机状态' }, - { id: '0xFF02', name: '电机速度' }, - { id: '0xFF03', name: '运行方向' }, - { id: '0xFF04', name: '定时设置' }, - { id: '0xFF05', name: '剩余时间' }, - { id: '0xFF06', name: '所有属性' } + { id: characteristicIds.value.status, name: '电机状态' }, + { id: characteristicIds.value.speed, name: '电机速度' }, + { id: characteristicIds.value.direction, name: '运行方向' }, + { id: characteristicIds.value.timer, name: '定时设置' }, + { id: characteristicIds.value.remaining, name: '剩余时间' }, + { id: characteristicIds.value.allProps, name: '所有属性' } ] + console.log('🔔 准备监听的特征值:') + characteristicsToNotify.forEach(char => { + console.log(` ${char.name}: ${char.id}`) + }) + // 注册数据监听回调 uni.onBLECharacteristicValueChange((res) => { - console.log('收到蓝牙数据:', res) + console.log('📡 收到蓝牙数据:', res) + console.log(' 特征值UUID:', res.characteristicId) // 根据特征值ID处理数据 const dataView = new Uint8Array(res.value) const dataLength = dataView.length const charId = res.characteristicId + console.log(' 数据长度:', dataLength, 'bytes') + console.log(' HEX:', Array.from(dataView).map(b => '0x' + b.toString(16).toUpperCase().padStart(2, '0')).join(' ')) + + // 🔧 灵活匹配特征值ID(支持16位和128位UUID) + const matchCharacteristic = (target) => { + const upperCharId = charId.toUpperCase() + const upperTarget = target.toUpperCase() + // 完全匹配或包含匹配(兼容128位UUID) + return upperCharId === upperTarget || upperCharId.includes(upperTarget.replace('0X', '')) + } + if (dataLength === 6) { // 6字节数据,根据特征值ID区分 - if (charId === '0xFF01') { - // 电机状态 + if (matchCharacteristic(characteristicIds.value.status)) { + // 电机状态 - 根据设备反馈更新本地状态 + console.log('📊 解析为电机状态数据') 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) + const newStatus = statusMap[status] || 'stopped' + + // 状态变化时才处理定时器 + if (channel.status !== newStatus) { + console.log(`${channel.name} 状态变化: ${channel.status} -> ${newStatus}`) + + // 如果从非定时状态变为定时状态,启动定时器 + if (newStatus === 'timing' && channel.timerMinutes > 0) { + channel.remainingTime = channel.timerMinutes * 60 + startTimer(channel.id) + } + + // 如果从定时/运行状态变为停止状态,停止定时器 + if (newStatus === 'stopped' && (channel.status === 'timing' || channel.status === 'running')) { + stopTimer(channel.id) + } + + channel.status = newStatus + } }) - } else if (charId === '0xFF02') { - // 电机速度 + } else if (matchCharacteristic(characteristicIds.value.speed)) { + // 电机速度 - 根据设备反馈更新本地速度 + console.log('📊 解析为电机速度数据') channels.value.forEach((channel, index) => { const speed = dataView[index] - channel.runValue = speed - console.log(`${channel.name} 速度:`, speed) + + // 只在速度真正变化时才更新和保存 + if (channel.runValue !== speed) { + console.log(`${channel.name} 速度变化: ${channel.runValue} -> ${speed}`) + channel.runValue = speed + // 保存到本地存储 + saveChannelData(channel.id) + } }) - } else if (charId === '0xFF03') { + } else if (matchCharacteristic(characteristicIds.value.direction)) { // 运行方向 + console.log('📊 解析为运行方向数据') channels.value.forEach((channel, index) => { const direction = dataView[index] console.log(`${channel.name} 方向:`, direction === 0 ? '正转' : '反转') }) } else { // 未知特征值,使用智能判断 + console.log('⚠️ 未知特征值,使用智能判断') parseMotorStatusOrSpeed(dataView) } } else if (dataLength === 12) { // 12字节数据,根据特征值ID区分 - if (charId === '0xFF04') { + if (matchCharacteristic(characteristicIds.value.timer)) { + console.log('📊 解析为定时设置数据') parseMotorTimer(dataView, 'setup') // 定时设置 - } else if (charId === '0xFF05') { + } else if (matchCharacteristic(characteristicIds.value.remaining)) { + console.log('📊 解析为剩余时间数据') parseMotorTimer(dataView, 'remaining') // 剩余时间 } else { + console.log('⚠️ 未知特征值,使用智能判断') parseMotorTimer(dataView, 'unknown') // 未知,智能判断 } } else if (dataLength === 42) { // 42字节数据 - 所有属性 + console.log('📊 解析为所有属性数据') parseMotorAllProperties(dataView) } else { // 其他长度数据,使用通用处理 @@ -932,7 +1347,9 @@ const initBluetoothCommunication = () => { console.log(`启用 ${char.name}(${char.id}) 数据监听成功`) if (index === characteristicsToNotify.length - 1) { hasRegisteredListener.value = true - // 启动定时查询 + // 初始化时查询一次数据(显示确认框) + queryInitialData() + // 启动定时查询(不显示确认框) startDataQuery() } }, @@ -989,14 +1406,35 @@ const parseMotorStatusOrSpeed = (dataView) => { 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) + const newStatus = statusMap[value] || 'stopped' + + // 状态变化时才处理定时器 + if (channel.status !== newStatus) { + console.log(`${channel.name} 状态变化: ${channel.status} -> ${newStatus}`) + + // 如果从非定时状态变为定时状态,启动定时器 + if (newStatus === 'timing' && channel.timerMinutes > 0) { + channel.remainingTime = channel.timerMinutes * 60 + startTimer(channel.id) + } + + // 如果从定时/运行状态变为停止状态,停止定时器 + if (newStatus === 'stopped' && (channel.status === 'timing' || channel.status === 'running')) { + stopTimer(channel.id) + } + + channel.status = newStatus + } } else { - // 认为是电机速度(0-100) - channel.runValue = value - console.log(`${channel.name} 速度:`, value) + // 认为是电机速度(0-100)- 根据设备反馈更新本地速度 + if (channel.runValue !== value) { + console.log(`${channel.name} 速度变化: ${channel.runValue} -> ${value}`) + channel.runValue = value + // 保存到本地存储 + saveChannelData(channel.id) + } } }) } @@ -1014,10 +1452,14 @@ const parseMotorTimer = (dataView, type = 'unknown') => { 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, '分钟') + // 明确是定时设置(0xFF04)- 根据设备反馈更新本地定时设置 + if (channel.timerMinutes !== minutes) { + console.log(`${channel.name} 定时设置变化: ${channel.timerMinutes} -> ${minutes} 分钟`) + channel.timerMinutes = minutes + channel.timerValue = minutes > 0 ? `Set ${minutes} min` : 'Not Set' + // 保存到本地存储 + saveChannelData(channel.id) + } } else { // 未知类型,根据当前状态智能判断(兼容旧逻辑) if (channel.status === 'timing' && channel.remainingTime > 0) { @@ -1025,10 +1467,14 @@ const parseMotorTimer = (dataView, type = 'unknown') => { channel.remainingTime = minutes * 60 console.log(`${channel.name} 剩余时间(推测):`, minutes, '分钟') } else { - // 否则当作定时设置 - channel.timerMinutes = minutes - channel.timerValue = `Set ${minutes} min` - console.log(`${channel.name} 定时设置(推测):`, minutes, '分钟') + // 否则当作定时设置 - 根据设备反馈更新本地定时设置 + if (channel.timerMinutes !== minutes) { + console.log(`${channel.name} 定时设置变化(推测): ${channel.timerMinutes} -> ${minutes} 分钟`) + channel.timerMinutes = minutes + channel.timerValue = minutes > 0 ? `Set ${minutes} min` : 'Not Set' + // 保存到本地存储 + saveChannelData(channel.id) + } } } } @@ -1040,24 +1486,46 @@ const parseMotorAllProperties = (dataView) => { // 格式:6字节状态 + 6字节速度 + 6字节方向 + 12字节定时设置 + 12字节剩余时间 channels.value.forEach((channel, index) => { - // 电机状态(0-5字节) + // 电机状态(0-5字节)- 根据设备反馈更新本地状态 const status = dataView[index] const statusMap = { 0: 'stopped', 1: 'stopped', 2: 'timing', 3: 'running' } - channel.status = statusMap[status] || 'stopped' + const newStatus = statusMap[status] || 'stopped' - // 电机速度(6-11字节) + // 状态变化时才处理定时器 + if (channel.status !== newStatus) { + // 如果从非定时状态变为定时状态,启动定时器 + if (newStatus === 'timing' && channel.timerMinutes > 0) { + channel.remainingTime = channel.timerMinutes * 60 + startTimer(channel.id) + } + + // 如果从定时/运行状态变为停止状态,停止定时器 + if (newStatus === 'stopped' && (channel.status === 'timing' || channel.status === 'running')) { + stopTimer(channel.id) + } + + channel.status = newStatus + } + + // 电机速度(6-11字节)- 根据设备反馈更新本地速度 const speed = dataView[6 + index] - channel.runValue = speed + if (channel.runValue !== speed) { + channel.runValue = speed + // 保存到本地存储 + saveChannelData(channel.id) + } // 电机方向(12-17字节) const direction = dataView[12 + index] // 可以根据需要存储方向信息 - // 定时设置(18-29字节) + // 定时设置(18-29字节)- 根据设备反馈更新本地定时设置 const timerMinutes = dataView[18 + index * 2] | (dataView[18 + index * 2 + 1] << 8) - if (timerMinutes > 0) { + if (channel.timerMinutes !== timerMinutes) { channel.timerMinutes = timerMinutes - channel.timerValue = `Set ${timerMinutes} min` + channel.timerValue = timerMinutes > 0 ? `Set ${timerMinutes} min` : 'Not Set' + // 保存到本地存储 + saveChannelData(channel.id) } // 剩余时间(30-41字节) @@ -1070,17 +1538,29 @@ const parseMotorAllProperties = (dataView) => { // 注:updateChannelFromDevice 已被新的协议解析方式替代 -// 启动定时查询所有通道数据 +// 初始化查询数据(显示确认框) +const queryInitialData = () => { + console.log('🚀 初始化查询所有通道数据') + + // 查询完整属性(显示确认框) + setTimeout(() => { + sendBluetoothCommand('query', null, true) // 第三个参数 true 表示显示确认框 + }, 100) +} + +// 启动定时查询所有通道数据(静默模式,不显示确认框) const startDataQuery = () => { // 如果已经在查询,先停止 if (dataQueryTimer.value) { stopDataQuery() } - console.log('启动定时查询通道数据') + console.log('启动定时查询通道数据(静默模式)') - // 立即查询一次 - queryAllChannelsData() + // 延迟 3 秒后开始第一次自动查询(避免与初始化查询冲突) + setTimeout(() => { + queryAllChannelsData() + }, 3000) // 每 5 秒查询一次(降低频率,避免蓝牙拥堵) dataQueryTimer.value = setInterval(() => { @@ -1098,6 +1578,7 @@ const stopDataQuery = () => { } // 查询所有通道数据(包括状态、速度、剩余时间等) +// 静默模式:不显示确认框 const queryAllChannelsData = () => { if (!connected.value || isQuerying.value) { return @@ -1105,14 +1586,14 @@ const queryAllChannelsData = () => { isQuerying.value = true - // 优先查询完整属性(0xFF06),包含所有信息 + // 优先查询完整属性(0xFF06),包含所有信息(不显示确认框) setTimeout(() => { - sendBluetoothCommand('query', null) + sendBluetoothCommand('query', null, false) // 第三个参数 false 表示不显示确认框 }, 0) - // 备选:单独查询剩余时间(0xFF05) + // 备选:单独查询剩余时间(0xFF05)(不显示确认框) setTimeout(() => { - sendBluetoothCommand('queryRemainingTime', null) + sendBluetoothCommand('queryRemainingTime', null, false) // 第三个参数 false 表示不显示确认框 }, 500) // 查询完成后重置标志