Files
ESP32_COMM/main/monitor/debug_log.h
T
小龙虾 5db3f66b8e feat(protocol): 更新SPI Link协议至v2版本并优化CAN通信
- 将SPI OTA帧格式升级为SPI Link v2,采用新的帧格式:
  | SOF0 0xA5 | SOF1 0x5A | VER 0x02 | FLAGS | SESSION 4B LE |
  TX_SEQ | ACK_SEQ | CREDIT | TYPE | LEN 2B LE | RSV 2B | PAYLOAD |
  CRC32 4B LE |
- 新增V2_OVERHEAD=20字节开销,APP最大payload为236字节,BOOT为108字节
- 修改帧类型定义:ENTER_BOOT从0xB5合并至0xB0,新增CAN业务类型0xC0~C3
- 更新CAN协议文档,支持SPI Link v2-only模式,调整最大业务payload计算
- 扩展0xC1帧功能,支持双向通信和1Hz主动推送ID快照
- 添加0xC3完整ID快照帧类型,包含seq/rx_count等完整字段
- 更新协议文档中的错误码定义和反馈payload格式

refactor(main): 重构主程序入口并添加CAN反馈任务

- 在main.cpp中添加CAN反馈任务,更新启动流程注释
- 创建createCriticalTask辅助函数,统一关键任务创建逻辑
- 调整SPI初始化顺序,确保CAN反馈任务依赖正确
- 优化错误处理机制,关键任务失败时立即重启
- 将GD32版本探测移至ota_relay任务处理
- 更新版本号至0.3.48,调整编译时间和Git哈希

config: 更新SPI和BLE配置参数

- 添加SPI主机相关配置参数,包括CS GPIO、READY GPIO等引脚定义
- 配置SPI缓冲区大小:APP为256字节,BOOT为128字节
- 更新BLE滑动窗口参数,ACK窗口设为32,数据队列深度为72
- 调整SPI工作队列大小至5120字节,增加任务堆栈监控说明
2026-07-16 23:00:15 +08:00

