feat(esp32): 添加多电机OTA中继功能并优化调试日志系统

- 新增多电机状态、版本和OTA中继监控模块(monitor/motor_state.cpp,
  monitor/motor_version.cpp, monitor/motor_ota_relay.cpp)
- 在CAN反馈任务中添加0xC5/0xC6/0xD2类型消息处理,支持多电机状态推送
- 重构调试日志系统,将一次性JSON分配改为512B栈缓冲流式发送,降低内存峰值
- 调试UART波特率从115200提升至30000
- 修改版本管理方式,从自动生version.h改为手动维护
- 更新VSCode设置以支持ESP-IDF开发环境配置
- 在OTA协调器中添加电机OTA互斥机制,防止并发冲突
- 完善中继状态机异常处理,添加远程取消通知机制
This commit is contained in:
小龙虾
2026-07-26 20:40:16 +08:00
parent 770ae14156
commit 2138a572ee
47 changed files with 6469 additions and 799 deletions
+51 -9
View File
@@ -458,7 +458,7 @@ ota_result_t OtaRelay::forwardFirmware()
if (meta.total_size == 0U || meta.stored_size < meta.total_size) {
DEBUG_ERR("OTA", "固件未完整存储 stored=%" PRIu32 " total=%" PRIu32,
meta.stored_size, meta.total_size);
reset();
abortRemoteAndReset(true); /* 顺带清上次远端半会话 */
return OTA_RESULT_ERR_STORAGE;
}
{
@@ -468,7 +468,7 @@ ota_result_t OtaRelay::forwardFirmware()
esp_err_t verify_err = Gd32FlashStore::instance().metaVerifyChecksum();
if (verify_err != ESP_OK) {
DEBUG_ERR("OTA", "固件完整性校验失败,中止转发");
reset();
abortRemoteAndReset(true);
return OTA_RESULT_ERR_CRC;
}
} else {
@@ -498,7 +498,8 @@ ota_result_t OtaRelay::forwardFirmware()
if (res != OTA_RESULT_OK) {
DEBUG_ERR("OTA", "中继失败: result=%d, state=%d", (int)res, (int)getState());
reset();
/* 必须通知 GD32 CANCEL,否则远端停在 WAITING_* 半会话 */
abortRemoteAndReset(true);
} else {
/* 成功路径:解除按铃抑制(reset 也会做,成功时状态机可能停在 DONE) */
SpiMaster::instance().setSuppressRingService(false);
@@ -547,6 +548,39 @@ void OtaRelay::reset()
DEBUG_INFO("OTA", "状态机已复位 → IDLE");
}
/**
* @brief 向 GD32 发送 OTA CANCEL,驱动远端 resetToIdle(含 CREDIT/缓冲清理)。
*
* 失败路径必须调用:本端 reset() 只清 ESP32 状态机,GD32 仍可能停在
* WAITING_RANDOM / WAITING_CONFIRM 等半会话,导致下次握手/进度错乱。
*/
esp_err_t OtaRelay::sendRemoteCancel()
{
uint8_t resp_type = 0;
uint8_t resp_payload[8] = {0};
size_t resp_len = 0;
/* payload 可空;GD32 CANCEL 分支无条件 resetToIdle 并 ACK 0x03 */
esp_err_t err = SpiMaster::instance().transact(
SPI_OTA_CMD_CANCEL, nullptr, 0,
&resp_type, resp_payload, sizeof(resp_payload), &resp_len,
1500U);
if (err != ESP_OK) {
DEBUG_WARN("OTA", "远端 CANCEL 发送失败: %s(本端仍复位)", esp_err_to_name(err));
return err;
}
DEBUG_INFO("OTA", "远端 CANCEL 已发送, resp t=0x%02X n=%zu", resp_type, resp_len);
return ESP_OK;
}
void OtaRelay::abortRemoteAndReset(bool notify_remote)
{
if (notify_remote) {
/* 中继失败时 suppress 可能仍为 true,CANCEL 需能走 SPI 事务 */
(void)sendRemoteCancel();
}
reset();
}
// ============================================================
// 内部工具函数(不变)
// ============================================================
@@ -965,8 +999,12 @@ ota_result_t OtaRelay::sendAndWaitAckShape(const char *stage, uint8_t cmd,
esp_err_t err = SpiMaster::instance().transact(cmd, payload, payload_len,
&resp_type, resp_payload, resp_max, resp_len, transact_timeout_ms);
logRespPayload(stage, resp_type, resp_payload, *resp_len);
err = pollUntilExpectedAckShape(stage, &resp_type, resp_payload, resp_max, resp_len,
poll_timeout_ms, wait_rounds, max_extra_polls, matcher);
/* transact 已超时/失败时勿再 pollUntil:否则 resp_type 仍为 0,会刷
* "unexpected type=0x00" 并二次空等,掩盖真实超时。 */
if (err == ESP_OK) {
err = pollUntilExpectedAckShape(stage, &resp_type, resp_payload, resp_max, resp_len,
poll_timeout_ms, wait_rounds, max_extra_polls, matcher);
}
if (err != ESP_OK || resp_type != SPI_OTA_RSP_ACK || !matcher(resp_payload, *resp_len)) {
DEBUG_ERR("OTA", "%s fail err=%d t=0x%02X n=%zu",
stage, err, resp_type, *resp_len);
@@ -1473,7 +1511,8 @@ ota_result_t OtaRelay::doWaitForAppAlive(uint32_t timeout_ms)
if (!getGd32AppAlive()) {
DEBUG_ERR("OTA", "GD32 APP 启动确认超时,OTA 失败");
reset();
/* APP 未起来时 CANCEL 可能无效,仍尽力清理 BOOT 半会话 */
abortRemoteAndReset(true);
return OTA_RESULT_ERR_GD32;
}
@@ -1581,16 +1620,19 @@ void OtaRelay::taskEntry(void *pvParameters)
xQueueSend(resultQueue, &result_msg, pdMS_TO_TICKS(100));
if (result == OTA_RESULT_OK) {
// 成功日志已在 doWaitForAppAlive 输出,此处不再重复
vTaskDelay(pdMS_TO_TICKS(3000));
// 成功日志已在 doWaitForAppAlive 输出,立即通知协调器置完成态
instance().reset(); /* 本端清会话;GD32 已跳 APP */
} else if (result == OTA_RESULT_ERR_TIMEOUT) {
DEBUG_ERR("OTA", "OTA超时中止 TIMEOUT=%d", (int)result);
/* forwardFirmware 失败路径已 abortRemoteAndReset;再调一次无害(二次 CANCEL) */
instance().abortRemoteAndReset(true);
} else if (result == OTA_RESULT_ERR_USER_ABORT) {
DEBUG_ERR("OTA", "OTA 用户中止 (USER_ABORT=%d)", (int)result);
instance().abortRemoteAndReset(true);
} else {
DEBUG_ERR("OTA", "OTA 转发失败,错误码: %d", (int)result);
instance().abortRemoteAndReset(true);
}
instance().reset();
// 通过 IRelayListener 接口回调通知协调器中继结束
listener = getRelayListener();
if (listener) listener->onRelayComplete(result == OTA_RESULT_OK);