diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index f24a39457..69afd6c7d 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -136,6 +136,11 @@ class BleManager extends GetxService { final _wakeupVoiceUpgradeProgress = 0.obs; // 唤醒语音升级进度 RxInt get wakeupVoiceUpgradeProgress => _wakeupVoiceUpgradeProgress; + + final _wakeupVoiceUpgradeStatus = + 0.obs; // 唤醒语音升级状态 0: 未升级 1: 升级中 2: 升级成功 3: 升级失败 + RxInt get wakeupVoiceUpgradeStatus => _wakeupVoiceUpgradeStatus; + // 订阅 StreamSubscription? _statusSubscription; StreamSubscription? _dataSubscription; @@ -224,9 +229,23 @@ class BleManager extends GetxService { Logger.info('lxm--处理唤醒语音升级进度事件: $event'); _wakeupVoiceUpgradeProgress.value = event['progress'] as int? ?? 0; + _wakeupVoiceUpgradeStatus.value = 1; // 升级中 Logger.info( 'lxm--唤醒语音升级进度: ${_wakeupVoiceUpgradeProgress.value}%'); } + if (event['type'] == 'wakeupVoiceUpgradeSuccess') { + // 唤醒语音升级状态 + Logger.info('lxm--处理唤醒语音升级状态事件: $event'); + _wakeupVoiceUpgradeStatus.value = 2; // 升级成功 + Logger.info('lxm--唤醒语音升级状态: ${_wakeupVoiceUpgradeStatus.value}'); + } + + if (event['type'] == 'wakeupVoiceUpgradeFailure') { + // 唤醒语音升级状态 + Logger.info('lxm--处理唤醒语音升级状态事件: $event'); + _wakeupVoiceUpgradeStatus.value = 3; // 升级失败 + Logger.info('lxm--唤醒语音升级状态: ${_wakeupVoiceUpgradeStatus.value}'); + } Logger.info('lxm--准备调用_processDeviceInfo处理设备信息'); // 处理设备信息 diff --git a/lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart b/lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart index 3934b4908..c316ea62d 100644 --- a/lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart +++ b/lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart @@ -89,6 +89,18 @@ class WakeupVoiceController extends GetxController { upgradingVoiceId.value = null; } }); + // 监听升级状态:仅当状态为“成功(2)”或“失败(3)”时弹出提示弹窗;升级中(1)或其他状态不提示 + ever(bleManager.wakeupVoiceUpgradeStatus, (s) { + // 升级状态字段未定义,暂存到日志 + Logger.info('BLE 升级状态码: $s'); + if (s == 2) { + // 升级成功 + Get.snackbar('成功', '唤醒语音升级成功'); + } else if (s == 3) { + // 升级失败 + Get.snackbar('错误', '唤醒语音升级失败'); + } + }); } catch (e) { Logger.warning('绑定BLE观察者失败: $e'); } diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt index 77c92bc80..3978b0cae 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt @@ -165,6 +165,14 @@ Log.d(TAG, "手动启动语音识别: ") // 处理升级进度 } + override fun onWakeupVoiceUpgradeSuccess() { + } + + override fun onWakeupVoiceUpgradeFailed() { + } + + + //============================================================================================ // AgentServiceListener 接口实现 //============================================================================================ diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt index 36cef590f..9436c1841 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt @@ -1198,7 +1198,12 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin { override fun onWakeupVoiceUpgradeProgressReceived(progress: Int) { // 处理升级进度 } -} + override fun onWakeupVoiceUpgradeSuccess() { + } + + override fun onWakeupVoiceUpgradeFailed() { + } +} \ No newline at end of file diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index 7238c700a..fafb5de43 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -59,6 +59,12 @@ object BleService { // 升级唤醒词进度相关回调 fun onWakeupVoiceUpgradeProgressReceived(progress: Int) + + // 升级唤醒词成功相关回调 + fun onWakeupVoiceUpgradeSuccess() + + // 升级唤醒词失败相关回调 + fun onWakeupVoiceUpgradeFailed() } // ====================================================================================================== @@ -81,7 +87,7 @@ object BleService { private var wakeupVoicesNotifyChar: BluetoothGattCharacteristic? = null //唤醒词通知特征 private var wakeupVoicesWriteChar: BluetoothGattCharacteristic? = null //唤醒词写入特征 private var audioChar: BluetoothGattCharacteristic? = null - private var callAudioChar: BluetoothGattCharacteristic? = null + private var callAudioChar: BluetoothGattCharacteristic? = null private var callWriteChar: BluetoothGattCharacteristic? = null var recordfile: RecordingFile? = null var recordfile1: RecordingFile? = null @@ -347,9 +353,10 @@ object BleService { * 检查是否已连接 */ override fun isConnected(): Boolean { + return bluetoothGatt != null && - connectionState.value == BleConst.STATE_CONNECTED && - wakeupVoicesWriteChar != null + connectionState.value == BleConst.STATE_CONNECTED && + wakeupVoicesWriteChar != null } /** @@ -373,6 +380,22 @@ object BleService { // 使用外部类的方法来分发升级进度,避免无限递归 this@BleService.notifyUpgradeProgressReceived(progress) } + + /** + * 通知升级成功 + */ + override fun notifyUpgradeSuccess() { + // 使用外部类的方法来分发升级成功,避免无限递归 + this@BleService.notifyUpgradeSuccess() + } + + /** + * 通知升级失败 + */ + override fun notifyUpgradeFailed() { + // 使用外部类的方法来分发升级失败,避免无限递归 + this@BleService.notifyUpgradeFailed() + } }) } @@ -925,8 +948,9 @@ object BleService { if (wakeupVoicesNotifyChar != null && wakeupVoicesWriteChar != null) { wakeupVoicesWriteChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE setupWakeupVoicesNotifications(g, wakeupVoicesNotifyChar) + Log.i(TAG, "liwei------- 唤醒词OTA服务特征已找到") }else{ - Log.w(TAG, "唤醒词OTA服务特征未找到") + Log.w(TAG, "liwei------- 唤醒词OTA服务特征未找到") } // 在服务发现完成后,请求最大MTU //requestMaxMtu(g) @@ -941,14 +965,14 @@ object BleService { // 音频特征数据 BleConst.RECEIVE_AUDIO_CHAR_UUID -> { - // 统计接收到的字节数 - bytesReceivedInCurrentSecond += data.size + // 统计接收到的字节数 + bytesReceivedInCurrentSecond += data.size processAudioData(data) } - BleConst.CALL_RECEIVE_AUDIO_CHAR_UUID -> { + BleConst.CALL_RECEIVE_AUDIO_CHAR_UUID -> { - // 统计接收到的字节数 - bytesReceivedInCurrentSecond += data.size + // 统计接收到的字节数 + bytesReceivedInCurrentSecond += data.size processAudioData(data) } // 通知特征数据(命令和控制) @@ -956,6 +980,10 @@ object BleService { Log.i(TAG, "收到通知特征数据(命令和控制)") // 判断数据类型 when { + // 测试:唤醒词OTA升级通知 (0x20-0x2F开头) + data.isNotEmpty() && data[0] == BleConst.DEVICE_RESPONSE_HEADER && data[1] >= 0x20 -> { + wakeupVoicesUpgradeSender.processDeviceNotification(data) + } // 设备响应 (0xBB开头) data.isNotEmpty() && data[0] == BleConst.DEVICE_RESPONSE_HEADER -> { commandSender.processDeviceResponse(data) @@ -974,7 +1002,7 @@ object BleService { } } BleConst.WakeupVoices_NOTIFY_UUID -> { - Log.i(TAG, "收到唤醒词OTA通知特征数据") + Log.i(TAG, "liwei----------- 收到唤醒词OTA通知特征数据") wakeupVoicesUpgradeSender.processDeviceNotification(data) } // 其他未知特征 @@ -1071,22 +1099,22 @@ object BleService { */ private fun setupWakeupVoicesNotifications(g: BluetoothGatt, characteristic: BluetoothGattCharacteristic?) : Boolean{ if (characteristic == null || characteristic.properties.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) { - Log.w(TAG, "唤醒词特征不支持通知功能: ${characteristic?.uuid}") + Log.w(TAG, "liwei----------- 唤醒词特征不支持通知功能: ${characteristic?.uuid}") return false } // 在本地启用特征通知监听 val localResult = g.setCharacteristicNotification(characteristic, true) if (!localResult) { - Log.e(TAG, "本地通知设置失败: ${characteristic.uuid}") + Log.e(TAG, "liwei----------- 本地通知设置失败: ${characteristic.uuid}") return false } - Log.d(TAG, "本地通知设置成功: ${characteristic.uuid}") + Log.d(TAG, "liwei----------- 本地通知设置成功: ${characteristic.uuid}") // 获取客户端特征配置描述符 val desc = characteristic.getDescriptor(BleConst.CLIENT_CHAR_CONFIG_UUID) if (desc == null) { - Log.w(TAG, "未找到客户端配置描述符: ${characteristic.uuid}") + Log.w(TAG, "liwei----------- 未找到客户端配置描述符: ${characteristic.uuid}") return false } @@ -1096,7 +1124,7 @@ object BleService { // 向远程设备写入描述符(异步操作,结果在onDescriptorWrite回调中处理) val writeResult = g.writeDescriptor(desc) if (writeResult) { - Log.d(TAG, "开始写入通知描述符: ${characteristic.uuid}") + Log.d(TAG, "liwei----------- 开始写入通知描述符: ${characteristic.uuid}") } else { Log.e(TAG, "通知描述符写入请求失败: ${characteristic.uuid}") } @@ -1767,6 +1795,9 @@ object BleService { } } + /** + * 向所有回调监听器分发升级进度 + */ private fun notifyUpgradeProgressReceived(progress: Int) { for (callback in callbacks) { try { @@ -1777,6 +1808,31 @@ object BleService { } } + /** + * 向所有回调监听器分发升级成功 + */ + private fun notifyUpgradeSuccess() { + for (callback in callbacks) { + try { + callback.onWakeupVoiceUpgradeSuccess() + } catch (e: Exception) { + Log.e(TAG, "分发升级成功回调异常", e) + } + } + } + /** + * 向所有回调监听器分发升级失败 + */ + private fun notifyUpgradeFailed() { + for (callback in callbacks) { + try { + callback.onWakeupVoiceUpgradeFailed() + } catch (e: Exception) { + Log.e(TAG, "分发升级失败回调异常", e) + } + } + } + /** * 获取PHY类型的字符串描述 */ diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt index 322925cca..0ef8ddcbe 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt @@ -410,4 +410,25 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware, // 发送升级进度事件 sendEvent(statusEventSink, resultMap, "发送升级进度异常") } + + + override fun onWakeupVoiceUpgradeSuccess() { + // 升级成功通过状态通道发送 + val resultMap = mapOf( + "type" to "wakeupVoiceUpgradeSuccess" + ) + + // 发送升级成功事件 + sendEvent(statusEventSink, resultMap, "发送升级成功异常") + } + + override fun onWakeupVoiceUpgradeFailed() { + // 升级失败通过状态通道发送 + val resultMap = mapOf( + "type" to "wakeupVoiceUpgradeFailed" + ) + + // 发送升级失败事件 + sendEvent(statusEventSink, resultMap, "发送升级失败异常") + } } diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt index 01db4ac60..569fc7aa9 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt @@ -79,6 +79,16 @@ class BleWakeupVoicesUpgradeSender { * @param progress 升级进度(0-100) */ fun notifyUpgradeProgressReceived(progress: Int) + + /** + * 通知升级成功 + */ + fun notifyUpgradeSuccess() + + /** + * 通知升级失败 + */ + fun notifyUpgradeFailed() } @@ -115,33 +125,39 @@ class BleWakeupVoicesUpgradeSender { fun startWakeupVoiceUpgrade(bytes: ByteArray): Boolean { if (isUpgrading) { - Log.e(TAG, "OTA启动失败: 正在升级中") + Log.e(TAG, "liwei----------- OTA启动失败: 正在升级中") return false } val cb = this.callback ?: run { - Log.e(TAG, "OTA启动失败: 回调接口未设置") + Log.e(TAG, "liwei----------- OTA启动失败: 回调接口未设置") return false } if (!cb.isConnected()) { - Log.e(TAG, "OTA启动失败: 设备未连接") + Log.e(TAG, "liwei----------- OTA启动失败: 设备未连接") + return false + } + if (bytes.isEmpty()) { + Log.e(TAG, "liwei----------- OTA启动失败: 数据为空") return false } - // 初始化状态 otaBytes = bytes - otaTotalPackets = if (bytes.isEmpty()) 0 else ((bytes.size + MAX_DATA_PER_PACKET - 1) / MAX_DATA_PER_PACKET) + otaTotalPackets = ((bytes.size + MAX_DATA_PER_PACKET - 1) / MAX_DATA_PER_PACKET) currentSeq = 1 // 序号按文档使用1基准 handshakeAcked = false isUpgrading = true - // 构建并发送握手包: 0xAA 0xBB 0xCC 0x08 0x02 [总包数(2byte)] CRC - val totalHi = ((otaTotalPackets shr 8) and 0xFF).toByte() - val totalLo = (otaTotalPackets and 0xFF).toByte() - val payload = byteArrayOf(totalHi, totalLo) - val handshake = buildWakeupPacket(0x08, payload) - + // 构建并发送握手包: 0xAA 0xBB 0xCC 0x20 0x04 [文件大小(4byte)] CRC + val size = bytes.size + val b3 = ((size shr 24) and 0xFF).toByte() + val b2 = ((size shr 16) and 0xFF).toByte() + val b1 = ((size shr 8) and 0xFF).toByte() + val b0 = (size and 0xFF).toByte() + val payload = byteArrayOf(b3, b2, b1, b0) + val handshake = buildWakeupCmdPacket(0x20,0x04, payload) + Log.e(TAG, "liwei----------- OTA唤醒词升级: size:${size} payload:${payload.joinToString(", ") { "0x%02X".format(it) }} otaTotalPackets:$otaTotalPackets") return sendRawPacket(handshake) } @@ -185,84 +201,92 @@ class BleWakeupVoicesUpgradeSender { fun processDeviceNotification(data: ByteArray) { Log.d( TAG, - "收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" + "liwei------------- 收到唤醒词升级回应数据: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" ) if (data.size < 4) { - Log.e(TAG, "OTA通知/响应数据格式错误:数据长度过短") + Log.e(TAG, "Oliwei------------- TA通知/响应数据格式错误:数据长度过短") return } val header = data[0].toInt() and 0xFF val cmd = data[1].toInt() and 0xFF - val length = data[2].toInt() and 0xFF + // val length = data[2].toInt() and 0xFF - if (data.size != length + 4) { - Log.e(TAG, "OTA通知长度不匹配: 期望=${length + 4}, 实际=${data.size}") - return - } + // if (data.size != length + 4) { + // Log.e(TAG, "liwei------------- OTA通知长度不匹配: 期望=${length + 4}, 实际=${data.size}") + // return + // } // 校验CRC val crcPayload = data.sliceArray(0 until data.size - 1) val calc = calculateCrc8Maxim(crcPayload, crcPayload.size).toInt() and 0xFF val recv = data[data.size - 1].toInt() and 0xFF if (calc != recv) { - Log.e(TAG, "OTA通知CRC校验失败: 计算=$calc, 接收=$recv") + Log.e(TAG, "liwei------------- OTA通知CRC校验失败: 计算=$calc, 接收=$recv") return } // 握手/数据包响应都是以0xBB为头 if (header == (BleConst.DEVICE_RESPONSE_HEADER.toInt() and 0xFF)) { when (cmd) { - 0x08 -> { // 握手确认: 0xBB 0x08 0x03 [总包数(2byte)] [0x01] CRC - if (length < 3) { - Log.e(TAG, "握手响应长度错误: $length") - return - } - val totalHi = data[3].toInt() and 0xFF - val totalLo = data[4].toInt() and 0xFF - val status = data[5].toInt() and 0xFF - val totalFromDevice = (totalHi shl 8) or totalLo - Log.d(TAG, "握手响应: total=$totalFromDevice, status=$status") + 0x20 -> { // 握手确认: 0xBB + 0x20 + 0x05 + OTA包总字节数(4Byte) + 0x01 + CRC + Log.d(TAG, "liwei------------- 握手响应成功") + handshakeAcked = true + } + 0x21 -> { // 握手确认: 0xBB + 0x21 + 0x01 + 0x01 + CRC + val status = data[3].toInt() and 0xFF if (status == 0x01) { + Log.d(TAG, "liwei------------- 耳机UBOOT升级完: status=$status") handshakeAcked = true // 开始发送第一包 sendNextDataPacket() } else { - Log.e(TAG, "握手失败,状态=$status") - isUpgrading = false + Log.e(TAG, "liwei------------- 耳机UBOOT升级失败: status=$status") } + } - 0x09 -> { // 数据包确认: 0xBB 0x09 0x03 [序号(2byte)] [0x01] CRC - if (length < 3) { - Log.e(TAG, "数据包响应长度错误: $length") - return - } + 0x22 -> { + // 数据包确认: 0xBB + 0x22 + 0x03 + OTA包序号(2Byte) + 0x01 + CRC 成功 + // 数据包确认: 0xBB + 0x22 + 0x03 + OTA包序号(2Byte) + 0x00 + CRC 失败 val seqHi = data[3].toInt() and 0xFF val seqLo = data[4].toInt() and 0xFF val status = data[5].toInt() and 0xFF val ackSeq = (seqHi shl 8) or seqLo - Log.d(TAG, "数据包确认: seq=$ackSeq, status=$status") - if (status == 0x01) { + Log.d(TAG, "liwei------------- 数据包确认: seq=$ackSeq, status=$status") + if (status == 0x01) { //成功 发送下一包 // 更新进度(1基准) val progress = ((ackSeq.coerceAtMost(otaTotalPackets)) * 100) / (otaTotalPackets.coerceAtLeast(1)) + Log.i(TAG, "liwei------------- OTA进度: $progress%") callback?.notifyUpgradeProgressReceived(progress) // 继续发送下一包 if (ackSeq >= otaTotalPackets) { - Log.i(TAG, "OTA完成,共发送${otaTotalPackets}包") + Log.i(TAG, "liwei------------- OTA完成,共发送${otaTotalPackets}包") callback?.notifyUpgradeProgressReceived(100) - isUpgrading = false + sendEndDataPacket() } else { currentSeq = ackSeq + 1 sendNextDataPacket() } - } else { - Log.e(TAG, "数据包确认失败,状态=$status") + } else { //失败重新发送当前包 + Log.e(TAG, "liwei------------- 数据包确认失败,状态=$status") // 可按需重试,这里简单结束 isUpgrading = false } } + 0x23 -> { + val status = data[3].toInt() and 0xFF + Log.d(TAG, "liwei------------- OTA结束确认: status=$status") + if (status == 0x01) { + Log.i(TAG, "liwei------------- OTA升级成功") + callback?.notifyUpgradeSuccess() + } else { + Log.e(TAG, "liwei------------- OTA升级失败") + callback?.notifyUpgradeFailed() + } + isUpgrading = false + } else -> { - Log.d(TAG, "收到未知OTA响应CMD=0x${cmd.toString(16)}") + Log.d(TAG, "liwei------------- 收到未知OTA响应CMD=0x${cmd.toString(16)}") } } return @@ -270,7 +294,7 @@ class BleWakeupVoicesUpgradeSender { // 设备主动上报(0xCC)可以忽略或保留原逻辑; OTA场景下无特殊处理 if (header == (BleConst.DEVICE_NOTIFY_HEADER.toInt() and 0xFF)) { - Log.d(TAG, "收到设备主动上报(OTA通道): CMD=0x${cmd.toString(16)} 长度=$length") + Log.d(TAG, "liwei------------- 收到设备主动上报(OTA通道): CMD=0x${cmd.toString(16)}") } } @@ -414,46 +438,75 @@ class BleWakeupVoicesUpgradeSender { return crc.toByte() } + + + // ========================================== // OTA封包/发送辅助 // ========================================== - private fun buildWakeupPacket(cmd: Int, payload: ByteArray): ByteArray { - val totalLen = 4 + payload.size + 1 // 帧头(3字节) + CMD(1) + LEN(1) + PAYLOAD + CRC(1) + private fun buildWakeupCmdPacket(cmd: Int,sub: Int, payload: ByteArray): ByteArray { + + val totalLen = 5 + payload.size + 1 // 帧头(3字节) + CMD(1) + LEN(1) + PAYLOAD + CRC(1) val packet = ByteArray(totalLen) packet[0] = BleConst.APP_REQUEST_HEADER // 0xAA packet[1] = BleConst.DEVICE_RESPONSE_HEADER // 0xBB packet[2] = BleConst.DEVICE_NOTIFY_HEADER // 0xCC packet[3] = (cmd and 0xFF).toByte() - packet[4] = (payload.size and 0xFF).toByte() + packet[4] = (sub and 0xFF).toByte() if (payload.isNotEmpty()) { System.arraycopy(payload, 0, packet, 5, payload.size) } val crc = calculateCrc8Maxim(packet.sliceArray(0 until packet.size - 1), packet.size - 1) packet[packet.size - 1] = crc + Log.e(TAG, "liwei----------- 封装升级包: cmd=$cmd, sub=$sub, payload=${payload.size}, crc=${crc.toInt() and 0xFF}") + return packet + } + + + // ========================================== + // OTA封包/发送辅助 + // ========================================== + private fun buildWakeupPacket(cmd: Int,payload: ByteArray): ByteArray { + + val totalLen = 5 + payload.size + 1 // 帧头(3字节) + CMD(1) + LEN(1) + PAYLOAD + CRC(1) + val packet = ByteArray(totalLen) + packet[0] = BleConst.APP_REQUEST_HEADER // 0xAA + packet[1] = BleConst.DEVICE_RESPONSE_HEADER // 0xBB + packet[2] = BleConst.DEVICE_NOTIFY_HEADER // 0xCC + packet[3] = (cmd and 0xFF).toByte() + if (payload.isNotEmpty()) { + System.arraycopy(payload, 0, packet, 4, payload.size) + } + val crc = calculateCrc8Maxim(packet.sliceArray(0 until packet.size - 1), packet.size - 1) + packet[packet.size - 1] = crc + Log.e(TAG, "liwei----------- 封装升级包: cmd=$cmd, payload=${payload.size}, crc=${crc.toInt() and 0xFF}") return packet } @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) private fun sendRawPacket(packet: ByteArray): Boolean { val cb = this.callback ?: run { - Log.e(TAG, "发送失败: 回调未设置") + Log.e(TAG, "liwei----------- 发送失败: 回调未设置") return false } if (!cb.isConnected()) { - Log.e(TAG, "发送失败: 未连接") + Log.e(TAG, "liwei----------- 发送失败: 未连接") return false } val writeChar = cb.getWriteCharacteristic() ?: run { - Log.e(TAG, "发送失败: 写特征为空") + Log.e(TAG, "liwei----------- 发送失败: 写特征为空") return false } val gatt = cb.getBluetoothGatt() ?: run { - Log.e(TAG, "发送失败: GATT为空") + Log.e(TAG, "liwei----------- 发送失败: GATT为空") return false } writeChar.value = packet val ok = gatt.writeCharacteristic(writeChar) if (ok) { + //打印一下发送数据包 + val hex = packet.joinToString(" ") { String.format("%02X", it) } + Log.d(TAG, "liwei----------- 发送数据包(${packet.size}B): $hex") isWaitingReply = false startReplyTimeout() } @@ -483,7 +536,17 @@ class BleWakeupVoicesUpgradeSender { payload[1] = seqLo payload[2] = dataLen.toByte() System.arraycopy(chunk, 0, payload, 3, chunk.size) - val packet = buildWakeupPacket(0x09, payload) + val packet = buildWakeupPacket(0x22,payload) + val ok = sendRawPacket(packet) + if (!ok) { + Log.e(TAG, "写入数据包失败: seq=$currentSeq") + isUpgrading = false + } + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + private fun sendEndDataPacket() { + val packet = buildWakeupCmdPacket(0x23,0x01, byteArrayOf()) val ok = sendRawPacket(packet) if (!ok) { Log.e(TAG, "写入数据包失败: seq=$currentSeq") diff --git a/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt b/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt index 93c63aed4..58e791daa 100644 --- a/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt +++ b/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt @@ -398,4 +398,11 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { override fun onWakeupVoiceUpgradeProgressReceived(progress: Int) { // 不处理升级进度 } + + override fun onWakeupVoiceUpgradeSuccess() { + } + + override fun onWakeupVoiceUpgradeFailed() { + } + }