docs(readme): 更新调试日志与上位机通信说明
更新了 README.md 中关于调试日志与上位机通信的部分,详细说明了当 前通信架构分为两类: 1. 业务通信:HTTP 接口用于 WebUI 与 ESP32 通信 2. 调试镜像:UART0 + UDP 用于独立调试接收器 明确区分了 WebUI 使用的 HTTP 接口功能,并移除了不再需要的心跳包 说明。 refactor(health): 移除健康监控模块的备份文件 删除了 _assistant_backups 目录中的 health_monitor.cpp 和 health_monitor.h 备份文件,清理项目结构。
This commit is contained in:
@@ -13,8 +13,12 @@
|
||||
#include "protocol.h"
|
||||
#include "monitor/debug_log.h"
|
||||
|
||||
/** @brief 协议层初始化标志。 */
|
||||
static bool s_protocol_initialized = false;
|
||||
|
||||
/**
|
||||
* @brief 初始化协议层,准备 CRC 和 AES 模块。
|
||||
*/
|
||||
void protocol_init(void)
|
||||
{
|
||||
if (s_protocol_initialized) return;
|
||||
@@ -22,11 +26,17 @@ void protocol_init(void)
|
||||
DEBUG_INFO("PROT", "=== 初始化协议层 ===");
|
||||
DEBUG_INFO("PROT", "协议层功能: CRC16 Modbus + 硬件 AES-128-CBC");
|
||||
|
||||
/** @brief 协议层初始化标志。 */
|
||||
s_protocol_initialized = true;
|
||||
DEBUG_INFO("PROT", "协议层就绪");
|
||||
}
|
||||
|
||||
// CRC16 Modbus(LE,低字节在前)
|
||||
/**
|
||||
* @brief CRC16 Modbus(LE,低字节在前)
|
||||
* @param data 待校验的数据指针。
|
||||
* @param len 数据长度(字节)。
|
||||
* @return CRC16 校验值。
|
||||
*/
|
||||
uint16_t crc16_modbus(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
@@ -43,7 +53,14 @@ uint16_t crc16_modbus(const uint8_t *data, size_t len)
|
||||
return (crc >> 8) | (crc << 8);
|
||||
}
|
||||
|
||||
// 构建 SPI 帧: SOF + CMD + LEN(2LE) + PAYLOAD + CRC16(2LE)
|
||||
/**
|
||||
* @brief 构建 SPI 帧: SOF + CMD + LEN(2LE) + PAYLOAD + CRC16(2LE)
|
||||
* @param cmd 命令码。
|
||||
* @param payload 负载数据指针,可为 NULL。
|
||||
* @param len 负载长度(字节)。
|
||||
* @param out_frame 输出帧缓冲区。
|
||||
* @return 帧总长度,失败返回 -1。
|
||||
*/
|
||||
int frame_build(uint8_t cmd, const uint8_t *payload, size_t len, uint8_t *out_frame)
|
||||
{
|
||||
if (len > FRAME_MAX_PAYLOAD) {
|
||||
@@ -253,7 +270,12 @@ void scramble_generate(uint32_t timestamp, uint32_t mem_addr,
|
||||
memcpy(nonce_out, state, 16);
|
||||
}
|
||||
|
||||
// 会话密钥初始化
|
||||
/**
|
||||
* @brief 会话密钥初始化
|
||||
* @param sess 待初始化的会话上下文。
|
||||
* @param k_session 128 位会话密钥。
|
||||
* @return 0 成功,-1 失败。
|
||||
*/
|
||||
int session_key_init(session_ctx_t *sess, const uint8_t *k_session)
|
||||
{
|
||||
if (!sess || !k_session) return -1;
|
||||
@@ -266,6 +288,10 @@ int session_key_init(session_ctx_t *sess, const uint8_t *k_session)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除会话上下文中的密钥和状态。
|
||||
* @param sess 待清除的会话上下文。
|
||||
*/
|
||||
void session_key_clear(session_ctx_t *sess)
|
||||
{
|
||||
if (sess) {
|
||||
@@ -275,6 +301,14 @@ void session_key_clear(session_ctx_t *sess)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用会话上下文加密数据。
|
||||
* @param sess 会话上下文。
|
||||
* @param in 输入明文。
|
||||
* @param len 数据长度。
|
||||
* @param out 输出密文缓冲区。
|
||||
* @return 密文长度,失败返回 -1。
|
||||
*/
|
||||
int session_encrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t *out)
|
||||
{
|
||||
if (!sess || !in || !out) return -1;
|
||||
@@ -291,6 +325,14 @@ int session_encrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用会话上下文解密数据。
|
||||
* @param sess 会话上下文。
|
||||
* @param in 输入密文。
|
||||
* @param len 数据长度。
|
||||
* @param out 输出明文缓冲区。
|
||||
* @return 明文长度,失败返回 -1。
|
||||
*/
|
||||
int session_decrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t *out)
|
||||
{
|
||||
if (!sess || !in || !out) return -1;
|
||||
@@ -307,7 +349,11 @@ int session_decrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 使用 ESP-IDF 硬件 RNG 生成随机数
|
||||
/**
|
||||
* @brief 使用 ESP-IDF 硬件 RNG 生成随机数
|
||||
* @param nonce 输出缓冲区。
|
||||
* @param len 随机数字节数。
|
||||
*/
|
||||
void generate_random_nonce(uint8_t *nonce, size_t len)
|
||||
{
|
||||
esp_fill_random(nonce, len);
|
||||
|
||||
+86
-15
@@ -46,48 +46,119 @@ inline constexpr size_t CHIP_UID_LEN = 12;
|
||||
typedef struct {
|
||||
uint8_t key[16]; // 会话密钥 K_session
|
||||
uint8_t iv[16]; // CBC 模式 IV
|
||||
int used;
|
||||
int used; // 会话是否已初始化标志
|
||||
} session_ctx_t;
|
||||
|
||||
// 初始化协议层
|
||||
/**
|
||||
* @brief 初始化协议层资源,建立 CRC/AES 等基础能力的可用状态。
|
||||
*/
|
||||
void protocol_init(void);
|
||||
|
||||
// CRC16 Modbus 计算(低字节在前,LE)
|
||||
/**
|
||||
* @brief 计算一段数据的 CRC16-Modbus 校验值。
|
||||
* @param data 待校验的数据指针。
|
||||
* @param len 数据长度(字节)。
|
||||
* @return CRC16 校验值。
|
||||
*/
|
||||
uint16_t crc16_modbus(const uint8_t *data, size_t len);
|
||||
|
||||
// 构建帧: 返回总帧长度
|
||||
// 输出格式: SOF + CMD + LEN(2) + PAYLOAD + CRC16(2)
|
||||
/**
|
||||
* @brief 按协议格式构建一帧数据,返回生成后的总帧长度。
|
||||
* @param cmd 命令码。
|
||||
* @param payload 负载数据指针,可为 NULL。
|
||||
* @param len 负载长度(字节)。
|
||||
* @param out_frame 输出帧缓冲区。
|
||||
* @return 帧总长度,失败返回 -1。
|
||||
*/
|
||||
int frame_build(uint8_t cmd, const uint8_t *payload, size_t len, uint8_t *out_frame);
|
||||
|
||||
// 解析帧: 返回 payload 起始偏移,失败返回 -1
|
||||
// 检查 SOF,验证 CRC16,提取 CMD 和 LEN
|
||||
/**
|
||||
* @brief 解析一帧协议数据,提取命令字、负载和长度,失败返回 -1。
|
||||
* @param frame 待解析的帧数据。
|
||||
* @param frame_len 帧数据总长度。
|
||||
* @param out_cmd 输出参数,解析出的命令码。
|
||||
* @param out_payload 输出缓冲区,存放解析出的负载。
|
||||
* @param out_len 输出参数,负载实际长度。
|
||||
* @return 0 成功,-1 失败。
|
||||
*/
|
||||
int frame_parse(const uint8_t *frame, size_t frame_len,
|
||||
uint8_t *out_cmd, uint8_t *out_payload, size_t *out_len);
|
||||
|
||||
// T2.2: AES-128 CBC 加密
|
||||
// 使用预设主密钥或会话密钥
|
||||
// 注意: 函数名带 protocol_ 前缀以避免与 ESP-IDF wpa_supplicant 组件冲突
|
||||
/**
|
||||
* @brief 使用 AES-128-CBC 对明文进行加密,返回密文长度,失败返回负值。
|
||||
* @param plaintext 明文数据。
|
||||
* @param len 明文长度(字节)。
|
||||
* @param key 128 位 AES 密钥。
|
||||
* @param iv 128 位 CBC 初始向量。
|
||||
* @param ciphertext_out 输出缓冲区,存放密文。
|
||||
* @return 密文长度,失败返回 -1。
|
||||
*/
|
||||
int protocol_aes_encrypt(const uint8_t *plaintext, size_t len,
|
||||
const uint8_t *key, const uint8_t *iv,
|
||||
uint8_t *ciphertext_out);
|
||||
|
||||
/**
|
||||
* @brief 使用 AES-128-CBC 对密文进行解密,返回去除填充后的明文长度。
|
||||
* @param ciphertext 密文数据。
|
||||
* @param len 密文长度(字节,须为 16 的倍数)。
|
||||
* @param key 128 位 AES 密钥。
|
||||
* @param iv 128 位 CBC 初始向量。
|
||||
* @param plaintext_out 输出缓冲区,存放解密后的明文。
|
||||
* @return 明文长度,失败返回 -1。
|
||||
*/
|
||||
int protocol_aes_decrypt(const uint8_t *ciphertext, size_t len,
|
||||
const uint8_t *key, const uint8_t *iv,
|
||||
uint8_t *plaintext_out);
|
||||
|
||||
// T2.1: 搅伴算法 - 生成随机数
|
||||
// 输入: 时间戳 + 内存地址 + rand_seed
|
||||
// 输出: 16 字节 nonce_server
|
||||
/**
|
||||
* @brief 根据时间戳、内存地址和随机种子生成 16 字节随机数。
|
||||
* @param timestamp 时间戳种子。
|
||||
* @param mem_addr 内存地址种子。
|
||||
* @param rand_seed 随机数种子。
|
||||
* @param nonce_out 输出缓冲区,存放生成的 16 字节随机数。
|
||||
*/
|
||||
void scramble_generate(uint32_t timestamp, uint32_t mem_addr,
|
||||
uint32_t rand_seed, uint8_t *nonce_out);
|
||||
|
||||
// T2.4: 会话密钥管理
|
||||
/**
|
||||
* @brief 用给定会话密钥初始化会话上下文。
|
||||
* @param sess 待初始化的会话上下文。
|
||||
* @param k_session 128 位会话密钥。
|
||||
* @return 0 成功,-1 失败。
|
||||
*/
|
||||
int session_key_init(session_ctx_t *sess, const uint8_t *k_session);
|
||||
|
||||
/**
|
||||
* @brief 清除会话上下文中的密钥与状态信息。
|
||||
* @param sess 待清除的会话上下文。
|
||||
*/
|
||||
void session_key_clear(session_ctx_t *sess);
|
||||
|
||||
/**
|
||||
* @brief 使用当前会话上下文对输入数据执行加密。
|
||||
* @param sess 会话上下文。
|
||||
* @param in 输入明文。
|
||||
* @param len 数据长度。
|
||||
* @param out 输出密文缓冲区。
|
||||
* @return 密文长度,失败返回 -1。
|
||||
*/
|
||||
int session_encrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t *out);
|
||||
|
||||
/**
|
||||
* @brief 使用当前会话上下文对输入数据执行解密。
|
||||
* @param sess 会话上下文。
|
||||
* @param in 输入密文。
|
||||
* @param len 数据长度。
|
||||
* @param out 输出明文缓冲区。
|
||||
* @return 明文长度,失败返回 -1。
|
||||
*/
|
||||
int session_decrypt(session_ctx_t *sess, const uint8_t *in, size_t len, uint8_t *out);
|
||||
|
||||
// 使用 ESP-IDF 硬件 RNG 生成随机数
|
||||
/**
|
||||
* @brief 使用 ESP-IDF 硬件 RNG 生成指定长度的随机数。
|
||||
* @param nonce 输出缓冲区。
|
||||
* @param len 随机数字节数。
|
||||
*/
|
||||
void generate_random_nonce(uint8_t *nonce, size_t len);
|
||||
|
||||
#endif // PROTOCOL_H
|
||||
@@ -15,9 +15,13 @@
|
||||
#include "spi_master.h"
|
||||
#include "monitor/debug_log.h"
|
||||
|
||||
/** @brief SPI2 从设备句柄。 */
|
||||
spi_device_handle_t g_spi_device = NULL;
|
||||
|
||||
// SPI2 总线初始化
|
||||
/**
|
||||
* @brief SPI2 总线初始化
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_master_init(void)
|
||||
{
|
||||
DEBUG_INFO("SPI", "=== 初始化 SPI2 (VSPI) ===");
|
||||
@@ -100,12 +104,27 @@ esp_err_t spi_sync_transfer(uint8_t cmd, const uint8_t *payload,
|
||||
}
|
||||
|
||||
// 仅发送
|
||||
/**
|
||||
* @brief 调用同步传输接口发送数据,不要求接收返回内容。
|
||||
* @param cmd SPI 命令码。
|
||||
* @param payload 发送数据指针。
|
||||
* @param len 发送数据长度。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_send(uint8_t cmd, const uint8_t *payload, size_t len)
|
||||
{
|
||||
return spi_sync_transfer(cmd, payload, len, NULL, 0, 3000);
|
||||
}
|
||||
|
||||
// 仅接收
|
||||
/**
|
||||
* @brief 发送哑数据产生时钟,从从设备同步读取指定长度数据。
|
||||
* @param cmd SPI 命令码。
|
||||
* @param recv_buf 接收缓冲区。
|
||||
* @param len 接收长度。
|
||||
* @param timeout_ms 超时时间(毫秒)。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_recv(uint8_t cmd, uint8_t *recv_buf, size_t len, uint32_t timeout_ms)
|
||||
{
|
||||
uint8_t dummy[64] = {0};
|
||||
@@ -182,6 +201,7 @@ static int can_frame_send(can_channel_t channel, uint32_t ext_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 对外暴露的 GD32 CAN 发送接口,会在必要时修正默认 DLC。
|
||||
int gd32_can_send(can_channel_t channel, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc,
|
||||
uint8_t *recv_buf, uint8_t recv_max,
|
||||
@@ -193,6 +213,7 @@ int gd32_can_send(can_channel_t channel, uint32_t ext_id,
|
||||
return can_frame_send(channel, ext_id, data, dlc, recv_buf, recv_max, timeout_ms);
|
||||
}
|
||||
|
||||
// 对外暴露的无响应 GD32 CAN 发送接口。
|
||||
int gd32_can_send_no_reply(can_channel_t channel, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc)
|
||||
{
|
||||
|
||||
+49
-22
@@ -38,47 +38,74 @@ typedef enum {
|
||||
typedef struct {
|
||||
can_channel_t channel; // CAN 通道 (0~2)
|
||||
uint32_t ext_id; // 扩展帧 ID (29bit, Big-Endian 存储)
|
||||
uint8_t data[CAN_MAX_DLC];
|
||||
uint8_t data[CAN_MAX_DLC]; // CAN 数据负载 (最大 64 字节)
|
||||
uint8_t dlc; // 数据长度 (1~64)
|
||||
} can_frame_t;
|
||||
|
||||
// ============================================================
|
||||
// 发送 CANFD 帧
|
||||
// channel: CAN 通道 (0, 1, 2)
|
||||
// ext_id: 扩展帧 ID (29bit)
|
||||
// data: 数据内容 (1~64 字节)
|
||||
// dlc: 数据长度 (自动识别,可传 0 由 data 长度决定)
|
||||
// recv_buf: 接收缓冲区 (可传 NULL)
|
||||
// recv_max: 期望接收最大字节数
|
||||
// timeout_ms: 超时时间 (ms)
|
||||
// 返回: >= 0 成功(实际接收字节数), < 0 失败
|
||||
// ============================================================
|
||||
/**
|
||||
* @brief 发送一帧 CANFD 数据,可选等待并读取响应内容。
|
||||
* @param channel CAN 通道(0~2)。
|
||||
* @param ext_id 29 位扩展帧 ID。
|
||||
* @param data 数据负载指针。
|
||||
* @param dlc 数据长度,0 则自动推断。
|
||||
* @param recv_buf 接收缓冲区,可为 NULL。
|
||||
* @param recv_max 期望最大接收字节数。
|
||||
* @param timeout_ms 超时时间(毫秒)。
|
||||
* @return >= 0 接收字节数,< 0 失败。
|
||||
*/
|
||||
int gd32_can_send(can_channel_t channel, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc,
|
||||
uint8_t *recv_buf, uint8_t recv_max,
|
||||
uint32_t timeout_ms);
|
||||
|
||||
// 快速发送接口(不带响应接收)
|
||||
// 用于广播/通知类 CAN 帧
|
||||
/**
|
||||
* @brief 发送一帧无需响应的 CANFD 数据,适合广播或通知场景。
|
||||
* @param channel CAN 通道(0~2)。
|
||||
* @param ext_id 29 位扩展帧 ID。
|
||||
* @param data 数据负载指针。
|
||||
* @param dlc 数据长度。
|
||||
* @return >= 0 成功,< 0 失败。
|
||||
*/
|
||||
int gd32_can_send_no_reply(can_channel_t channel, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc);
|
||||
|
||||
// ============================================================
|
||||
|
||||
// 初始化 SPI2 (VSPI) 总线和设备
|
||||
/**
|
||||
* @brief 初始化 SPI2 总线与目标从设备。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_master_init(void);
|
||||
|
||||
// 同步 SPI 传输(阻塞式)
|
||||
// 发送 cmd + payload,接收响应到 recv_buf
|
||||
// 返回: ESP_OK 成功, ESP_ERR_TIMEOUT 超时
|
||||
/**
|
||||
* @brief 执行一次同步 SPI 传输,发送命令和负载并可选接收返回数据。
|
||||
* @param cmd SPI 命令码。
|
||||
* @param payload 发送数据指针。
|
||||
* @param payload_len 发送数据长度。
|
||||
* @param recv_buf 接收缓冲区,可为 NULL。
|
||||
* @param recv_len 期望接收长度。
|
||||
* @param timeout_ms 超时时间(毫秒)。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_sync_transfer(uint8_t cmd, const uint8_t *payload,
|
||||
size_t payload_len, uint8_t *recv_buf,
|
||||
size_t recv_len, uint32_t timeout_ms);
|
||||
|
||||
// 仅发送(不期望接收)
|
||||
/**
|
||||
* @brief 仅发送 SPI 数据,不关心返回内容。
|
||||
* @param cmd SPI 命令码。
|
||||
* @param payload 发送数据指针。
|
||||
* @param len 发送数据长度。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_send(uint8_t cmd, const uint8_t *payload, size_t len);
|
||||
|
||||
// 仅接收(发送哑数据)
|
||||
/**
|
||||
* @brief 仅接收 SPI 数据,底层会发送哑数据产生时钟。
|
||||
* @param cmd SPI 命令码。
|
||||
* @param recv_buf 接收缓冲区。
|
||||
* @param len 接收长度。
|
||||
* @param timeout_ms 超时时间(毫秒)。
|
||||
* @return ESP_OK 成功,其他失败。
|
||||
*/
|
||||
esp_err_t spi_recv(uint8_t cmd, uint8_t *recv_buf, size_t len, uint32_t timeout_ms);
|
||||
|
||||
#endif // SPI_MASTER_H
|
||||
Reference in New Issue
Block a user