实践案例 · 可复用模板
👉 关于作者

高可复用嵌入式AI编程模板
传感器采集 · RS485通讯 · 上位机管理

基于"五层进化"方法论的真实项目骨架,集成负日志、CLI调试助手与自动化闭环

itg · 公众号: 火星火箭 · 嵌入式AI工程化系列

1. 项目全景

本项目是一个开箱即用的嵌入式AI编程模板,包含:

  • 嵌入式主板(STM32F407):采集温湿度、压力等传感器,通过RS485总线与上位机通讯。
  • 上位机软件(Python/Qt):负责设备管理、数据存储、实时曲线显示。
  • Python调试助手(核心):同时监控RS485总线流量、控制板串口日志、上位机文件日志,并在适当时机输出AI可读的诊断总结。

整个项目严格遵循文章中的五层进化架构:结构化正/负日志、统一工程目录、多模型协作契约、CLI全自动闭环、Vibe+Verify模板化生成

2. 项目目录结构(统一上下文)

project/
├── firmware/                     # 嵌入式固件
│   ├── src/
│   │   ├── main.c
│   │   ├── sensor.c               # 传感器采集
│   │   ├── rs485.c                # RS485驱动
│   │   ├── protocol.c             # 通讯协议
│   │   └── logger.c               # 正/负日志引擎
│   ├── inc/
│   │   ├── logger.h
│   │   └── negative_logger.h
│   └── build/
│       └── CMakeLists.txt
├── host/                         # 上位机软件
│   ├── main.py                     # 主界面
│   ├── rs485_manager.py           # 串口通讯管理
│   ├── data_store.py              # 数据存储
│   └── logs/                      # 运行时日志目录
├── debug_tools/                 # 调试助手(Python脚本)
│   ├── debug_assistant.py         # 核心调试脚本(CLI)
│   └── log_parser.py              # 日志解析与总结
├── .vscode/
│   └── tasks.json                 # 一键自动化任务
├── agents.md                      # AI行为准则与项目上下文
└── README.md

3. 嵌入式固件:正/负日志实战

固件中集成了完整的结构化日志与负日志引擎,所有关键路径都添加了日志点。以下是RS485通讯任务的核心片段:

// firmware/src/protocol.c
void rs485_communication_task(void) {
    static uint8_t epoch = 0;
    LOG_STATE(WAITING_FOR_POLL, epoch);   // 正日志:记录状态

    // 等待上位机轮询帧(50ms超时)
    EXPECT_WITHIN(50, "POLL_FRAME", poll_received_flag);
    if (!poll_received_flag) {
        // 负日志已自动输出,此处仅记录额外上下文
        LOG("WARN", "Poll timeout, epoch=%d", epoch);
        return;
    }
    EVENT_OCCURRED("POLL_received");      // 正日志:事件发生

    // 采集传感器数据并回复
    float temp = read_temperature();
    float pressure = read_pressure();
    rs485_send_response(epoch, temp, pressure);
    LOG("INFO", "Response sent: temp=%.2f, press=%.2f, epoch=%d", temp, pressure, epoch);
    epoch++;
}
固件中同时使用了 EXPECT_WITHIN(负日志)和 EVENT_OCCURRED(正日志),保证了运行时状态对AI完全透明。

4. 上位机软件:通讯与管理

RS485管理器

负责串口打开/关闭、轮询帧发送、超时重试。内部同样集成了正/负日志(输出到文件)。

# host/rs485_manager.py
def poll_device(self):
    self.send_frame(CMD_POLL)
    t0 = time.time()
    while not self.response_received:
        if time.time() - t0 > 0.05:
            self.logger.negative("POLL_RESPONSE",
                f"Device {self.dev_id} not responding")
            break
        time.sleep(0.001)
    if self.response_received:
        self.logger.positive("POLL_RESPONSE",
            f"temp={self.temp}, press={self.press}")

数据管理

存储传感器历史数据,支持CSV导出。日志记录每次操作的成功/失败状态,便于AI诊断。

5. Python调试助手:三合一监控与自动总结

这是整个模板的灵魂脚本,它实现了对三个数据源的实时监控:

  1. RS485总线监听(通过USB转RS485适配器,监听模式)
  2. 控制板串口日志(通过另一个COM口读取固件printf输出)
  3. 上位机文件日志(实时tail -f,监控日志文件新增行)

