feat: add debug log system (ring buffer in SDRAM) and serial capture tool

- debug_log.h/cpp: ring buffer with 100 entries in SDRAM, dual output to serial
- main.cpp: add log_init() initialization
- packer_ui.cpp: add LOG_INFO at start/reset/minus/plus/pack-complete events
- tools/capture_log.py: PC-side serial log capture with timestamped files
This commit is contained in:
2026-05-04 21:45:54 +08:00
parent 15c052b253
commit e6a9965d22
5 changed files with 499 additions and 175 deletions
+3
View File
@@ -10,6 +10,7 @@
#include "monitor_task.h"
#include "work_task.h"
#include "lvgl_task.h"
#include "debug_log.h"
#include <cstdio>
#include <cstring>
#include <rthw.h>
@@ -159,6 +160,8 @@ extern "C" int main(void)
printf("\r\n===== System Ready =====\r\n");
printf("CPU: 168MHz, SDRAM: 32MB, Flash: 1MB\r\n\r\n");
log_init();
Lcd::instance().clear(Lcd::BLACK);
printf("Initializing RT-Thread...\r\n");
+108
View File
@@ -0,0 +1,108 @@
#include "debug_log.h"
#include <rtthread.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
static log_entry_t log_buf[LOG_BUF_SIZE] __attribute__((section(".sdram")));
static uint32_t write_idx = 0;
static uint32_t total_count = 0;
static bool initialized = false;
void log_init(void)
{
if (!initialized)
{
memset(log_buf, 0, sizeof(log_buf));
write_idx = 0;
total_count = 0;
initialized = true;
LOG_INFO("Debug log system initialized, %d entries buffer", LOG_BUF_SIZE);
}
}
void log_write(log_level_t level, const char *file, int line, const char *func, const char *fmt, ...)
{
if (!initialized) return;
uint32_t idx = write_idx % LOG_BUF_SIZE;
log_entry_t *entry = &log_buf[idx];
entry->tick = rt_tick_get();
entry->level = level;
va_list args;
va_start(args, fmt);
rt_snprintf(entry->message, LOG_MSG_MAX, "[%s:%d] ", file, line);
uint32_t prefix_len = strlen(entry->message);
rt_vsnprintf(entry->message + prefix_len, LOG_MSG_MAX - prefix_len, fmt, args);
va_end(args);
write_idx++;
total_count++;
const char *level_str = "I";
if (level == LOG_LEVEL_WARN) level_str = "W";
else if (level == LOG_LEVEL_ERROR) level_str = "E";
printf("[%lu][%s] %s\r\n",
(unsigned long)entry->tick,
level_str,
entry->message);
}
void log_dump(void)
{
printf("\r\n========== DEBUG LOG DUMP ==========\r\n");
printf("Total entries: %lu, Buffer slots: %d\r\n",
(unsigned long)total_count, LOG_BUF_SIZE);
printf("====================================\r\n");
if (total_count == 0)
{
printf("(no log entries)\r\n");
return;
}
uint32_t start = 0;
uint32_t count = LOG_BUF_SIZE;
if (total_count < LOG_BUF_SIZE)
{
start = 0;
count = total_count;
}
else
{
start = write_idx % LOG_BUF_SIZE;
}
for (uint32_t i = 0; i < count; i++)
{
uint32_t idx = (start + i) % LOG_BUF_SIZE;
const log_entry_t *entry = &log_buf[idx];
if (entry->tick == 0 && entry->message[0] == '\0')
continue;
const char *level_str = "INFO";
if (entry->level == LOG_LEVEL_WARN) level_str = "WARN";
else if (entry->level == LOG_LEVEL_ERROR) level_str = "ERROR";
printf("[%06lu][%s] %s\r\n",
(unsigned long)entry->tick,
level_str,
entry->message);
}
printf("========== DUMP END ==========\r\n");
}
uint32_t log_count(void)
{
return total_count;
}
const log_entry_t *log_get_entry(uint32_t index)
{
if (index >= LOG_BUF_SIZE) return NULL;
return &log_buf[index];
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef __DEBUG_LOG_H__
#define __DEBUG_LOG_H__
#include <cstdint>
#include <cstdio>
#define LOG_BUF_SIZE 100
#define LOG_MSG_MAX 100
typedef enum {
LOG_LEVEL_INFO = 0,
LOG_LEVEL_WARN = 1,
LOG_LEVEL_ERROR = 2,
} log_level_t;
typedef struct {
uint32_t tick;
log_level_t level;
char message[LOG_MSG_MAX];
} log_entry_t;
void log_init(void);
void log_write(log_level_t level, const char *file, int line, const char *func, const char *fmt, ...);
void log_dump(void);
uint32_t log_count(void);
const log_entry_t *log_get_entry(uint32_t index);
#define LOG_INFO(fmt, ...) \
log_write(LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) \
log_write(LOG_LEVEL_WARN, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) \
log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#endif
+255 -175
View File
@@ -1,6 +1,8 @@
#include "packer_ui.h"
#include "debug_log.h"
#include "lvgl/lvgl.h"
#include <stdio.h>
#include <string.h>
LV_FONT_DECLARE(custom_cjk_16);
LV_FONT_DECLARE(custom_cjk_20);
@@ -8,241 +10,319 @@ LV_FONT_DECLARE(custom_cjk_30);
LV_FONT_DECLARE(custom_cjk_40);
static lv_obj_t *packer_scr = NULL;
static lv_obj_t *header = NULL;
static lv_obj_t *weight_panel = NULL;
static lv_obj_t *control_panel = NULL;
static lv_obj_t *start_btn = NULL;
static lv_obj_t *weight_label = NULL;
static lv_obj_t *count_label = NULL;
static lv_obj_t *status_label = NULL;
static lv_obj_t *set_weight_label = NULL;
static lv_obj_t *minus_btn = NULL;
static lv_obj_t *plus_btn = NULL;
static lv_obj_t *card_status = NULL;
static lv_obj_t *card_target = NULL;
static lv_obj_t *btn_minus = NULL;
static lv_obj_t *btn_plus = NULL;
static lv_obj_t *btn_start = NULL;
static lv_obj_t *btn_reset = NULL;
static lv_obj_t *label_target_val = NULL;
static lv_obj_t *label_current_weight = NULL;
static lv_obj_t *label_pack_count = NULL;
static lv_obj_t *label_status = NULL;
static lv_obj_t *status_dot = NULL;
static volatile bool is_running = false;
static volatile uint32_t target_weight = 190;
static volatile uint32_t current_weight = 0;
static volatile uint32_t pack_count = 0;
static lv_color_t COLOR_HEADER_BG = lv_color_hex(0x153962);
static lv_color_t COLOR_BG = lv_color_hex(0x0D1117);
static lv_color_t COLOR_CARD = lv_color_hex(0x161B22);
static lv_color_t COLOR_TEXT = lv_color_hex(0xFFFFFF);
static lv_color_t COLOR_GREEN = lv_color_hex(0x3FB950);
static lv_color_t COLOR_GREEN_BRIGHT = lv_color_hex(0x01EB9B);
static lv_color_t COLOR_RED = lv_color_hex(0xF76F4A);
static lv_color_t COLOR_BORDER = lv_color_hex(0x30363D);
static lv_color_t COLOR_BUTTON_DARK = lv_color_hex(0x2A2828);
static lv_color_t COLOR_BG;
static lv_color_t COLOR_CARD;
static lv_color_t COLOR_CARD_TARGET;
static lv_color_t COLOR_BTN_START;
static lv_color_t COLOR_BTN_DARK;
static lv_color_t COLOR_BTN_ADJ;
static lv_color_t COLOR_BORDER;
static lv_color_t COLOR_TEXT_MAIN;
static lv_color_t COLOR_TEXT_SEC;
static lv_color_t COLOR_GREEN;
static lv_color_t COLOR_ADJ_ICON;
static bool is_running = false;
static uint32_t target_weight = 200;
static uint32_t pack_count = 200;
static void start_cb(lv_event_t *e);
static void reset_cb(lv_event_t *e);
static void minus_cb(lv_event_t *e);
static void plus_cb(lv_event_t *e);
static void update_display(void);
static void start_stop_cb(lv_event_t *e);
static void weight_dec_cb(lv_event_t *e);
static void weight_inc_cb(lv_event_t *e);
static void update_status_display();
static void set_colors(void)
{
COLOR_BG = lv_color_hex(0x161b22);
COLOR_CARD = lv_color_hex(0x000000);
COLOR_CARD_TARGET = lv_color_hex(0x080a0e);
COLOR_BTN_START = lv_color_hex(0x06990c);
COLOR_BTN_DARK = lv_color_hex(0x000000);
COLOR_BTN_ADJ = lv_color_hex(0x1e3c28);
COLOR_BORDER = lv_color_hex(0x30363d);
COLOR_TEXT_MAIN = lv_color_hex(0xffffff);
COLOR_TEXT_SEC = lv_color_hex(0x9ab3a5);
COLOR_GREEN = lv_color_hex(0x06990c);
COLOR_ADJ_ICON = lv_color_hex(0xc4c4c4);
}
static lv_obj_t *create_card(lv_obj_t *parent, int x, int y, int w, int h, lv_color_t bg, int radius)
{
lv_obj_t *card = lv_obj_create(parent);
lv_obj_set_size(card, w, h);
lv_obj_set_pos(card, x, y);
lv_obj_set_style_bg_color(card, bg, 0);
lv_obj_set_style_radius(card, radius, 0);
lv_obj_set_style_border_width(card, 0, 0);
lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE);
return card;
}
RetCode packer_ui_create(void)
{
set_colors();
packer_scr = lv_obj_create(NULL);
lv_obj_set_style_bg_color(packer_scr, COLOR_BG, 0);
lv_obj_set_size(packer_scr, 800, 480);
lv_obj_clear_flag(packer_scr, LV_OBJ_FLAG_SCROLLABLE);
header = lv_obj_create(packer_scr);
lv_obj_set_size(header, 800, 60);
lv_obj_set_pos(header, 0, 0);
lv_obj_set_style_bg_color(header, COLOR_HEADER_BG, 0);
lv_obj_set_style_radius(header, 0, 0);
lv_obj_clear_flag(header, LV_OBJ_FLAG_SCROLLABLE);
card_status = create_card(packer_scr, 10, 10, 560, 330, COLOR_CARD, 20);
weight_panel = lv_obj_create(packer_scr);
lv_obj_set_size(weight_panel, 560, 400);
lv_obj_set_pos(weight_panel, 10, 70);
lv_obj_set_style_bg_color(weight_panel, COLOR_CARD, 0);
lv_obj_set_style_radius(weight_panel, 10, 0);
lv_obj_set_style_border_width(weight_panel, 2, 0);
lv_obj_set_style_border_color(weight_panel, COLOR_BORDER, 0);
lv_obj_clear_flag(weight_panel, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t *w_title = lv_label_create(card_status);
lv_label_set_text(w_title, "当前重量");
lv_obj_set_style_text_font(w_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(w_title, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(w_title, 103, 14);
lv_obj_t *set_weight_title = lv_label_create(weight_panel);
lv_label_set_text(set_weight_title, "设置重量");
lv_obj_set_style_text_font(set_weight_title, &custom_cjk_40, 0);
lv_obj_set_style_text_color(set_weight_title, COLOR_TEXT, 0);
lv_obj_set_pos(set_weight_title, 39, 101);
label_current_weight = lv_label_create(card_status);
lv_label_set_text(label_current_weight, "0.0");
lv_obj_set_style_text_font(label_current_weight, &custom_cjk_40, 0);
lv_obj_set_style_text_color(label_current_weight, COLOR_TEXT_MAIN, 0);
lv_obj_set_pos(label_current_weight, 82, 64);
set_weight_label = lv_label_create(weight_panel);
char weight_str[20];
snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10);
lv_label_set_text(set_weight_label, weight_str);
lv_obj_set_style_text_font(set_weight_label, &custom_cjk_40, 0);
lv_obj_set_style_text_color(set_weight_label, COLOR_GREEN_BRIGHT, 0);
lv_obj_set_pos(set_weight_label, 239, 101);
lv_obj_t *w_unit = lv_label_create(card_status);
lv_label_set_text(w_unit, "");
lv_obj_set_style_text_font(w_unit, &custom_cjk_20, 0);
lv_obj_set_style_text_color(w_unit, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(w_unit, 140, 60);
minus_btn = lv_obj_create(weight_panel);
lv_obj_set_size(minus_btn, 80, 80);
lv_obj_set_pos(minus_btn, 350, 89);
lv_obj_set_style_bg_color(minus_btn, COLOR_BUTTON_DARK, 0);
lv_obj_set_style_radius(minus_btn, 20, 0);
lv_obj_add_event_cb(minus_btn, weight_dec_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *w_sub = lv_label_create(card_status);
lv_label_set_text(w_sub, "称重斗实时值");
lv_obj_set_style_text_font(w_sub, &custom_cjk_16, 0);
lv_obj_set_style_text_color(w_sub, lv_color_hex(0x6f9181), 0);
lv_obj_set_pos(w_sub, 25, 105);
lv_obj_t *minus_line = lv_obj_create(minus_btn);
lv_obj_set_size(minus_line, 40, 5);
lv_obj_set_style_bg_color(minus_line, COLOR_TEXT, 0);
lv_obj_align(minus_line, LV_ALIGN_CENTER, 0, 0);
lv_obj_t *div_line = lv_obj_create(card_status);
lv_obj_set_size(div_line, 500, 1);
lv_obj_set_pos(div_line, 25, 140);
lv_obj_set_style_bg_color(div_line, COLOR_BORDER, 0);
lv_obj_clear_flag(div_line, LV_OBJ_FLAG_SCROLLABLE);
plus_btn = lv_obj_create(weight_panel);
lv_obj_set_size(plus_btn, 80, 80);
lv_obj_set_pos(plus_btn, 470, 90);
lv_obj_set_style_bg_color(plus_btn, COLOR_BUTTON_DARK, 0);
lv_obj_set_style_radius(plus_btn, 20, 0);
lv_obj_add_event_cb(plus_btn, weight_inc_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *vertical_line = lv_obj_create(card_status);
lv_obj_set_size(vertical_line, 1, 287);
lv_obj_set_pos(vertical_line, 277, 22);
lv_obj_set_style_bg_color(vertical_line, COLOR_BORDER, 0);
lv_obj_clear_flag(vertical_line, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t *plus_h_line = lv_obj_create(plus_btn);
lv_obj_set_size(plus_h_line, 40, 5);
lv_obj_set_style_bg_color(plus_h_line, COLOR_TEXT, 0);
lv_obj_align(plus_h_line, LV_ALIGN_CENTER, 0, 0);
lv_obj_t *c_title = lv_label_create(card_status);
lv_label_set_text(c_title, "总包数");
lv_obj_set_style_text_font(c_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(c_title, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(c_title, 297, 22);
lv_obj_t *plus_v_line = lv_obj_create(plus_btn);
lv_obj_set_size(plus_v_line, 5, 40);
lv_obj_set_style_bg_color(plus_v_line, COLOR_TEXT, 0);
lv_obj_align(plus_v_line, LV_ALIGN_CENTER, 0, 0);
label_pack_count = lv_label_create(card_status);
lv_label_set_text(label_pack_count, "0");
lv_obj_set_style_text_font(label_pack_count, &custom_cjk_40, 0);
lv_obj_set_style_text_color(label_pack_count, COLOR_TEXT_MAIN, 0);
lv_obj_set_pos(label_pack_count, 297, 50);
control_panel = lv_obj_create(packer_scr);
lv_obj_set_size(control_panel, 200, 400);
lv_obj_set_pos(control_panel, 590, 70);
lv_obj_set_style_bg_color(control_panel, COLOR_CARD, 0);
lv_obj_set_style_radius(control_panel, 10, 0);
lv_obj_set_style_border_width(control_panel, 2, 0);
lv_obj_set_style_border_color(control_panel, COLOR_BORDER, 0);
lv_obj_clear_flag(control_panel, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t *c_unit = lv_label_create(card_status);
lv_label_set_text(c_unit, "");
lv_obj_set_style_text_font(c_unit, &custom_cjk_20, 0);
lv_obj_set_style_text_color(c_unit, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(c_unit, 390, 60);
start_btn = lv_obj_create(control_panel);
lv_obj_set_size(start_btn, 159, 190);
lv_obj_set_pos(start_btn, 20, 20);
lv_obj_set_style_bg_color(start_btn, COLOR_GREEN, 0);
lv_obj_set_style_radius(start_btn, 20, 0);
lv_obj_add_event_cb(start_btn, start_stop_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *c_sub = lv_label_create(card_status);
lv_label_set_text(c_sub, "累计成品");
lv_obj_set_style_text_font(c_sub, &custom_cjk_16, 0);
lv_obj_set_style_text_color(c_sub, lv_color_hex(0x6f9181), 0);
lv_obj_set_pos(c_sub, 297, 110);
lv_obj_t *btn_label = lv_label_create(start_btn);
lv_label_set_text(btn_label, "启动");
lv_obj_set_style_text_font(btn_label, &custom_cjk_30, 0);
lv_obj_set_style_text_color(btn_label, COLOR_TEXT, 0);
lv_obj_align(btn_label, LV_ALIGN_TOP_MID, 0, 73);
lv_obj_t *status_bg = create_card(card_status, 318, 196, 224, 71, COLOR_CARD, 30);
lv_obj_t *btn_hint = lv_label_create(start_btn);
lv_label_set_text(btn_hint, "按此启动");
lv_obj_set_style_text_font(btn_hint, &custom_cjk_16, 0);
lv_obj_set_style_text_color(btn_hint, COLOR_TEXT, 0);
lv_obj_align(btn_hint, LV_ALIGN_TOP_MID, 0, 160);
status_dot = lv_obj_create(status_bg);
lv_obj_set_size(status_dot, 34, 35);
lv_obj_set_style_radius(status_dot, 17, 0);
lv_obj_set_style_border_width(status_dot, 0, 0);
lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0);
lv_obj_set_pos(status_dot, 22, 9);
lv_obj_t *weight_title = lv_label_create(control_panel);
lv_label_set_text(weight_title, "当前重量");
lv_obj_set_style_text_font(weight_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(weight_title, COLOR_TEXT, 0);
lv_obj_set_pos(weight_title, 30, 218);
label_status = lv_label_create(status_bg);
lv_label_set_text(label_status, "已停止");
lv_obj_set_style_text_font(label_status, &custom_cjk_30, 0);
lv_obj_set_style_text_color(label_status, COLOR_TEXT_SEC, 0);
lv_obj_align(label_status, LV_ALIGN_RIGHT_MID, -20, 0);
weight_label = lv_label_create(control_panel);
lv_label_set_text(weight_label, "0.0g");
lv_obj_set_style_text_font(weight_label, &custom_cjk_20, 0);
lv_obj_set_style_text_color(weight_label, COLOR_GREEN, 0);
lv_obj_set_pos(weight_label, 30, 244);
btn_start = lv_btn_create(packer_scr);
lv_obj_set_size(btn_start, 210, 330);
lv_obj_set_pos(btn_start, 580, 10);
lv_obj_set_style_bg_color(btn_start, COLOR_BTN_START, 0);
lv_obj_set_style_radius(btn_start, 20, 0);
lv_obj_clear_flag(btn_start, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_event_cb(btn_start, start_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *count_title = lv_label_create(control_panel);
lv_label_set_text(count_title, "包装计数");
lv_obj_set_style_text_font(count_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(count_title, COLOR_TEXT, 0);
lv_obj_set_pos(count_title, 30, 280);
lv_obj_t *start_lbl = lv_label_create(btn_start);
lv_label_set_text(start_lbl, "启动");
lv_obj_set_style_text_font(start_lbl, &custom_cjk_30, 0);
lv_obj_set_style_text_color(start_lbl, COLOR_TEXT_MAIN, 0);
lv_obj_align(start_lbl, LV_ALIGN_CENTER, 0, 0);
count_label = lv_label_create(control_panel);
char count_str[20];
snprintf(count_str, sizeof(count_str), "%lu", pack_count);
lv_label_set_text(count_label, count_str);
lv_obj_set_style_text_font(count_label, &custom_cjk_20, 0);
lv_obj_set_style_text_color(count_label, COLOR_GREEN, 0);
lv_obj_set_pos(count_label, 30, 300);
card_target = create_card(packer_scr, 10, 350, 560, 120, COLOR_CARD_TARGET, 20);
lv_obj_t *status_title = lv_label_create(control_panel);
lv_label_set_text(status_title, "运行状态");
lv_obj_set_style_text_font(status_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(status_title, COLOR_TEXT, 0);
lv_obj_set_pos(status_title, 30, 335);
lv_obj_t *t_title = lv_label_create(card_target);
lv_label_set_text(t_title, "设计重量");
lv_obj_set_style_text_font(t_title, &custom_cjk_16, 0);
lv_obj_set_style_text_color(t_title, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(t_title, 25, 15);
status_label = lv_label_create(control_panel);
lv_label_set_text(status_label, "已停止");
lv_obj_set_style_text_font(status_label, &custom_cjk_20, 0);
lv_obj_set_style_text_color(status_label, COLOR_RED, 0);
lv_obj_set_pos(status_label, 28, 361);
label_target_val = lv_label_create(card_target);
char tmp[20];
snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10));
lv_label_set_text(label_target_val, tmp);
lv_obj_set_style_text_font(label_target_val, &custom_cjk_40, 0);
lv_obj_set_style_text_color(label_target_val, COLOR_TEXT_MAIN, 0);
lv_obj_set_pos(label_target_val, 25, 40);
lv_obj_t *t_unit = lv_label_create(card_target);
lv_label_set_text(t_unit, "克/包");
lv_obj_set_style_text_font(t_unit, &custom_cjk_16, 0);
lv_obj_set_style_text_color(t_unit, COLOR_TEXT_SEC, 0);
lv_obj_set_pos(t_unit, 130, 50);
btn_minus = lv_btn_create(card_target);
lv_obj_set_size(btn_minus, 60, 60);
lv_obj_set_pos(btn_minus, 331, 30);
lv_obj_set_style_bg_color(btn_minus, COLOR_BTN_ADJ, 0);
lv_obj_set_style_radius(btn_minus, 30, 0);
lv_obj_clear_flag(btn_minus, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_event_cb(btn_minus, minus_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *minus_lbl = lv_label_create(btn_minus);
lv_label_set_text(minus_lbl, "-");
lv_obj_set_style_text_font(minus_lbl, &custom_cjk_30, 0);
lv_obj_set_style_text_color(minus_lbl, COLOR_TEXT_MAIN, 0);
lv_obj_align(minus_lbl, LV_ALIGN_CENTER, 0, 0);
btn_plus = lv_btn_create(card_target);
lv_obj_set_size(btn_plus, 60, 60);
lv_obj_set_pos(btn_plus, 463, 30);
lv_obj_set_style_bg_color(btn_plus, COLOR_BTN_ADJ, 0);
lv_obj_set_style_radius(btn_plus, 30, 0);
lv_obj_clear_flag(btn_plus, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_event_cb(btn_plus, plus_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *plus_lbl = lv_label_create(btn_plus);
lv_label_set_text(plus_lbl, "+");
lv_obj_set_style_text_font(plus_lbl, &custom_cjk_30, 0);
lv_obj_set_style_text_color(plus_lbl, COLOR_TEXT_MAIN, 0);
lv_obj_align(plus_lbl, LV_ALIGN_CENTER, 0, 0);
btn_reset = lv_btn_create(packer_scr);
lv_obj_set_size(btn_reset, 210, 120);
lv_obj_set_pos(btn_reset, 580, 350);
lv_obj_set_style_bg_color(btn_reset, COLOR_BTN_DARK, 0);
lv_obj_set_style_radius(btn_reset, 20, 0);
lv_obj_clear_flag(btn_reset, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_event_cb(btn_reset, reset_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *reset_lbl = lv_label_create(btn_reset);
lv_label_set_text(reset_lbl, "复位");
lv_obj_set_style_text_font(reset_lbl, &custom_cjk_30, 0);
lv_obj_set_style_text_color(reset_lbl, COLOR_TEXT_SEC, 0);
lv_obj_align(reset_lbl, LV_ALIGN_CENTER, 0, 0);
lv_scr_load(packer_scr);
update_display();
return RET_OK;
}
static void start_stop_cb(lv_event_t *e)
static void start_cb(lv_event_t *e)
{
(void)e;
is_running = !is_running;
update_status_display();
is_running = true;
current_weight = 0;
LOG_INFO("Start button pressed, target=%lu.%lug",
(unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10));
update_display();
}
static void weight_dec_cb(lv_event_t *e)
static void reset_cb(lv_event_t *e)
{
(void)e;
if (is_running) return;
if (target_weight > 10) {
LOG_INFO("Reset button pressed, total packs=%lu", (unsigned long)pack_count);
is_running = false;
current_weight = 0;
pack_count = 0;
update_display();
}
static void minus_cb(lv_event_t *e)
{
(void)e;
if (is_running)
return;
if (target_weight > 10)
{
target_weight -= 10;
char weight_str[20];
snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10);
lv_label_set_text(set_weight_label, weight_str);
char tmp[20];
snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10));
lv_label_set_text(label_target_val, tmp);
LOG_INFO("Target weight decreased to %s", tmp);
}
}
static void weight_inc_cb(lv_event_t *e)
static void plus_cb(lv_event_t *e)
{
(void)e;
if (is_running) return;
if (target_weight < 1000) {
if (is_running)
return;
if (target_weight < 2000)
{
target_weight += 10;
char weight_str[20];
snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10);
lv_label_set_text(set_weight_label, weight_str);
char tmp[20];
snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10));
lv_label_set_text(label_target_val, tmp);
LOG_INFO("Target weight increased to %s", tmp);
}
}
static void update_status_display()
static void update_display(void)
{
if (is_running) {
lv_label_set_text(status_label, "运行中");
lv_obj_set_style_text_color(status_label, COLOR_GREEN, 0);
lv_label_set_text(lv_obj_get_child(start_btn, 0), "停止");
lv_obj_set_style_bg_color(start_btn, COLOR_RED, 0);
} else {
lv_label_set_text(status_label, "已停止");
lv_obj_set_style_text_color(status_label, COLOR_RED, 0);
lv_label_set_text(lv_obj_get_child(start_btn, 0), "启动");
lv_obj_set_style_bg_color(start_btn, COLOR_GREEN, 0);
char tmp[32];
snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(current_weight / 10), (unsigned long)(current_weight % 10));
lv_label_set_text(label_current_weight, tmp);
snprintf(tmp, sizeof(tmp), "%lu", (unsigned long)pack_count);
lv_label_set_text(label_pack_count, tmp);
if (is_running)
{
lv_label_set_text(label_status, "运行中");
lv_obj_set_style_text_color(label_status, COLOR_GREEN, 0);
lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0);
}
else
{
lv_label_set_text(label_status, "已停止");
lv_obj_set_style_text_color(label_status, COLOR_TEXT_SEC, 0);
lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0);
}
}
void packer_ui_update(void)
{
if (is_running) {
static uint32_t current_weight = 0;
if (current_weight < target_weight) {
current_weight += 5;
} else {
current_weight = 0;
pack_count++;
char count_str[20];
snprintf(count_str, sizeof(count_str), "%lu", pack_count);
lv_label_set_text(count_label, count_str);
}
char weight_str[20];
snprintf(weight_str, sizeof(weight_str), "%lu.%lug", current_weight / 10, current_weight % 10);
lv_label_set_text(weight_label, weight_str);
}
update_display();
}
void packer_ui_destroy(void)
{
if (packer_scr) {
if (packer_scr)
{
lv_obj_del(packer_scr);
packer_scr = NULL;
}
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""
Serial log capture tool for LSPi Project.
Captures device debug output and saves to timestamped log files.
Usage:
python tools/capture_log.py # Auto-detect COM port
python tools/capture_log.py --port COM3 # Specify port
python tools/capture_log.py --port COM3 --baud 9600 --output logs/
"""
import serial
import serial.tools.list_ports
import datetime
import argparse
import os
import sys
import signal
def find_device_port():
ports = serial.tools.list_ports.comports()
for p in ports:
if p.vid is not None:
return p.device
if ports:
return ports[0].device
return None
def main():
parser = argparse.ArgumentParser(description="LSPi Serial Log Capture")
parser.add_argument("--port", "-p", default=None, help="Serial port (e.g. COM3)")
parser.add_argument("--baud", "-b", type=int, default=9600, help="Baud rate (default: 9600)")
parser.add_argument("--output", "-o", default="logs", help="Output directory (default: logs)")
args = parser.parse_args()
port = args.port
if port is None:
port = find_device_port()
if port is None:
print("ERROR: No serial port found. Specify with --port")
sys.exit(1)
print(f"Auto-detected port: {port}")
os.makedirs(args.output, exist_ok=True)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = os.path.join(args.output, f"device_log_{timestamp}.txt")
latest_link = os.path.join(args.output, "latest.txt")
print(f"Connecting to {port} @ {args.baud} baud...")
print(f"Saving to: {log_file}")
print("Press Ctrl+C to stop capture")
print("-" * 60)
ser = serial.Serial(port, args.baud, timeout=0.1)
running = True
def handle_exit(sig, frame):
nonlocal running
print("\nCapture stopped.")
running = False
signal.signal(signal.SIGINT, handle_exit)
with open(log_file, "w", encoding="utf-8") as f:
f.write(f"LSPi Device Log Capture\n")
f.write(f"Port: {port} @ {args.baud} baud\n")
f.write(f"Started: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 60 + "\n")
while running:
try:
line = ser.readline().decode("utf-8", errors="replace").rstrip()
if line:
now = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
output = f"[{now}] {line}"
print(output)
f.write(output + "\n")
f.flush()
except serial.SerialException:
print("ERROR: Connection lost")
break
ser.close()
if os.path.exists(latest_link):
os.remove(latest_link)
try:
os.symlink(os.path.basename(log_file), latest_link)
except:
pass
print(f"\nLog saved to: {log_file}")
if __name__ == "__main__":
main()