项目开源地址:https://github.com/1919884213/ESP32-LCD-CAN-IAP(MIT License)
技术栈:ESP32-S3 + LVGL 9 + ESP-IDF 5.x | STM32F103C8T6 ×2 | CAN 2.0A @ 50 kbit/s | FreeRTOS / 自研 TinyRTOS
一、项目是做什么的
这是一套「1 个上位机 + 2 个下位机节点」的车载仪表演示系统,核心卖点一句话:
通过 CAN 总线对下位机 Bootloader 做远程固件升级(IAP),全程不拆机、不接 SWD。
系统分工:
- 上位机(ESP32-S3):LVGL 9 仪表盘 UI(320×240,10 个页面)、WiFi/MQTT(OneNET)、和风天气、智谱 AI 流式对话、OTA 自升级,并通过 TWAI 外设收发 CAN 报文;
- 下位机(STM32F103C8T6 ×2):
- 环境节点:DHT11 温湿度、GL5528 光照、NTC;
- 运动节点:MPU6050 姿态、超声波测距、霍尔轮速;
- 每块 STM32 都是Bootloader(0x08000000)+ APP(0x08004000)双区结构,上位机从本地 bin / W25Q64 / HTTP 拉取固件,走 CAN 协议刷入。
还有一个我觉得比较有意思的点:下位机 APP 提供两套运行时——标准 FreeRTOS 版(CanDevice*)和自研 TinyRTOS 版(myCanDevice*),后者通过 CMSIS-RTOS v2 兼容层无缝替换内核,业务任务代码一行不改,固件体积从 29.8 KB 降到 19.5 KB。
二、系统架构
┌──────────────────────────────┐ WiFi / MQTT │ ESP32-S3 上位机 │ ┌──────────────│ LVGL 9 仪表盘 · TWAI(CAN) │──── OTA (HTTPS) │ │ WiFi/MQTT(OneNET) · LLM对话 │ │ └──────────┬───────────────────┘ │ │ CAN 总线 @ 50 kbit/s │ ┌──────────┴──────────┐ │ ┌────────▼────────┐ ┌────────▼────────┐ │ │ STM32F103 节点1 │ │ STM32F103 节点2 │ │ │ 环境节点 │ │ 运动节点 │ │ │ Bootloader+APP │ │ Bootloader+APP │ │ │ DHT11/光照/NTC │ │ MPU6050/超声波/霍尔│ │ └─────────────────┘ └─────────────────┘三、CAN IAP 协议设计(重点)
完整报文表见仓库myCanDevice2/CAN总线报文ID汇总_v2.0.0 (1).md,要点:
- CAN 2.0A 标准帧,11 位 ID,DLC=8,50 kbit/s,多字节字段小端序;
- IAP 流程四步走:握手擦除 → 分块传输(带序号 + CRC)→ 校验 → 激活重启;
- 节点侧升级指令 ID:运动节点
0x600/0x610/0x620,环境节点0x601/0x611/0x621; - 业务数据帧举例:环境节点上报
0x200/0x201,故障帧0x080;运动节点上报0x100/0x101/0x110/0x701。
Bootloader 的判断逻辑:上电后先等一小段时间的 CAN IAP 指令,没收到就校验 APP 区有效标志并跳转0x08004000;APP 有效标志持久化保存,升级失败不会变砖。板载 PC13 LED 可以直观区分当前跑在哪边——闪烁 = Bootloader,常亮 = APP。
四、上位机功能一览
| 模块 | 说明 |
|---|---|
| 仪表盘 UI | LVGL 9,320×240 运动仪表风主题(碳黑 + 橙),10 个功能页面 |
| CAN 通信 | ESP32 TWAI 驱动,50 kbit/s,收发环境/运动节点业务帧 |
| IAP 升级 | 本地 / W25Q64 / HTTP 三种固件源,CAN 协议刷入,带 CRC 校验 |
| OTA 升级 | 上位机自身 HTTPS OTA(自定义分区表,factory 单分区 3.9 MB) |
| 云端 | OneNET MQTT 物模型上报;和风天气;智谱 AI 流式对话 |
| WiFi | 软键盘配网、连接状态机、NVS 持久化凭据 |
| 存储 | W25Q64:GIF 待机动画帧、中文字库、OTA/IAP 固件缓存;FATFS |
五、TinyRTOS 版与 CMSIS-OS2 兼容层
myCanDevice*的内核是我自己写的 TinyRTOS:
- 位图调度器 + 协程式上下文切换(
switch.s,PendSV); - 软件定时器、队列、信号量、优先级继承互斥锁;
- 关键是
Core/rtos/os_compat.c:实现CMSIS-RTOS v2 → TinyRTOS 兼容层,既有的 CanTask / MPU6050Task / UltrasonicTask / freertos.c一行不改就能跑在自研内核上,这也让"换内核"的对比测试变得非常方便——同一个 IAP 协议、同一套上位机,两个内核的固件都可以刷。
六、快速上手
1. 下位机(每块 STM32 板)
- 编译烧录 Bootloader:
BootLoader_device1(或 2)→0x08000000; - 编译 APP(二选一):
- FreeRTOS 版:
CanDevice1/CanDevice2(CMake + arm-none-eabi-gcc,或 STM32CubeIDE 导入); - TinyRTOS 版:
myCanDevice1/myCanDevice2;
- FreeRTOS 版:
- APP 烧到
0x08004000——也可以偷懒:直接用上位机 IAP 页面通过 CAN 刷入仓库里自带的LCD/*.bin。
2. 上位机(ESP32-S3)
cdLCD idf.py set-target esp32s3 idf.py build flash monitor首次使用必做:main/secrets.h不入库,克隆后复制main/secrets.h.example为main/secrets.h,填入:
- WiFi 热点 SSID / 密码(UI 网络页也可现场改);
- 智谱 AI API Key(LLM 对话页);
- 和风天气 API Key(天气页);
- OneNET 产品/设备三元组(不用可留占位值,连接失败不影响其他功能)。
3. IAP 演示
上位机 IAP 页面选择节点(0x600/0x601)与固件源(本地 bin / W25Q64 / HTTP),点升级即可看到进度条;CRC 校验通过后 Bootloader 跳转新 APP。
七、开发环境
- ESP32-S3:ESP-IDF ≥ 5.1(推荐 5.2/5.3),LVGL 9.x(managed component);
- STM32F103:arm-none-eabi-gcc 12+ 与 CMake(
CMakePresets.json),或 STM32CubeIDE;.ioc可重新生成 CubeMX 工程; - 另附
BootLoader_device*/tools/can_iap_flash.py,可用 CAN 口直接刷 Bootloader 初始镜像。
八、写在最后
这个项目把嵌入式求职常被问到的几块硬骨头串成了一条完整的链路:
LVGL 多页 UI + TWAI/CAN 驱动 + Bootloader/双区 IAP + CRC 校验 + OTA + MQTT 物模型 + 自研 RTOS 内核,而且每个环节都有实机可演示。仓库是多个子工程的合并快照,结构清晰,欢迎 Star / Fork / 提 Issue 交流。
再放一次地址:https://github.com/1919884213/ESP32-LCD-CAN-IAP