148 lines
5.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* debug_log.h - 调试日志输出
*
* 当前日志会并行写入:
* - UART0 串口
* - UDP 单播(独立调试接收端可选)
* - 环形缓冲区,供 HTTP /logs 拉取
*
* 注意:当前 tools/WebUI 日志面板走的是 HTTP /logs,
* 不是直接监听 UDP 6666。
*
* UART0: TX=GPIO21, RX=GPIO20, 115200 波特率
*
* 线程模型:DEBUG_* / log() / send() 仅允许在任务上下文调用
* (内部使用 portMAX_DELAY 互斥锁,禁止 ISR)。
*/
#ifndef DEBUG_LOG_H
#define DEBUG_LOG_H
#include <stdint.h>
#include <stdarg.h>
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "app_config.h"
#include <sys/socket.h>
#include <netinet/in.h>
// 调试日志配置(DEBUG_LOG_IP/PORT 在 app_config.h)
// UART0 调试配置
#define UART_DEBUG_TX_GPIO 21 ///< UART0 TX GPIO 引脚号
#define UART_DEBUG_RX_GPIO 20 ///< UART0 RX GPIO 引脚号
#define UART_DEBUG_BAUD_RATE 115200 ///< UART0 波特率
// 日志级别
typedef enum {
DEBUG_LOG_ERR = 1, // 错误
DEBUG_LOG_WARN = 2, // 警告
DEBUG_LOG_INFO = 3, // 信息
DEBUG_LOG_DBG = 4 // 调试
} debug_log_level_t;
/**
* @brief 调试日志类(单例)
*
* 封装 UART0、UDP 和环形缓冲区三路日志输出通道,
* 所有内部状态(socket、缓冲区、互斥锁等)均为私有成员。
*/
class DebugLog {
public:
static DebugLog& instance();
/** @brief 初始化日志系统(UART + ring;UDP socket 延迟创建)。 */
esp_err_t init();
/** @brief 初始化 UART0 调试串口。 */
esp_err_t uartInit();
/** @brief 通过 UART0 直接发送一段原始字节数据。 */
esp_err_t uartSend(const uint8_t *data, size_t len);
/** @brief 发送原始日志数据到底层输出通道(任务上下文)。 */
esp_err_t send(const uint8_t *data, size_t len);
/** @brief 按日志级别与标签格式化输出一条日志(任务上下文)。 */
void log(debug_log_level_t level, const char *tag, const char *fmt, ...);
/** @brief 获取当前环形日志缓冲区中的日志条数。 */
int ringCount();
/** @brief 弹出一条最旧日志并从环形缓冲区移除。 */
bool ringPopOldest(char *out, size_t out_size, int *out_len);
/**
* @brief 将环形缓冲区日志导出为 JSON 数组字符串。
* @param out_len 输出 JSON 字符串长度。
* @return 动态分配的 JSON 字符串,调用者须 free();失败返回 NULL。
*/
char* ringDrainJson(int *out_len);
void setLogLevel(debug_log_level_t level) { m_logLevel = level; }
debug_log_level_t getLogLevel() const { return m_logLevel; }
private:
DebugLog() = default;
~DebugLog() = default;
DebugLog(const DebugLog&) = delete;
DebugLog& operator=(const DebugLog&) = delete;
int m_udpSock = -1;
struct sockaddr_in m_debugAddr = {};
bool m_initialized = false;
bool m_uartInitialized = false;
/** uartInit 成功后才为 true;send() 据此跳过 uart_write_bytes */
bool m_uartAvailable = false;
debug_log_level_t m_logLevel = DEBUG_LOG_INFO;
/* 环形日志:OTA 中继期需保留关键 INFO,64×160≈10KB BSS。 */
static constexpr int LOG_RING_SLOTS = 64;
static constexpr int LOG_RING_SIZE = 160;
/** 单次 /logs drain 上限(与 SLOTS 更接近,减 burst 滞后) */
static constexpr int LOG_DRAIN_MAX = 48;
char m_logRing[LOG_RING_SLOTS][LOG_RING_SIZE] = {};
int m_logRingLens[LOG_RING_SLOTS] = {};
int m_logRingHead = 0;
int m_logRingCount = 0;
SemaphoreHandle_t m_logRingMutex = nullptr;
/* 堆门控(防 OTA 时 UDP/格式化抢 LWIP 堆):
* ERR/WARN 永不因堆门控丢弃
* free < kDropInfoFree → 非 keep-alive 丢 INFO/DBG;keep-alive 保留 INFO
* free < kDropWarnFree → 全局丢 DBG
* free < kSkipUdpFree → 不 sendto(仍 UART+ring)
* free < kDrainMinFree → /logs 返 "[]" 避免大块 malloc
* keep-alive tag:OTA 前缀、SPI、WIFI */
static constexpr uint32_t kDropWarnFree = 6U * 1024U;
static constexpr uint32_t kDropInfoFree = 10U * 1024U;
static constexpr uint32_t kSkipUdpFree = 6U * 1024U;
static constexpr uint32_t kDrainMinFree = 4U * 1024U;
/** UDP 跳过/失败告警节流与 socket 重建 */
TickType_t m_lastUdpSkipWarnTick = 0;
TickType_t m_lastUdpFailWarnTick = 0;
uint8_t m_udpFailStreak = 0;
portMUX_TYPE m_udpMux = portMUX_INITIALIZER_UNLOCKED;
esp_err_t ensureUdpSocket();
void closeUdpSocket();
bool udpNetifHasIp() const;
void ringPush(const char *data, int len);
void ringPushLocked(const char *data, int len);
bool ringPopOldestLocked(char *out, size_t out_size, int *out_len);
int ringPeekDrainMetaLocked(int max_n, int *total_raw_len) const;
static bool isKeepAliveTag(const char *tag);
static char *allocEmptyJson(int *out_len);
static int jsonEscapeAppend(char *buf, int pos, int capacity, const char *src, int src_len);
};
// 便捷宏 — 直接调用单例方法(禁止在 ISR 中使用)
#define DEBUG_ERR(tag, fmt, ...) DebugLog::instance().log(DEBUG_LOG_ERR, tag, fmt, ##__VA_ARGS__)
#define DEBUG_WARN(tag, fmt, ...) DebugLog::instance().log(DEBUG_LOG_WARN, tag, fmt, ##__VA_ARGS__)
#define DEBUG_INFO(tag, fmt, ...) DebugLog::instance().log(DEBUG_LOG_INFO, tag, fmt, ##__VA_ARGS__)
#define DEBUG_DBG(tag, fmt, ...) DebugLog::instance().log(DEBUG_LOG_DBG, tag, fmt, ##__VA_ARGS__)
#endif // DEBUG_LOG_H