Browse Source

上传唤醒词升级代码

newdev_shunjiawei
liwei1dao 10 months ago
parent
commit
aa763173af
  1. 19
      lib/data/services/ble_manager.dart
  2. 12
      lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart
  3. 8
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt
  4. 7
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  5. 86
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  6. 21
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt
  7. 167
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt
  8. 7
      local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt

19
lib/data/services/ble_manager.dart

@ -136,6 +136,11 @@ class BleManager extends GetxService {
final _wakeupVoiceUpgradeProgress = 0.obs; // 唤醒语音升级进度 final _wakeupVoiceUpgradeProgress = 0.obs; // 唤醒语音升级进度
RxInt get wakeupVoiceUpgradeProgress => _wakeupVoiceUpgradeProgress; RxInt get wakeupVoiceUpgradeProgress => _wakeupVoiceUpgradeProgress;
final _wakeupVoiceUpgradeStatus =
0.obs; // 唤醒语音升级状态 0: 未升级 1: 升级中 2: 升级成功 3: 升级失败
RxInt get wakeupVoiceUpgradeStatus => _wakeupVoiceUpgradeStatus;
// 订阅 // 订阅
StreamSubscription<dynamic>? _statusSubscription; StreamSubscription<dynamic>? _statusSubscription;
StreamSubscription<dynamic>? _dataSubscription; StreamSubscription<dynamic>? _dataSubscription;
@ -224,9 +229,23 @@ class BleManager extends GetxService {
Logger.info('lxm--处理唤醒语音升级进度事件: $event'); Logger.info('lxm--处理唤醒语音升级进度事件: $event');
_wakeupVoiceUpgradeProgress.value = _wakeupVoiceUpgradeProgress.value =
event['progress'] as int? ?? 0; event['progress'] as int? ?? 0;
_wakeupVoiceUpgradeStatus.value = 1; // 升级中
Logger.info( Logger.info(
'lxm--唤醒语音升级进度: ${_wakeupVoiceUpgradeProgress.value}%'); '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处理设备信息'); Logger.info('lxm--准备调用_processDeviceInfo处理设备信息');
// 处理设备信息 // 处理设备信息

12
lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart

@ -89,6 +89,18 @@ class WakeupVoiceController extends GetxController {
upgradingVoiceId.value = null; upgradingVoiceId.value = null;
} }
}); });
// 监听升级状态:仅当状态为“成功(2)”或“失败(3)”时弹出提示弹窗;升级中(1)或其他状态不提示
ever<int>(bleManager.wakeupVoiceUpgradeStatus, (s) {
// 升级状态字段未定义,暂存到日志
Logger.info('BLE 升级状态码: $s');
if (s == 2) {
// 升级成功
Get.snackbar('成功', '唤醒语音升级成功');
} else if (s == 3) {
// 升级失败
Get.snackbar('错误', '唤醒语音升级失败');
}
});
} catch (e) { } catch (e) {
Logger.warning('绑定BLE观察者失败: $e'); Logger.warning('绑定BLE观察者失败: $e');
} }

8
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 接口实现 // AgentServiceListener 接口实现
//============================================================================================ //============================================================================================

7
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 onWakeupVoiceUpgradeProgressReceived(progress: Int) {
// 处理升级进度 // 处理升级进度
} }
}
override fun onWakeupVoiceUpgradeSuccess() {
}
override fun onWakeupVoiceUpgradeFailed() {
}
}

