Skip to content

Commit 414e6e6

Browse files
author
naahm003
committed
Update README to reflect project scope and modular architecture enhancements
1 parent 5ee7cfd commit 414e6e6

File tree

1 file changed

+237
-80
lines changed

1 file changed

+237
-80
lines changed

README.md

Lines changed: 237 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,270 @@
1-
# CAN Lecture RX Firmware — Modular Architecture (with Message Router)
1+
# CAN Bus Demo with ESP32, LVGL, and DBC Code Generation
22

3-
This README describes the implemented RX firmware architecture. It replaces the earlier refactor plan with the current, working design that introduces a MessageRouter for modular fan-out of CAN messages and a dedicated IO relay module for blinkers.
3+
A demonstration project showcasing **CAN bus communication** between two ESP32 boards with **LVGL GUI**, **DBC-based code generation**, and a **modular firmware architecture**. This project is ideal for learning CAN protocols, embedded UI development, and code generation workflows.
44

5-
## High-level data flow
5+
---
66

7-
1) CAN ISR (esp32_can) → EventQueue
8-
- `CanInterface` sets up pins, bitrate, and mailbox filtering for `Cluster` message (ID 0x65).
9-
- ISR validates ID/DLC/IDE and unpacks the frame into `Cluster_t` (generated from DBC), then pushes `Event::ClusterFrame` to a FreeRTOS queue (`EventQueue`).
7+
## 🎯 What This Project Demonstrates
108

11-
2) SystemController (state machine) → MessageRouter
12-
- `SystemController::Dispatch(EventType::ClusterFrame)` handles state transitions (`WaitingForData``Active`, recovery from `Degraded`).
13-
- It unconditionally publishes the `Cluster_t` to the `MessageRouter` with a millisecond timestamp.
9+
1. **CAN Bus Communication**
10+
- Dual ESP32 setup: one **TX board** (transmitter) and one **RX board** (receiver)
11+
- 500 kbps CAN communication using ESP32's built-in CAN controller
12+
- Message filtering, unpacking, and event-driven processing
1413

15-
3) MessageRouter (central pub/sub)
16-
- Typed topic for `Cluster_t` with subscribe/unsubscribe and a sticky last-value cache.
17-
- Maintains a last-seen timestamp for freshness checks.
14+
2. **DBC-Driven Code Generation**
15+
- Define CAN messages in a standard `.dbc` file
16+
- Auto-generate C code for encoding/decoding using `c-coderdbc`
17+
- Type-safe message handling with generated structs
1818

19-
4) Subscribers consume from the router
20-
- `UiController`: subscribed via a small adapter, maps `Cluster_t` to `UiData` and enqueues it to the UI task (LVGL thread).
21-
- `IOModule` (blinkers): subscribed; drives two relay GPIOs for left/right indicators with its own blink state machine (independent of message rate).
22-
- `HealthMonitor`: pull model; no longer notified by subscribers. It queries the router’s last-seen timestamp to detect staleness and emits `FrameTimeout` events.
19+
3. **LVGL Graphical UI (RX Board)**
20+
- Real-time display of CAN data on TFT touchscreen
21+
- SquareLine Studio integration for UI design
22+
- Arc gauge for speed, turn signal indicators
2323

24-
5) SystemController::Update()
25-
- Periodically asks `HealthMonitor` to check for timeouts (pulling timestamps from the router) and transitions to `Degraded` on staleness.
24+
4. **Modular Firmware Architecture**
25+
- Event-driven state machine with message routing
26+
- Pub/sub pattern for decoupled components
27+
- Health monitoring with automatic degradation and recovery
2628

27-
## State machine
29+
---
2830

29-
`SystemState` lifecycle is unchanged but simplified by the router:
31+
## 🚀 Quick Start
3032

31-
- Boot → DisplayInit → WaitingForData → Active
32-
- Active ⇄ Degraded on `FrameTimeout` and recovery when frames resume
33-
- Fault for unrecoverable errors
33+
### Prerequisites
3434

35-
`SystemController` still owns transitions and UI screen changes; modules subscribe to data via the router rather than being hard-wired in the controller.
35+
1. **Hardware**
36+
- 2x ESP32 boards (e.g., ESP32-EVB or similar)
37+
- CAN transceivers (e.g., SN65HVD230 or MCP2551)
38+
- TFT display with touch (for RX board)
39+
- CAN bus wiring (120Ω termination resistors recommended)
3640

