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:
小龙虾
2026-05-05 04:48:31 +08:00
parent 478e5e5a31
commit f598b9acb3
20 changed files with 955 additions and 1420 deletions
+59 -15
View File
@@ -1,16 +1,18 @@
/*
* debug_log.c - WiFi 调试日志输出实现
* debug_log.c - 调试日志输出实现
*
* 日志会同时写入三处:
* 1. UART0 串口(本地串口调试)
* 2. UDP 单播到指定上位机(独立 UDP 调试接收端可选)
* 3. 内部环形缓冲区(供 HTTP /logs 拉取,WebUI 实际使用这一条)
*
* 通过 UDP socket 将调试日志发送到上位机
* 上位机需监听 <DEBUG_LOG_IP>:<DEBUG_LOG_PORT>
*
* 支持双通道输出:UDP + UART0 串口
* UART0: TX=GPIO21, RX=GPIO20, 115200 波特率
* UDP: <DEBUG_LOG_IP>:<DEBUG_LOG_PORT>
*
* 时序说明:
* esp_netif_init() 创建 tcpip 线程后才可安全使用 socket
* 本模块在首次 debug_log_send() 时才创建 socket(延迟创建)
* WiFi 连接前 sendto() 会失败(无路由),日志仅走 UART
* WiFi 连接前 sendto() 会失败(无路由),日志仍可写入 UART 与 HTTP 日志缓冲
* WiFi 连接后 UDP 立即生效,无需额外操作
*/
#include <string.h>
@@ -28,10 +30,14 @@
static const char *TAG = "DEBUG_LOG";
/** @brief 当前调试日志过滤级别,运行时可动态调整。 */
debug_log_level_t g_debug_log_level = DEBUG_LOG_ALL;
/** @brief UDP 调试日志 socket 句柄,首次发送时延迟创建。 */
static int s_udp_sock = -1;
/** @brief UDP 调试日志目标地址。 */
static struct sockaddr_in s_debug_addr;
/** @brief 日志模块初始化标志。 */
static bool s_initialized = false;
// ============================================================
@@ -43,9 +49,13 @@ static char s_log_ring[LOG_RING_SLOTS][LOG_RING_SIZE];
static int s_log_ring_lens[LOG_RING_SLOTS];
static int s_log_ring_head = 0; // 下次写入位置
static int s_log_ring_count = 0; // 已存条数(超过 LOG_RING_SLOTS 后不再增长,覆盖旧数据)
/** @brief 环形缓冲区互斥锁,保护多线程并发读写。 */
static SemaphoreHandle_t s_log_ring_mutex = NULL;
// 获取日志缓冲区中的条数
/**
* @brief 获取日志缓冲区中的条数
* @return 当前日志条数。
*/
int debug_log_ring_count(void)
{
if (!s_log_ring_mutex) return 0;
@@ -94,7 +104,6 @@ char *debug_log_ring_drain_json(int *out_len)
if (i > 0) json[pos++] = ',';
// JSON 转义日志内容
pos += snprintf(json + pos, buf_size - pos, "\"");
for (int j = 0; j < len && pos < (int)buf_size - 10; j++) {
char c = s_log_ring[idx][j];
@@ -116,6 +125,7 @@ char *debug_log_ring_drain_json(int *out_len)
// 清空缓冲区
s_log_ring_head = 0;
/** @brief 环形缓冲区中已存储的日志条数。 */
s_log_ring_count = 0;
xSemaphoreGive(s_log_ring_mutex);
@@ -124,7 +134,11 @@ char *debug_log_ring_drain_json(int *out_len)
return json;
}
// 写入一条日志到环形缓冲区
/**
* @brief 写入一条日志到环形缓冲区
* @param data
* @param len
*/
static void log_ring_push(const char *data, int len)
{
if (!s_log_ring_mutex || len <= 0) return;
@@ -138,6 +152,7 @@ static void log_ring_push(const char *data, int len)
s_log_ring[s_log_ring_head][copy_len] = '\0';
s_log_ring_lens[s_log_ring_head] = copy_len;
/** @brief 环形缓冲区下一个写入位置。 */
s_log_ring_head = (s_log_ring_head + 1) % LOG_RING_SLOTS;
if (s_log_ring_count < LOG_RING_SLOTS) s_log_ring_count++;
@@ -146,12 +161,16 @@ static void log_ring_push(const char *data, int len)
// UART0 状态
static bool s_uart_initialized = false;
/** @brief 调试串口使用的 UART 端口号。 */
static uart_port_t s_uart_port = UART_NUM_0;
// ============================================================
// UART0 串口调试
// ============================================================
/**
* @brief 初始化 UART0 调试串口,配置波特率和引脚映射。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t uart_debug_init(void)
{
if (s_uart_initialized) {
@@ -187,6 +206,7 @@ esp_err_t uart_debug_init(void)
return ret;
}
/** @brief UART0 初始化标志。 */
s_uart_initialized = true;
ESP_LOGI(TAG, "UART0 调试输出已初始化 (TX=GPIO%d, RX=GPIO%d, %d baud)",
UART_DEBUG_TX_GPIO, UART_DEBUG_RX_GPIO, UART_DEBUG_BAUD_RATE);
@@ -194,6 +214,12 @@ esp_err_t uart_debug_init(void)
return ESP_OK;
}
/**
* @brief 通过 UART0 发送一段原始字节数据。
* @param data 待发送的数据指针。
* @param len 数据长度。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t uart_debug_send(const uint8_t *data, size_t len)
{
if (!s_uart_initialized) {
@@ -212,7 +238,10 @@ esp_err_t uart_debug_send(const uint8_t *data, size_t len)
// UDP 调试日志
// ============================================================
// 初始化(UART 立即可用,UDP socket 延迟到首次发送时创建)
/**
* @brief 初始化(UART 立即可用,UDP socket 延迟到首次发送时创建)
* @return ESP_OK 成功,其他失败。
*/
esp_err_t debug_log_init(void)
{
if (s_initialized) {
@@ -221,7 +250,7 @@ esp_err_t debug_log_init(void)
uart_debug_init();
// 日志环形缓冲区互斥锁
/** @brief 环形缓冲区互斥锁,保护多线程并发读写。 */
s_log_ring_mutex = xSemaphoreCreateMutex();
// 目标地址:单播到上位机
@@ -230,19 +259,24 @@ esp_err_t debug_log_init(void)
s_debug_addr.sin_port = htons(DEBUG_LOG_PORT);
s_debug_addr.sin_addr.s_addr = inet_addr(DEBUG_LOG_IP);
/** @brief 日志模块初始化标志。 */
s_initialized = true;
ESP_LOGI(TAG, "调试日志已初始化,上位机 %s:%d", DEBUG_LOG_IP, DEBUG_LOG_PORT);
return ESP_OK;
}
// 延迟创建 UDP socket(首次发送时调用)
/**
* @brief 延迟创建 UDP socket(首次发送时调用)
* @return
*/
static esp_err_t ensure_udp_socket(void)
{
if (s_udp_sock >= 0) {
return ESP_OK;
}
/** @brief UDP 调试日志 socket 句柄,首次发送时延迟创建。 */
s_udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (s_udp_sock < 0) {
ESP_LOGE(TAG, "创建 UDP socket 失败");
@@ -253,7 +287,12 @@ static esp_err_t ensure_udp_socket(void)
return ESP_OK;
}
// 发送原始数据(UDP 单播 + UART 双通道)
/**
* @brief 发送原始数据(UDP 单播 + UART 双通道)
* @param data 日志数据指针。
* @param len 数据长度。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t debug_log_send(const uint8_t *data, size_t len)
{
esp_err_t udp_ret = ESP_FAIL;
@@ -289,7 +328,12 @@ esp_err_t debug_log_send(const uint8_t *data, size_t len)
return ESP_FAIL;
}
// 格式化日志输出
/**
* @brief 格式化日志输出
* @param level 日志级别。
* @param tag 日志标签。
* @param fmt 格式化字符串。
*/
void debug_log(debug_log_level_t level, const char *tag, const char *fmt, ...)
{
if (!s_initialized) {