86
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 onWakeupVoiceUpgradeProgressReceived(progress: Int)
// 升级唤醒词成功相关回调
fun onWakeupVoiceUpgradeSuccess()
// 升级唤醒词失败相关回调
fun onWakeupVoiceUpgradeFailed()
} }
// ====================================================================================================== // ======================================================================================================
@ -81,7 +87,7 @@ object BleService {
private var wakeupVoicesNotifyChar: BluetoothGattCharacteristic? = null //唤醒词通知特征 private var wakeupVoicesNotifyChar: BluetoothGattCharacteristic? = null //唤醒词通知特征
private var wakeupVoicesWriteChar: BluetoothGattCharacteristic? = null //唤醒词写入特征 private var wakeupVoicesWriteChar: BluetoothGattCharacteristic? = null //唤醒词写入特征
private var audioChar: BluetoothGattCharacteristic? = null private var audioChar: BluetoothGattCharacteristic? = null
private var callAudioChar: BluetoothGattCharacteristic? = null private var callAudioChar: BluetoothGattCharacteristic? = null
private var callWriteChar: BluetoothGattCharacteristic? = null private var callWriteChar: BluetoothGattCharacteristic? = null
var recordfile: RecordingFile? = null var recordfile: RecordingFile? = null
var recordfile1: RecordingFile? = null var recordfile1: RecordingFile? = null
@ -347,9 +353,10 @@ object BleService {
* 检查是否已连接 * 检查是否已连接
*/ */
override fun isConnected(): Boolean { override fun isConnected(): Boolean {
return bluetoothGatt != null && return bluetoothGatt != null &&
connectionState.value == BleConst.STATE_CONNECTED && connectionState.value == BleConst.STATE_CONNECTED &&
wakeupVoicesWriteChar != null wakeupVoicesWriteChar != null
} }
/** /**
@ -373,6 +380,22 @@ object BleService {
// 使用外部类的方法来分发升级进度,避免无限递归 // 使用外部类的方法来分发升级进度,避免无限递归
this@BleService.notifyUpgradeProgressReceived(progress) 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) { if (wakeupVoicesNotifyChar != null && wakeupVoicesWriteChar != null) {
wakeupVoicesWriteChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE wakeupVoicesWriteChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
setupWakeupVoicesNotifications(g, wakeupVoicesNotifyChar) setupWakeupVoicesNotifications(g, wakeupVoicesNotifyChar)
Log.i(TAG, "liwei------- 唤醒词OTA服务特征已找到")
}else{ }else{
Log.w(TAG, "唤醒词OTA服务特征未找到") Log.w(TAG, "liwei------- 唤醒词OTA服务特征未找到")
} }
// 在服务发现完成后,请求最大MTU // 在服务发现完成后,请求最大MTU
//requestMaxMtu(g) //requestMaxMtu(g)
@ -941,14 +965,14 @@ object BleService {
// 音频特征数据 // 音频特征数据
BleConst.RECEIVE_AUDIO_CHAR_UUID -> { BleConst.RECEIVE_AUDIO_CHAR_UUID -> {
// 统计接收到的字节数 // 统计接收到的字节数
bytesReceivedInCurrentSecond += data.size bytesReceivedInCurrentSecond += data.size
processAudioData(data) processAudioData(data)
} }
BleConst.CALL_RECEIVE_AUDIO_CHAR_UUID -> { BleConst.CALL_RECEIVE_AUDIO_CHAR_UUID -> {
// 统计接收到的字节数 // 统计接收到的字节数
bytesReceivedInCurrentSecond += data.size bytesReceivedInCurrentSecond += data.size
processAudioData(data) processAudioData(data)
} }
// 通知特征数据(命令和控制) // 通知特征数据(命令和控制)
@ -956,6 +980,10 @@ object BleService {
Log.i(TAG, "收到通知特征数据(命令和控制)") Log.i(TAG, "收到通知特征数据(命令和控制)")
// 判断数据类型 // 判断数据类型
when { when {
// 测试:唤醒词OTA升级通知 (0x20-0x2F开头)
data.isNotEmpty() && data[0] == BleConst.DEVICE_RESPONSE_HEADER && data[1] >= 0x20 -> {
wakeupVoicesUpgradeSender.processDeviceNotification(data)
}
// 设备响应 (0xBB开头) // 设备响应 (0xBB开头)
data.isNotEmpty() && data[0] == BleConst.DEVICE_RESPONSE_HEADER -> { data.isNotEmpty() && data[0] == BleConst.DEVICE_RESPONSE_HEADER -> {
commandSender.processDeviceResponse(data) commandSender.processDeviceResponse(data)
@ -974,7 +1002,7 @@ object BleService {
} }
} }
BleConst.WakeupVoices_NOTIFY_UUID -> { BleConst.WakeupVoices_NOTIFY_UUID -> {
Log.i(TAG, "收到唤醒词OTA通知特征数据") Log.i(TAG, "liwei----------- 收到唤醒词OTA通知特征数据")
wakeupVoicesUpgradeSender.processDeviceNotification(data) wakeupVoicesUpgradeSender.processDeviceNotification(data)
} }
// 其他未知特征 // 其他未知特征
@ -1071,22 +1099,22 @@ object BleService {
*/ */
private fun setupWakeupVoicesNotifications(g: BluetoothGatt, characteristic: BluetoothGattCharacteristic?) : Boolean{ private fun setupWakeupVoicesNotifications(g: BluetoothGatt, characteristic: BluetoothGattCharacteristic?) : Boolean{
if (characteristic == null || characteristic.properties.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) { if (characteristic == null || characteristic.properties.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
Log.w(TAG, "唤醒词特征不支持通知功能: ${characteristic?.uuid}") Log.w(TAG, "liwei----------- 唤醒词特征不支持通知功能: ${characteristic?.uuid}")
return false return false
} }
// 在本地启用特征通知监听 // 在本地启用特征通知监听
val localResult = g.setCharacteristicNotification(characteristic, true) val localResult = g.setCharacteristicNotification(characteristic, true)
if (!localResult) { if (!localResult) {
Log.e(TAG, "本地通知设置失败: ${characteristic.uuid}") Log.e(TAG, "liwei----------- 本地通知设置失败: ${characteristic.uuid}")
return false return false
} }
Log.d(TAG, "本地通知设置成功: ${characteristic.uuid}") Log.d(TAG, "liwei----------- 本地通知设置成功: ${characteristic.uuid}")
// 获取客户端特征配置描述符 // 获取客户端特征配置描述符
val desc = characteristic.getDescriptor(BleConst.CLIENT_CHAR_CONFIG_UUID) val desc = characteristic.getDescriptor(BleConst.CLIENT_CHAR_CONFIG_UUID)
if (desc == null) { if (desc == null) {
Log.w(TAG, "未找到客户端配置描述符: ${characteristic.uuid}") Log.w(TAG, "liwei----------- 未找到客户端配置描述符: ${characteristic.uuid}")
return false return false
} }
@ -1096,7 +1124,7 @@ object BleService {
// 向远程设备写入描述符(异步操作,结果在onDescriptorWrite回调中处理) // 向远程设备写入描述符(异步操作,结果在onDescriptorWrite回调中处理)
val writeResult = g.writeDescriptor(desc) val writeResult = g.writeDescriptor(desc)
if (writeResult) { if (writeResult) {
Log.d(TAG, "开始写入通知描述符: ${characteristic.uuid}") Log.d(TAG, "liwei----------- 开始写入通知描述符: ${characteristic.uuid}")
} else { } else {
Log.e(TAG, "通知描述符写入请求失败: ${characteristic.uuid}") Log.e(TAG, "通知描述符写入请求失败: ${characteristic.uuid}")
} }
@ -1767,6 +1795,9 @@ object BleService {
} }
} }
/**
* 向所有回调监听器分发升级进度
*/
private fun notifyUpgradeProgressReceived(progress: Int) { private fun notifyUpgradeProgressReceived(progress: Int) {
for (callback in callbacks) { for (callback in callbacks) {
try { 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类型的字符串描述 * 获取PHY类型的字符串描述
*/ */

21
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, "发送升级进度异常") 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, "发送升级失败异常")
}
} }

167
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeupVoicesUpgradeSender.kt

@ -79,6 +79,16 @@ class BleWakeupVoicesUpgradeSender {
* @param progress 升级进度(0-100) * @param progress 升级进度(0-100)
*/ */
fun notifyUpgradeProgressReceived(progress: Int) fun notifyUpgradeProgressReceived(progress: Int)
/**
* 通知升级成功
*/
fun notifyUpgradeSuccess()
/**
* 通知升级失败
*/
fun notifyUpgradeFailed()
} }
@ -115,33 +125,39 @@ class BleWakeupVoicesUpgradeSender {
fun startWakeupVoiceUpgrade(bytes: ByteArray): Boolean { fun startWakeupVoiceUpgrade(bytes: ByteArray): Boolean {
if (isUpgrading) { if (isUpgrading) {
Log.e(TAG, "OTA启动失败: 正在升级中") Log.e(TAG, "liwei----------- OTA启动失败: 正在升级中")
return false return false
} }
val cb = this.callback ?: run { val cb = this.callback ?: run {
Log.e(TAG, "OTA启动失败: 回调接口未设置") Log.e(TAG, "liwei----------- OTA启动失败: 回调接口未设置")
return false return false
} }
if (!cb.isConnected()) { if (!cb.isConnected()) {
Log.e(TAG, "OTA启动失败: 设备未连接") Log.e(TAG, "liwei----------- OTA启动失败: 设备未连接")
return false
}
if (bytes.isEmpty()) {
Log.e(TAG, "liwei----------- OTA启动失败: 数据为空")
return false return false
} }
// 初始化状态 // 初始化状态
otaBytes = bytes 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基准 currentSeq = 1 // 序号按文档使用1基准
handshakeAcked = false handshakeAcked = false
isUpgrading = true isUpgrading = true
// 构建并发送握手包: 0xAA 0xBB 0xCC 0x08 0x02 [总包数(2byte)] CRC // 构建并发送握手包: 0xAA 0xBB 0xCC 0x20 0x04 [文件大小(4byte)] CRC
val totalHi = ((otaTotalPackets shr 8) and 0xFF).toByte() val size = bytes.size
val totalLo = (otaTotalPackets and 0xFF).toByte() val b3 = ((size shr 24) and 0xFF).toByte()
val payload = byteArrayOf(totalHi, totalLo) val b2 = ((size shr 16) and 0xFF).toByte()
val handshake = buildWakeupPacket(0x08, payload) 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) return sendRawPacket(handshake)
} }
@ -185,84 +201,92 @@ class BleWakeupVoicesUpgradeSender {
fun processDeviceNotification(data: ByteArray) { fun processDeviceNotification(data: ByteArray) {
Log.d( Log.d(
TAG, TAG,
"收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" "liwei------------- 收到唤醒词升级回应数据: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}"
) )
if (data.size < 4) { if (data.size < 4) {
Log.e(TAG, "OTA通知/响应数据格式错误:数据长度过短") Log.e(TAG, "Oliwei------------- TA通知/响应数据格式错误:数据长度过短")
return return
} }
val header = data[0].toInt() and 0xFF val header = data[0].toInt() and 0xFF
val cmd = data[1].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) { // if (data.size != length + 4) {
Log.e(TAG, "OTA通知长度不匹配: 期望=${length + 4}, 实际=${data.size}") // Log.e(TAG, "liwei------------- OTA通知长度不匹配: 期望=${length + 4}, 实际=${data.size}")
return // return
} // }
// 校验CRC // 校验CRC
val crcPayload = data.sliceArray(0 until data.size - 1) val crcPayload = data.sliceArray(0 until data.size - 1)
val calc = calculateCrc8Maxim(crcPayload, crcPayload.size).toInt() and 0xFF val calc = calculateCrc8Maxim(crcPayload, crcPayload.size).toInt() and 0xFF
val recv = data[data.size - 1].toInt() and 0xFF val recv = data[data.size - 1].toInt() and 0xFF
if (calc != recv) { if (calc != recv) {
Log.e(TAG, "OTA通知CRC校验失败: 计算=$calc, 接收=$recv") Log.e(TAG, "liwei------------- OTA通知CRC校验失败: 计算=$calc, 接收=$recv")
return return
} }
// 握手/数据包响应都是以0xBB为头 // 握手/数据包响应都是以0xBB为头
if (header == (BleConst.DEVICE_RESPONSE_HEADER.toInt() and 0xFF)) { if (header == (BleConst.DEVICE_RESPONSE_HEADER.toInt() and 0xFF)) {
when (cmd) { when (cmd) {
0x08 -> { // 握手确认: 0xBB 0x08 0x03 [总包数(2byte)] [0x01] CRC 0x20 -> { // 握手确认: 0xBB + 0x20 + 0x05 + OTA包总字节数(4Byte) + 0x01 + CRC
if (length < 3) { Log.d(TAG, "liwei------------- 握手响应成功")
Log.e(TAG, "握手响应长度错误: $length") handshakeAcked = true
return }
} 0x21 -> { // 握手确认: 0xBB + 0x21 + 0x01 + 0x01 + CRC
val totalHi = data[3].toInt() and 0xFF val status = 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")
if (status == 0x01) { if (status == 0x01) {
Log.d(TAG, "liwei------------- 耳机UBOOT升级完: status=$status")
handshakeAcked = true handshakeAcked = true
// 开始发送第一包 // 开始发送第一包
sendNextDataPacket() sendNextDataPacket()
} else { } else {
Log.e(TAG, "握手失败,状态=$status") Log.e(TAG, "liwei------------- 耳机UBOOT升级失败: status=$status")
isUpgrading = false
} }
} }
0x09 -> { // 数据包确认: 0xBB 0x09 0x03 [序号(2byte)] [0x01] CRC 0x22 -> {
if (length < 3) { // 数据包确认: 0xBB + 0x22 + 0x03 + OTA包序号(2Byte) + 0x01 + CRC 成功
Log.e(TAG, "数据包响应长度错误: $length") // 数据包确认: 0xBB + 0x22 + 0x03 + OTA包序号(2Byte) + 0x00 + CRC 失败
return
}
val seqHi = data[3].toInt() and 0xFF val seqHi = data[3].toInt() and 0xFF
val seqLo = data[4].toInt() and 0xFF val seqLo = data[4].toInt() and 0xFF
val status = data[5].toInt() and 0xFF val status = data[5].toInt() and 0xFF
val ackSeq = (seqHi shl 8) or seqLo val ackSeq = (seqHi shl 8) or seqLo
Log.d(TAG, "数据包确认: seq=$ackSeq, status=$status") Log.d(TAG, "liwei------------- 数据包确认: seq=$ackSeq, status=$status")
if (status == 0x01) { if (status == 0x01) { //成功 发送下一包
// 更新进度(1基准) // 更新进度(1基准)
val progress = ((ackSeq.coerceAtMost(otaTotalPackets)) * 100) / (otaTotalPackets.coerceAtLeast(1)) val progress = ((ackSeq.coerceAtMost(otaTotalPackets)) * 100) / (otaTotalPackets.coerceAtLeast(1))
Log.i(TAG, "liwei------------- OTA进度: $progress%")
callback?.notifyUpgradeProgressReceived(progress) callback?.notifyUpgradeProgressReceived(progress)
// 继续发送下一包 // 继续发送下一包
if (ackSeq >= otaTotalPackets) { if (ackSeq >= otaTotalPackets) {
Log.i(TAG, "OTA完成,共发送${otaTotalPackets}包") Log.i(TAG, "liwei------------- OTA完成,共发送${otaTotalPackets}包")
callback?.notifyUpgradeProgressReceived(100) callback?.notifyUpgradeProgressReceived(100)
isUpgrading = false sendEndDataPacket()
} else { } else {
currentSeq = ackSeq + 1 currentSeq = ackSeq + 1
sendNextDataPacket() sendNextDataPacket()
} }
} else { } else { //失败重新发送当前包
Log.e(TAG, "数据包确认失败,状态=$status") Log.e(TAG, "liwei------------- 数据包确认失败,状态=$status")
// 可按需重试,这里简单结束 // 可按需重试,这里简单结束
isUpgrading = false 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 -> { else -> {
Log.d(TAG, "收到未知OTA响应CMD=0x${cmd.toString(16)}") Log.d(TAG, "liwei------------- 收到未知OTA响应CMD=0x${cmd.toString(16)}")
} }
} }
return return
@ -270,7 +294,7 @@ class BleWakeupVoicesUpgradeSender {
// 设备主动上报(0xCC)可以忽略或保留原逻辑; OTA场景下无特殊处理 // 设备主动上报(0xCC)可以忽略或保留原逻辑; OTA场景下无特殊处理
if (header == (BleConst.DEVICE_NOTIFY_HEADER.toInt() and 0xFF)) { 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() return crc.toByte()
} }
// ========================================== // ==========================================
// OTA封包/发送辅助 // OTA封包/发送辅助
// ========================================== // ==========================================
private fun buildWakeupPacket(cmd: Int, payload: ByteArray): ByteArray { private fun buildWakeupCmdPacket(cmd: Int,sub: Int, payload: ByteArray): ByteArray {
val totalLen = 4 + payload.size + 1 // 帧头(3字节) + CMD(1) + LEN(1) + PAYLOAD + CRC(1)
val totalLen = 5 + payload.size + 1 // 帧头(3字节) + CMD(1) + LEN(1) + PAYLOAD + CRC(1)
val packet = ByteArray(totalLen) val packet = ByteArray(totalLen)
packet[0] = BleConst.APP_REQUEST_HEADER // 0xAA packet[0] = BleConst.APP_REQUEST_HEADER // 0xAA
packet[1] = BleConst.DEVICE_RESPONSE_HEADER // 0xBB packet[1] = BleConst.DEVICE_RESPONSE_HEADER // 0xBB
packet[2] = BleConst.DEVICE_NOTIFY_HEADER // 0xCC packet[2] = BleConst.DEVICE_NOTIFY_HEADER // 0xCC
packet[3] = (cmd and 0xFF).toByte() packet[3] = (cmd and 0xFF).toByte()
packet[4] = (payload.size and 0xFF).toByte() packet[4] = (sub and 0xFF).toByte()
if (payload.isNotEmpty()) { if (payload.isNotEmpty()) {
System.arraycopy(payload, 0, packet, 5, payload.size) System.arraycopy(payload, 0, packet, 5, payload.size)
} }
val crc = calculateCrc8Maxim(packet.sliceArray(0 until packet.size - 1), packet.size - 1) val crc = calculateCrc8Maxim(packet.sliceArray(0 until packet.size - 1), packet.size - 1)
packet[packet.size - 1] = crc 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 return packet
} }
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
private fun sendRawPacket(packet: ByteArray): Boolean { private fun sendRawPacket(packet: ByteArray): Boolean {
val cb = this.callback ?: run { val cb = this.callback ?: run {
Log.e(TAG, "发送失败: 回调未设置") Log.e(TAG, "liwei----------- 发送失败: 回调未设置")
return false return false
} }
if (!cb.isConnected()) { if (!cb.isConnected()) {
Log.e(TAG, "发送失败: 未连接") Log.e(TAG, "liwei----------- 发送失败: 未连接")
return false return false
} }
val writeChar = cb.getWriteCharacteristic() ?: run { val writeChar = cb.getWriteCharacteristic() ?: run {
Log.e(TAG, "发送失败: 写特征为空") Log.e(TAG, "liwei----------- 发送失败: 写特征为空")
return false return false
} }
val gatt = cb.getBluetoothGatt() ?: run { val gatt = cb.getBluetoothGatt() ?: run {
Log.e(TAG, "发送失败: GATT为空") Log.e(TAG, "liwei----------- 发送失败: GATT为空")
return false return false
} }
writeChar.value = packet writeChar.value = packet
val ok = gatt.writeCharacteristic(writeChar) val ok = gatt.writeCharacteristic(writeChar)
if (ok) { if (ok) {
//打印一下发送数据包
val hex = packet.joinToString(" ") { String.format("%02X", it) }
Log.d(TAG, "liwei----------- 发送数据包(${packet.size}B): $hex")
isWaitingReply = false isWaitingReply = false
startReplyTimeout() startReplyTimeout()
} }
@ -483,7 +536,17 @@ class BleWakeupVoicesUpgradeSender {
payload[1] = seqLo payload[1] = seqLo
payload[2] = dataLen.toByte() payload[2] = dataLen.toByte()
System.arraycopy(chunk, 0, payload, 3, chunk.size) 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) val ok = sendRawPacket(packet)
if (!ok) { if (!ok) {
Log.e(TAG, "写入数据包失败: seq=$currentSeq") Log.e(TAG, "写入数据包失败: seq=$currentSeq")

7
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 onWakeupVoiceUpgradeProgressReceived(progress: Int) {
// 不处理升级进度 // 不处理升级进度
} }
override fun onWakeupVoiceUpgradeSuccess() {
}
override fun onWakeupVoiceUpgradeFailed() {
}
} }

Loading…
Cancel
Save