脚本将所有日志统一为结构化格式,并在测试结束后、检测到负日志时、或定时周期自动输出AI可读的总结报告,方便复制给AI模型诊断。

#!/usr/bin/env python3
# debug_tools/debug_assistant.py
"""
三合一调试助手:监听RS485总线、控制板串口日志、上位机日志文件,
并按正/负日志规范输出统一的结构化日志与AI诊断总结。
用法:
    python debug_assistant.py --rs485 COM3 --mcu COM4 --host-log ../host/logs/app.log
选项:
    --summary-interval 10  每10秒自动输出一次总结(默认检测到负日志时立即输出)
    --output-summary-only  仅输出总结,不输出实时日志行
"""
import serial
import time
import re
import os
import argparse
from datetime import datetime
from collections import defaultdict

class DebugAssistant:
    def __init__(self, rs485_port, mcu_port, host_log_path, summary_interval=0):
        self.rs485 = serial.Serial(rs485_port, 9600, timeout=0.1) if rs485_port else None
        self.mcu = serial.Serial(mcu_port, 115200, timeout=0.1) if mcu_port else None
        self.host_log_path = host_log_path
        self.host_log_pos = 0
        self.summary_interval = summary_interval  # 0表示仅负日志触发
        self.negative_events = []
        self.positive_events = []
        self.last_summary_time = time.time()
        self.running = True

    def read_rs485_line(self):
        if self.rs485 and self.rs485.in_waiting:
            line = self.rs485.readline().decode('utf-8', errors='ignore').strip()
            if line:
                print(f"[RS485] {line}")
                self.categorize_log(line, source='RS485')

    def read_mcu_line(self):
        if self.mcu and self.mcu.in_waiting:
            line = self.mcu.readline().decode('utf-8', errors='ignore').strip()
            if line:
                print(f"[MCU] {line}")
                self.categorize_log(line, source='MCU')

    def read_host_log(self):
        if not os.path.exists(self.host_log_path):
            return
        with open(self.host_log_path, 'r', encoding='utf-8') as f:
            f.seek(0, 2)
            size = f.tell()
            if size > self.host_log_pos:
                f.seek(self.host_log_pos)
                new_lines = f.read().splitlines()
                self.host_log_pos = size
                for line in new_lines:
                    print(f"[HOST] {line}")
                    self.categorize_log(line, source='HOST')

    def categorize_log(self, line, source):
        """根据日志内容分类为正/负事件"""
        if 'NEGATIVE' in line or 'expected' in line.lower():
            self.negative_events.append((source, line))
        elif 'POSITIVE' in line or 'occurred' in line.lower():
            self.positive_events.append((source, line))

    def generate_summary(self):
        """生成AI可读的诊断总结"""
        summary = []
        summary.append("=" * 40)
        summary.append(f"🕒 调试总结 [{datetime.now().strftime('%H:%M:%S')}]")
        summary.append(f"📊 正事件数量: {len(self.positive_events)}")
        summary.append(f"⚠️ 负事件数量: {len(self.negative_events)}")
        if self.negative_events:
            summary.append("--- 负事件详情 (期望但未发生) ---")
            for src, evt in self.negative_events[-5:]:  # 最近5条
                summary.append(f"  [{src}] {evt}")
        if self.positive_events:
            summary.append("--- 最近正事件 ---")
            for src, evt in self.positive_events[-3:]:
                summary.append(f"  [{src}] {evt}")
        summary.append("=" * 40)
        return "\n".join(summary)

    def run(self):
        print("🚀 调试助手启动,按Ctrl+C停止...")
        try:
            while self.running:
                self.read_rs485_line()
                self.read_mcu_line()
                self.read_host_log()
                # 触发总结的条件:
                # 1. 检测到新的负事件立即输出
                # 2. 或者按间隔周期输出
                now = time.time()
                if self.negative_events and (self.summary_interval == 0 or now - self.last_summary_time > self.summary_interval):
                    print("\n" + self.generate_summary())
                    self.last_summary_time = now
                    # 仅保留最近的汇总,防止重复输出同一批
                    self.negative_events = self.negative_events[-1:]
                time.sleep(0.05)
        except KeyboardInterrupt:
            print("\n🛑 停止监控,输出最终总结:")
            print(self.generate_summary())

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="三合一调试助手")
    parser.add_argument("--rs485", help="RS485监听串口")
    parser.add_argument("--mcu", help="控制板日志串口")
    parser.add_argument("--host-log", help="上位机日志文件路径")
    parser.add_argument("--summary-interval", type=int, default=0, help="总结输出间隔(秒),0=仅负日志触发")
    args = parser.parse_args()
    assistant = DebugAssistant(args.rs485, args.mcu, args.host_log, args.summary_interval)
    assistant.run()