37-
## Modules (responsibilities and locations)
41+
2. **Software**
42+
- [PlatformIO](https://platformio.org/) (VS Code extension or CLI)
43+
- [Python 3.x](https://www.python.org/) (for optional documentation builds)
44+
- Git (to clone this repository)
3845

39-
- CanInterface (`src/rx/CanInterface.{h,cpp}`)
40-
- Configures CAN and registers ISR.
41-
- Unpacks `Cluster_t` in ISR and pushes `Event::ClusterFrame` to `EventQueue`.
46+
### Installation
4247

43-
- EventQueue (`src/rx/EventQueue.{h,cpp}`)
44-
- FreeRTOS queue wrapper for typed events from ISR and other producers.
48+
1. **Clone the repository**
49+
```bash
50+
git clone https://github.com/rvxfahim/CAN-Demo-ESP32.git
51+
cd CAN-Demo-ESP32
52+
```
4553

46-
- MessageRouter (`src/common/MessageRouter.{h,cpp}`)
47-
- Typed subscription API: `SubscribeCluster`, `PublishCluster`.
48-
- Sticky last-value cache and `GetLastSeenMs()` for freshness.
49-
- All subscribers run in task context (publish is called from main loop via controller dispatch).
54+
2. **Configure serial ports** (if different from defaults)
55+
Edit `platformio.ini` and set your COM ports:
56+
```ini
57+
[env:rx_board]
58+
upload_port = COM8 ; Change to your RX board port
59+
monitor_port = COM8
60+
61+
[env:tx_board]
62+
upload_port = COM9 ; Change to your TX board port
63+
monitor_port = COM9
64+
```
5065

51-
- UiController (`src/rx/UiController.{h,cpp}`)
52-
- Initializes LVGL, TFT_eSPI, touch, and SquareLine-generated UI.
53-
- Dedicated UI task with queues; subscribed to router for `Cluster` updates (via adapter in `main.cpp`).
66+
3. **Build and upload**
67+
68+
**RX Board** (receiver with display):
69+
```bash
70+
pio run -e rx_board -t upload
71+
pio device monitor -e rx_board
72+
```
73+
74+
**TX Board** (transmitter):
75+
```bash
76+
pio run -e tx_board -t upload
77+
pio device monitor -e tx_board
78+
```
5479

55-
- IOModule (`src/rx/IOModule.{h,cpp}`)
56-
- Drives two relay outputs for left/right turn indicators.
57-
- Subscribes to `Cluster` via router.
58-
- Internal blink state machine at 1 Hz (500 ms ON / 500 ms OFF), decoupled from message rate.
59-
- Safe default OFF if no update for >1s.
60-
- Relay pins configurable in `include/IOPins.h` (defaults: GPIO 25 and 26).
80+
4. **Connect the hardware**
81+
- Wire CAN_H and CAN_L between boards through transceivers
82+
- Connect 120Ω termination resistors at both ends
83+
- Power both boards
84+
- TX will start sending `Cluster` frames; RX display will update
6185

62-
- HealthMonitor (`src/rx/HealthMonitor.{h,cpp}`)
63-
- Pull model for staleness: `CheckTimeout(EventQueue&, const MessageRouter&)` reads `GetLastSeenMs()` and emits `FrameTimeout` if stale (default 1500 ms, configurable).
86+
---
6487

65-
- SystemController (`src/rx/SystemController.{h,cpp}`)
66-
- Orchestrates boot, display init, waiting/active/degraded/fault.
67-
- Publishes `Cluster_t` to router on each frame event and handles state transitions.
88+
## 📖 Understanding the DBC Workflow
6889

69-
- Entry points (`src/rx/main.cpp`)
70-
- Instantiates modules and runs the boot sequence.
71-
- Subscribes UI to router; starts IO module subscription.
72-
- Calls `ioModule.Update(millis())` for blink timing and `systemController.Update()` for health.
90+
### What is a DBC File?
7391

74-
## Message formats and DBC integration
92+
A **DBC** (CAN Database) file is an industry-standard format for defining CAN messages, signals, and their properties. It serves as the **single source of truth** for all CAN communication in this project.
7593

76-
- Source of truth: `tools/Lecture.dbc`.
77-
- Code generation: `lib/Generated/lib/lecture.{c,h}` (do not edit generated files).
78-
- Wrapper: `src/generated_lecture_dbc.c` includes generated code; use `Cluster_t`, `Pack_Cluster_lecture`, `Unpack_Cluster_lecture`.
94+
**Location:** `tools/Lecture.dbc`
7995

80-
## Extending the system
96+
### Code Generation Process
8197

82-
- To add a new consumer of `Cluster` data, subscribe via `MessageRouter::SubscribeCluster(cb, ctx)` and process updates in task context.
83-
- For additional CAN messages/IDs:
84-
- Extend `CanInterface` to push new events.
85-
- Add typed topics (similar to `Cluster`) in `MessageRouter` or expose per-ID raw subscriptions.
86-
- Have modules subscribe to those topics rather than plumbing through `SystemController`.
98+
This project uses **c-coderdbc** to auto-generate C encode/decode functions from the DBC file.
8799

88-
## Build, upload, monitor (PlatformIO)
100+
#### Step 1: Edit the DBC File
89101

90-
- Build RX: `pio run -e rx_board`
91-
- Upload RX: `pio run -e rx_board -t upload`
92-
- Monitor RX: `pio device monitor -e rx_board`
102+
The example `Lecture.dbc` defines a `Cluster` message (ID 0x65) with signals:
103+
- `Speed` (12-bit): 0–4095 range
104+
- `Left_Turn_Signal` (1-bit): boolean
105+
- `Right_Turn_Signal` (1-bit): boolean
93106

94-
Serial ports and speeds are set in `platformio.ini` per environment.
107+
You can edit this file with any text editor or use tools like [CANdb++ Editor](https://vector.com/) or [SavvyCAN](https://www.savvycan.com/).
95108

96-
## Notable implementation details
109+
#### Step 2: Run the Code Generator
97110

98-
- ISR is minimal and never calls subscribers; it only validates, unpacks, and queues events.
99-
- Router timestamps are in milliseconds (`millis()`), providing a single source of truth for freshness.
100-
- UI and IO operate fully from router data; controller no longer pushes UI payloads directly.
101-
- Blinkers are immune to CAN update rate; they align phase on rising edges and toggle at a fixed cadence.
111+
**Windows:**
112+
```powershell
113+
cd tools\c-coderdbc
114+
.\build\coderdbc.exe -dbc ..\Lecture.dbc -out ..\..\lib\Generated
115+
```
102116

103-
## Troubleshooting
117+
This regenerates:
118+
- `lib/Generated/lib/lecture.c` and `lecture.h` (generated code, **do not edit**)
119+
- Helper files in `lib/Generated/conf/`, `lib/Generated/inc/`, etc.
104120

105-
- If the UI doesn’t update: ensure UI task started before router subscription (it is subscribed after `RunBootSequence()` in `main.cpp`).
106-
- If blinkers don’t actuate: verify relay pins in `include/IOPins.h` match hardware; confirm `Cluster.Left_Turn_Signal`/`Right_Turn_Signal` bits from TX.
107-
- If system drops to Degraded: router last-seen likely stale; check TX is sending `Cluster` frames and bus wiring.
121+
**Linux/macOS:**
122+
You'll need to compile `c-coderdbc` from source (see `tools/c-coderdbc/README.md`).
108123

109-
## References
124+
#### Step 3: Use Generated Types in Code
110125

111-
- LVGL v9.1
112-
- esp32_can (custom wrapper in `lib/CanDriver`)
113-
- DBC generator under `tools/c-coderdbc/`
126+
The firmware uses the generated types exclusively:
127+
128+
```cpp
129+
#include "lecture.h" // Generated header
130+
131+
// Packing (TX side)
132+
Cluster_t cluster = {0};
133+
cluster.Speed = 2048;
134+
cluster.Left_Turn_Signal = 1;
135+
cluster.Right_Turn_Signal = 0;
136+
137+
uint8_t data[8];
138+
Pack_Cluster_lecture(&cluster, data, 8);
139+
CAN0.sendFrame({ .identifier = 0x65, .data = data, ... });
140+
141+
// Unpacking (RX side)
142+
Cluster_t received;
143+
Unpack_Cluster_lecture(&received, frame.data, frame.data_length_code);
144+
```
145+
146+
**Key principle:** Never manually parse CAN bytes. Always use `Pack_*` and `Unpack_*` functions.
147+
148+
---
149+
150+
## 🏗️ Project Structure
151+
152+
```
153+
CAN-Demo-ESP32/
154+
├── platformio.ini # Build config (two environments: rx_board, tx_board)
155+
├── tools/
156+
│ ├── Lecture.dbc # CAN message definitions (source of truth)
157+
│ └── c-coderdbc/ # DBC-to-C code generator
158+
├── lib/
159+
│ ├── Generated/ # Auto-generated C code (from DBC)
160+
│ ├── CanDriver/ # ESP32 CAN driver abstraction
161+
│ ├── Ui/ # LVGL UI (SquareLine Studio exports)
162+
│ └── TouchLibrary/ # Touch controller drivers
163+
├── src/
164+
│ ├── common/ # Shared code (MessageRouter, etc.)
165+
│ ├── rx/ # RX board firmware (main + modules)
166+
│ ├── tx/ # TX board firmware (main only)
167+
│ └── generated_lecture_dbc.c # Single include wrapper for DBC code
168+
├── include/ # Global headers (IOPins, TFT config)
169+
├── docs/ # Sphinx documentation source
170+
└── README.md # This file
171+
```
172+
173+
---
174+
175+
## 📚 Documentation
176+
177+
For **detailed architecture, sequence diagrams, and API references**, see the full documentation:
178+
179+
### 🌐 [**GitHub Pages Documentation**](https://rvxfahim.github.io/CAN-Demo-ESP32/)
180+
181+
Topics covered:
182+
- **Getting Started:** Detailed setup and hardware connections
183+
- **Architecture:** Component diagrams, state machines, data flow
184+
- **CAN & DBC:** In-depth DBC workflow and regeneration steps
185+
- **Testing:** Unit test guidelines and test hooks
186+
- **API Reference:** Doxygen-generated class/function documentation
187+
188+
---
189+
190+
## 🔧 Configuration and Customization
191+
192+
### CAN Parameters
193+
- **Bitrate:** 500 kbps (configurable in `CanInterface.cpp`)
194+
- **Pins:** GPIO 35 (RX), GPIO 5 (TX) — change in `CanInterface::Initialize()`
195+
- **Message ID:** `0x65` for `Cluster` (defined in DBC)
196+
197+
### Display and UI
198+
- **Screen:** Configured in `include/TFTConfiguration.h` and `lib/Ui/`
199+
- **UI Design:** Edit with [SquareLine Studio](https://squareline.io/), export to `lib/Ui/`
200+
- **Widgets:** Arc gauge (`ui_Arc1`), labels (`ui_RightLabel`, `ui_LeftLabel`)
201+
202+
### IO Relays (Blinkers)
203+
- **Pins:** GPIO 25 (left), GPIO 26 (right) — see `include/IOPins.h`
204+
- **Blink Rate:** 1 Hz (500ms ON/OFF) — adjust in `IOModule::Update()`
205+
206+
### Health Monitoring
207+
- **Timeout:** 1500ms (RX declares `Degraded` if no CAN frames) — see `HealthMonitor.cpp`
208+
209+
---
210+
211+
## 🧪 Testing and Troubleshooting
212+
213+
### Common Issues
214+
215+
**RX display doesn't update:**
216+
- Verify TX is sending frames (check TX serial monitor)
217+
- Check CAN wiring and termination resistors
218+
- Ensure both boards have matching bitrate (500 kbps)
219+
220+
**Blinkers don't work:**
221+
- Confirm relay pins in `include/IOPins.h` match your hardware
222+
- Verify `Left_Turn_Signal` / `Right_Turn_Signal` bits are set in TX
223+
224+
**System shows "Degraded":**
225+
- Normal behavior when CAN frames stop arriving (timeout threshold)
226+
- Should auto-recover when TX resumes
227+
228+
### Build Errors
229+
230+
**Missing LVGL or TFT_eSPI:**
231+
PlatformIO will auto-install dependencies from `lib_deps` in `platformio.ini`.
232+
233+
**Linker errors about `Cluster_t`:**
234+
Ensure `src/generated_lecture_dbc.c` is included in the build (check `build_src_filter`).
235+
236+
---
237+
238+
## 🤝 Contributing
239+
240+
Contributions welcome! Please:
241+
1. Fork the repository
242+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
243+
3. Commit changes (`git commit -m 'Add amazing feature'`)
244+
4. Push to branch (`git push origin feature/amazing-feature`)
245+
5. Open a Pull Request
246+
247+
---
248+
249+
## 📄 License
250+
251+
This project is licensed under the MIT License. See `LICENSE` file for details.
252+
253+
---
254+
255+
## 🔗 Key Technologies
256+
257+
- **[PlatformIO](https://platformio.org/)** — Cross-platform embedded build system
258+
- **[LVGL v9.1](https://lvgl.io/)** — Lightweight graphics library
259+
- **[TFT_eSPI](https://github.com/Bodmer/TFT_eSPI)** — Fast TFT display driver
260+
- **[SquareLine Studio](https://squareline.io/)** — Drag-and-drop LVGL UI designer
261+
- **[c-coderdbc](https://github.com/astand/c-code-generator-from-dbc)** — DBC-to-C code generator
262+
- **ESP32 Arduino** — Arduino framework for ESP32
263+
264+
---
265+
266+
## 📞 Questions or Support
267+
268+
- **Documentation:** https://rvxfahim.github.io/CAN-Demo-ESP32/
269+
- **Issues:** [GitHub Issues](https://github.com/rvxfahim/CAN-Demo-ESP32/issues)
270+
- **Repository:** [github.com/rvxfahim/CAN-Demo-ESP32](https://github.com/rvxfahim/CAN-Demo-ESP32)

0 commit comments

Comments
 (0)