- 实现了通过 SPI PING 命令获取 GD32 应用程序和引导加载程序版本信息 - 添加 /api/version HTTP 端点返回固件版本、编译时间和 Git 哈希 - 在系统状态 API 中包含 GD32 版本信息和存活状态 - 优化 SPI PING 协议解析以支持版本数据提取 refactor(ota): 移除压缩功能并简化 OTA 上传流程 - 移除 OTA 数据压缩和解压缩功能,简化数据处理流程 - 删除 utils/ota_compressor.cpp 文件及相关依赖 - 直接写入原始数据块而不是解压缩后写入 - 更新 OTA 上传处理器以处理原始二进制数据 build(cmakelists): 添加版本头文件自动生成机制 - 配置 CMake 构建时自动生成 version.h 头文件 - 集成 git 哈希和编译时间信息到固件版本 - 更新主 CMakeLists.txt 以包含版本生成目标 - 修改 app_config.h 以使用自动生成的版本字符串 fix(wifi_ota): 修复 OTA 中继状态管理和 SPI 通信问题 - 修复 OTA 中继完成后立即重置的问题,增加 3 秒延迟让前端轮询完成 - 修正 SPI CRC 错误检测和调试信息输出 - 优化 SPI PING 通信超时和重试机制 - 修复 OTA 上传大小计算和数据写入偏移跟踪
147 lines
5.2 KiB
C++
147 lines
5.2 KiB
C++
/*
|
|
* ota_relay.h - OTA 中继核心状态机
|
|
* T2.3, T3.1 - T3.7
|
|
*
|
|
* OtaRelay 类封装 OTA 中继状态机、Flash 存储和元数据管理。
|
|
*/
|
|
#ifndef OTA_RELAY_H
|
|
#define OTA_RELAY_H
|
|
|
|
#include <stdint.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
#include "transport/protocol.h"
|
|
#include "transport/spi_master.h"
|
|
#include "esp_partition.h"
|
|
|
|
#define OTA_META_NVS_NAMESPACE "ota_meta"
|
|
#define OTA_META_NVS_KEY "meta"
|
|
|
|
typedef struct {
|
|
uint32_t total_size;
|
|
uint32_t stored_size;
|
|
uint32_t last_seq;
|
|
uint32_t chunk_count;
|
|
uint8_t checksum[32];
|
|
} ota_meta_t;
|
|
|
|
typedef enum {
|
|
RELAY_IDLE = 0,
|
|
RELAY_HANDSHAKE_IDLE,
|
|
RELAY_HANDSHAKE_SENT,
|
|
RELAY_CHALLENGE_SENT,
|
|
RELAY_CHALLENGE_RESPOND,
|
|
RELAY_SESSION_KEY_SENT,
|
|
RELAY_AUTH_COMPLETE,
|
|
RELAY_SIZE_SENT,
|
|
RELAY_SIZE_ACK,
|
|
RELAY_RECEIVING,
|
|
RELAY_RELAY_ACTIVE,
|
|
RELAY_PACKET_SENT,
|
|
RELAY_PACKET_ACK_OK,
|
|
RELAY_PACKET_ACK_RETRY,
|
|
RELAY_RECEIVE_DONE,
|
|
RELAY_TRANSFER_DONE,
|
|
RELAY_FINISH_SENT,
|
|
RELAY_DONE,
|
|
RELAY_APP_VERIFY,
|
|
RELAY_ERROR = 0xFF
|
|
} relay_state_t;
|
|
|
|
typedef enum {
|
|
OTA_RESULT_OK = 0,
|
|
OTA_RESULT_ERR_TIMEOUT = -1,
|
|
OTA_RESULT_ERR_CRC = -2,
|
|
OTA_RESULT_ERR_AUTH = -3,
|
|
OTA_RESULT_ERR_STORAGE = -4,
|
|
OTA_RESULT_ERR_GD32 = -5,
|
|
OTA_RESULT_ERR_HEALTH = -6,
|
|
OTA_RESULT_ERR_USER_ABORT = -7
|
|
} ota_result_t;
|
|
|
|
class OtaRelay {
|
|
public:
|
|
static OtaRelay& instance();
|
|
|
|
static void taskEntry(void *pvParameters);
|
|
ota_result_t startHandshake();
|
|
ota_result_t forwardFirmware();
|
|
relay_state_t getState();
|
|
void reset();
|
|
|
|
static esp_err_t flashWriteChunk(uint32_t offset, const uint8_t *data, size_t len);
|
|
static esp_err_t flashReadChunk(uint32_t offset, uint8_t *data, size_t len);
|
|
static esp_err_t metaLoad(ota_meta_t *meta);
|
|
static esp_err_t metaSave(const ota_meta_t *meta);
|
|
static esp_err_t metaUpdate(uint32_t stored_size, uint32_t last_seq);
|
|
static esp_err_t metaClear();
|
|
|
|
// 公开状态
|
|
static relay_state_t relayState;
|
|
static uint32_t relaySeq;
|
|
static uint32_t relayTotalPackets;
|
|
static volatile bool gd32AppAlive;
|
|
static gd32_version_t gd32Version;
|
|
static QueueHandle_t dispatchQueue;
|
|
static QueueHandle_t resultQueue;
|
|
|
|
// 实例状态(public,供状态机方法访问)
|
|
const esp_partition_t *m_gd32Partition = NULL;
|
|
session_ctx_t m_sessionCtx;
|
|
uint8_t m_nonceServer[16] = {0};
|
|
uint8_t m_kSession[16] = {0};
|
|
uint8_t m_gd32UidRuntime[12] = {0};
|
|
|
|
static constexpr size_t GD32_UID_LEN = 12;
|
|
static constexpr size_t GD32_CHALLENGE_RESPONSE_LEN = 36;
|
|
|
|
esp_err_t requestGd32AppEnterBootloader();
|
|
ota_result_t probeBootHandshake();
|
|
ota_result_t sendHandshakeReq();
|
|
ota_result_t sendNonceAndAuthenticate();
|
|
ota_result_t sendFirmwareSize(uint32_t size);
|
|
ota_result_t relayPacketsFromFlash();
|
|
ota_result_t finishOta();
|
|
|
|
static int aes128EcbEncryptBlockLocal(const uint8_t *key, const uint8_t *in, uint8_t *out);
|
|
static void logRespPayload(const char *stage, uint8_t resp_type, const uint8_t *payload, size_t len);
|
|
esp_err_t waitUntilNotWait(const char *stage, uint8_t *resp_type,
|
|
uint8_t *resp_payload, size_t resp_max,
|
|
size_t *resp_len, uint32_t timeout_ms,
|
|
uint32_t max_wait_rounds);
|
|
|
|
static bool isStructuredAckWithSeq(const uint8_t *payload, size_t len, uint32_t expected_seq);
|
|
static bool parseStructuredError(const uint8_t *payload, size_t len, uint32_t *seq_out, uint8_t *err_out);
|
|
static bool isHandshakeEchoAck(const uint8_t *payload, size_t len);
|
|
static bool isChallengePayloadAck(const uint8_t *payload, size_t len);
|
|
static bool isConfirmPlainAck(const uint8_t *payload, size_t len);
|
|
static bool isSizeSeq0Ack(const uint8_t *payload, size_t len);
|
|
static bool isFinishPlainAck(const uint8_t *payload, size_t len);
|
|
static bool isTransferDoneAck(const uint8_t *payload, size_t len);
|
|
static bool isStructuredAckWithPayload(const uint8_t *payload, size_t len);
|
|
|
|
static esp_err_t pollUntilExpectedAckShape(const char *stage,
|
|
uint8_t *resp_type,
|
|
uint8_t *resp_payload,
|
|
size_t resp_max,
|
|
size_t *resp_len,
|
|
uint32_t poll_timeout_ms,
|
|
uint32_t wait_rounds,
|
|
uint32_t maxExtraPolls,
|
|
bool (*matcher)(const uint8_t *, size_t));
|
|
static bool parseAndVerifyChallengePayload(const uint8_t *resp_payload, size_t resp_len,
|
|
uint8_t *out_k_session, uint8_t *out_uid);
|
|
static bool aes128EcbEncrypt48Bytes(const uint8_t *key, const uint8_t *in, uint8_t *out);
|
|
static void calculateCrc32WithKey(const uint8_t *key, const uint8_t *data48, uint8_t *output);
|
|
|
|
private:
|
|
OtaRelay() = default;
|
|
~OtaRelay() = default;
|
|
OtaRelay(const OtaRelay&) = delete;
|
|
OtaRelay& operator=(const OtaRelay&) = delete;
|
|
};
|
|
|
|
#endif // OTA_RELAY_H
|
|
|
|
|