💡 这个脚本就是CLI闭环的物理载体:AI可以通过命令 python debug_tools/debug_assistant.py --rs485 COM3 --mcu COM4 --host-log host/logs/app.log 直接启动监控,并实时获取结构化总结,无需人工解读日志。

6. agents.md:AI协作契约

放入项目根目录,任何接入的AI助手都会自动遵守项目规范。

# agents.md - RS485传感器采集项目

## 项目概述
- 嵌入式平台:STM32F407,RS485总线9600bps
- 传感器:温湿度SHT30、压力传感器(模拟量)
- 上位机:Python/PyQt,通过USB-RS485适配器通信

## 状态枚举
| 数值 | 枚举名 | 含义 |
|------|--------|------|
| 0 | IDLE | 空闲 |
| 1 | WAITING_FOR_POLL | 等待上位机轮询 |
| 2 | SENDING_RESPONSE | 正在发送数据 |

## 行为准则
1. 所有日志分析优先查找 [NEGATIVE] 和 [POSITIVE] 标签
2. 上位机日志位于 host/logs/ 下,格式为结构化键值对
3. 调试助手启动命令:`python debug_tools/debug_assistant.py --rs485 COMx --mcu COMy --host-log host/logs/app.log`
4. 建议修改固件时,使用 git diff 格式输出
5. 遇到通讯超时,优先检查RS485总线终端电阻和设备地址

## 已知陷阱
- 轮询间隔必须 > 50ms,否则固件可能因中断过载丢帧
- RS485收发切换需延时2ms,避免总线竞争

7. VSCode Tasks:一键全自动测试

// .vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build-fw",
            "type": "shell",
            "command": "cd firmware/build && cmake --build .",
            "problemMatcher": ["$gcc"]
        },
        {
            "label": "flash",
            "dependsOn": ["build-fw"],
            "type": "shell",
            "command": "openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c 'program firmware/build/firmware.elf verify reset exit'"
        },
        {
            "label": "start-debug-assistant",
            "dependsOn": ["flash"],
            "type": "shell",
            "command": "python debug_tools/debug_assistant.py --rs485 COM3 --mcu COM4 --host-log host/logs/app.log",
            "isBackground": true,
            "problemMatcher": []
        },
        {
            "label": "run-host-app",
            "dependsOn": ["start-debug-assistant"],
            "type": "shell",
            "command": "python host/main.py"
        },
        {
            "label": "full-test",
            "dependsOn": ["run-host-app"],
            "group": { "kind": "test", "isDefault": true }
        }
    ]
}

8. 五层进化在本项目中的映射

层级文章方法本项目落地
第一招双向可观测性固件/上位机均使用正负日志宏;调试助手自动归类并总结
第二招统一上下文标准目录结构 + agents.md 提供完整项目语境
第三招多模型协作低代码模型生成模板,高能力模型分析调试总结
第四招CLI闭环debug_assistant.py 可被AI直接调用,输出结构化总结
第五招完整闭环工程VSCode Tasks 实现一键 full-test,案例库可积累日志样本

🎯 使用场景:当AI助手(如Claude、GPT)接入此项目时,只需读取 agents.md 和项目结构,即可自主运行 full-test 任务,读取调试助手输出的总结,并基于正/负日志定位问题。整个过程无需人工解释硬件细节。

这个模板可以直接克隆到任何RS485传感器采集项目中。只需修改传感器驱动和协议帧定义,其余工程化组件(日志、调试助手、agents.md、tasks.json)均可复用。真正的"一次搭建,终身受益"。