Project 11: CAN Bus Interface
Build a CAN bus sniffer and transmitter using an external MCP2515 controller over SPI, with decoding and error handling.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 3: Advanced |
| Time Estimate | 2-3 weeks |
| Main Programming Language | C |
| Alternative Programming Languages | MicroPython |
| Coolness Level | Level 4: Automotive Grade |
| Business Potential | 4. The “Industrial/Automotive” Tier |
| Prerequisites | SPI basics, interrupts, serial logging |
| Key Topics | SPI timing, CAN frames, filtering, error handling |
1. Learning Objectives
By completing this project, you will:
- Interface with MCP2515 over SPI reliably.
- Parse standard and extended CAN frames.
- Implement filtering and rate management.
- Handle CAN errors and bus-off recovery.
- Stream decoded frames to a host.
2. All Theory Needed (Per-Concept Breakdown)
2.1 SPI Mode, Timing, and Chip Select
Fundamentals
SPI is a synchronous serial bus with clock (SCK), master-out (MOSI), master-in (MISO), and chip-select (CS). Correct CPOL/CPHA settings are essential for reliable communication.
Deep Dive into the concept
SPI timing is defined by two parameters: clock polarity (CPOL) and clock phase (CPHA). These determine when data is sampled relative to clock edges. The MCP2515 typically requires SPI mode 0 (CPOL=0, CPHA=0). If you use the wrong mode, data appears shifted or corrupted. Chip-select timing matters: CS must be asserted for the entire command sequence. The MCP2515 uses command bytes followed by register addresses and data; splitting the transaction can lead to invalid reads or writes. You should verify SPI speed; too fast may violate MCP2515 timing. On the RP2040, use the SPI peripheral and configure the clock to a safe speed (e.g., 1-5 MHz) for initial testing. Always validate SPI with known registers (e.g., CANSTAT) before attempting CAN traffic.
How this fits on projects
SPI reliability is required for §3.2 and is validated in §3.7 demos.
Definitions & key terms
- CPOL/CPHA -> SPI timing mode
- Chip-select -> selects the target device
- Full-duplex -> data in/out simultaneously
Mental model diagram (ASCII)
CS: ____|-----------------|____
SCK: _-_-_-_-_-_-_-_-_-_-_-_
MOSI/MISO valid on edges
How it works (step-by-step)
- Assert CS low.
- Send command byte + address.
- Read/write data bytes.
- Deassert CS high.
Minimal concrete example
spi_write_blocking(spi0, cmd, 2);
spi_read_blocking(spi0, 0, buf, len);
Common misconceptions
- “SPI is self-configuring” -> you must match mode and speed.
- “CS can toggle between bytes” -> most devices require CS held low.
Check-your-understanding questions
- What happens if CPHA is wrong?
- Why keep CS low during a full transaction?
- How do you verify SPI works before CAN?
Check-your-understanding answers
- Data is sampled on the wrong edge and becomes corrupt.
- The device treats it as a new command and resets state.
- Read a known status register and verify bits.
Real-world applications
- Sensors, flash memory, display drivers, CAN controllers.
Where you’ll apply it
- In this project: §3.2, §5.10 Phase 1.
- Also used in: P05-smart-home-sensor-hub-wifi-connected-iot.md.
References
- MCP2515 datasheet
- RP2040 SPI documentation
Key insights
SPI reliability is the foundation; if SPI fails, CAN cannot work.
Summary
Match CPOL/CPHA and keep CS stable for correct MCP2515 communication.
Homework/Exercises to practice the concept
- Read MCP2515 CANSTAT register and decode mode bits.
- Measure SPI clock with a logic analyzer.
Solutions to the homework/exercises
- CANSTAT shows configuration or normal mode.
- Validate SCK frequency matches configured value.
2.2 CAN Frame Structure and Parsing
Fundamentals
CAN frames include an identifier, control bits, data length (DLC), and payload. Standard IDs are 11 bits; extended IDs are 29 bits. Correct parsing is required for meaningful data.
Deep Dive into the concept
A CAN data frame begins with a start-of-frame bit, followed by the identifier, RTR bit, control field, DLC, data bytes (0-8), CRC, and ACK. The MCP2515 handles bit-level timing; you read decoded frames from its RX buffers. For standard frames, the ID is 11 bits; for extended frames, 29 bits. You must parse the MCP2515 register layout correctly to reconstruct the ID and data. Filtering is done via masks and filters in the controller, which allows hardware-level acceptance of specific IDs. For high bus load, hardware filtering is essential. You should also timestamp frames so you can analyze timing. Parsing errors often come from incorrect bit shifts or ignoring extended IDs. The log output should clearly distinguish standard vs extended frames.
How this fits on projects
Frame parsing is core to §3.2 and used in §3.7 demos.
Definitions & key terms
- DLC -> data length code (0-8)
- RTR -> remote transmission request
- Standard ID -> 11-bit ID
- Extended ID -> 29-bit ID
Mental model diagram (ASCII)
SOF | ID | RTR | DLC | DATA | CRC | ACK | EOF
How it works (step-by-step)
- Read RX buffer registers.
- Determine standard vs extended frame.
- Extract ID, DLC, data bytes.
- Format for output or filtering.
Minimal concrete example
uint32_t id = (sid_high << 3) | (sid_low >> 5);
Common misconceptions
- “All CAN frames are 8 bytes” -> DLC can be 0-8.
- “Extended IDs are rare” -> many automotive networks use them.
Check-your-understanding questions
- What does DLC represent?
- How do extended IDs differ from standard IDs?
- Why timestamp frames?
Check-your-understanding answers
- Number of data bytes in the payload.
- Extended IDs use 29 bits and have extra control bits.
- For timing analysis and debugging.
Real-world applications
- Automotive diagnostics, industrial field buses, robotics.
Where you’ll apply it
- In this project: §3.2, §5.10 Phase 2.
- Also used in: P04-logic-analyzer-debug-digital-signals.md.
References
- CAN specification overview
- MCP2515 register map
Key insights
Correct frame parsing is about bit-level mapping, not just printing bytes.
Summary
Decode IDs and DLC correctly to interpret CAN traffic.
Homework/Exercises to practice the concept
- Parse a known MCP2515 register dump into a CAN frame.
- Implement display format for standard and extended frames.
Solutions to the homework/exercises
- Use datasheet bitfields to reconstruct ID.
- Prefix with “STD” or “EXT” in logs.
2.3 Error Handling, Bus-Off, and Filtering
Fundamentals
CAN includes error handling at the protocol level. Nodes track error counters and may enter bus-off state if too many errors occur. Filtering reduces CPU load by only accepting relevant frames.
Deep Dive into the concept
CAN controllers maintain transmit and receive error counters. If errors accumulate, the controller enters error passive or bus-off states, effectively disconnecting from the bus. Your firmware should detect these states and attempt recovery (e.g., reset the controller). Filtering uses masks and filters to accept specific IDs. For example, you can mask high bits to capture a range of IDs. Filtering is crucial on busy buses; without it, the MCU can be overwhelmed by interrupts. For logging tools, you may choose to accept all frames and filter in software, but you must still handle high traffic and potential buffer overflow. Implement a rate limiter or drop strategy when the host cannot keep up.
How this fits on projects
Error handling is required for §3.2 and is tested in §6.
Definitions & key terms
- Bus-off -> controller disconnects due to errors
- Error passive -> reduced participation due to errors
- Filter/mask -> hardware ID acceptance rules
Mental model diagram (ASCII)
Bus -> Filter -> RX Buffer -> MCU
How it works (step-by-step)
- Configure masks and filters.
- Monitor error counters.
- On bus-off, reset controller and re-init.
- Log errors and recovery events.
Minimal concrete example
if (canstat & BUS_OFF) reset_mcp2515();
Common misconceptions
- “CAN handles errors for you” -> you must detect and recover.
- “Filtering is optional” -> high traffic can overwhelm MCU.
Check-your-understanding questions
- What triggers bus-off?
- Why use hardware filtering?
- How do you recover from bus-off?
Check-your-understanding answers
- Excessive error counts.
- It reduces interrupts and CPU load.
- Reset controller and reinitialize bit timing.
Real-world applications
- Automotive diagnostics, industrial control networks.
Where you’ll apply it
- In this project: §3.2, §6.2 tests.
- Also used in: P05-smart-home-sensor-hub-wifi-connected-iot.md for error handling.
References
- CAN protocol error handling sections
Key insights
A CAN tool is only useful if it survives real bus errors.
Summary
Handle bus-off and use filters to keep traffic manageable.
Homework/Exercises to practice the concept
- Configure a filter to accept IDs 0x100-0x1FF.
- Simulate a bus-off condition and reset sequence.
Solutions to the homework/exercises
- Use mask 0x700 and filter 0x100.
- Increment error counters and observe bus-off flag.
3. Project Specification
3.1 What You Will Build
A CAN sniffer/transmitter that reads frames via MCP2515, decodes IDs and data, and outputs logs over USB serial.
3.2 Functional Requirements
- SPI interface: reliable MCP2515 communication.
- Frame parsing: standard and extended IDs.
- Filtering: hardware and software filters.
- Error handling: detect and recover from bus-off.
- Transmit: send a test frame on demand.
3.3 Non-Functional Requirements
- Performance: handle 100+ frames/sec without loss.
- Reliability: recover from bus-off within 5 seconds.
- Usability: readable logs with timestamps.
3.4 Example Usage / Output
[CAN] RX id=0x123 dlc=8 data=11 22 33 44 55 66 77 88
[CAN] RX id=0x7DF dlc=8 data=02 01 0C 00 00 00 00 00
3.5 Data Formats / Schemas / Protocols
[CAN] ts=123456 id=0x123 std dlc=8 data=...
3.6 Edge Cases
- Bus speed mismatch -> no frames; log warning.
- Missing termination -> error frames and bus-off.
- SPI noise -> corrupted registers.
3.7 Real World Outcome
You can attach to a CAN bus and see decoded frames in real time.
3.7.1 How to Run (Copy/Paste)
cmake .. && make -j4
picotool load -f can_sniffer.uf2
3.7.2 Golden Path Demo (Deterministic)
- Send a known test frame from a CAN generator.
- Verify the sniffer logs the correct ID and data.
3.7.3 Failure Demo (Bad Input)
- Scenario: no termination resistors.
- Expected result: error frames and
[ERROR] bus-offlog.
3.7.4 If CLI: exact terminal transcript
$ minicom -b 115200 -o -D /dev/ttyACM0
[CAN] RX id=0x123 dlc=8 data=11 22 33 44 55 66 77 88
4. Solution Architecture
4.1 High-Level Design
CAN Bus -> MCP2515 -> SPI -> MCU -> Parser -> Logger -> Host
4.2 Key Components
| Component | Responsibility | Key Decisions | |———–|—————-|—————| | SPI Driver | MCP2515 comms | Mode 0, safe speed | | CAN Parser | Decode frames | Support standard+extended | | Filter | Accept relevant IDs | Hardware filters | | Error Handler | Recover bus-off | Auto reset | | Logger | Output frames | Timestamped logs |
4.3 Data Structures (No Full Code)
typedef struct {
uint32_t id;
uint8_t dlc;
uint8_t data[8];
uint8_t flags;
} can_frame_t;
4.4 Algorithm Overview
Key Algorithm: Receive + Decode Loop
- Wait for MCP2515 RX interrupt.
- Read RX buffer via SPI.
- Parse ID, DLC, data.
- Log or forward frame.
Complexity Analysis:
- Time: O(1) per frame
- Space: O(1) per frame
5. Implementation Guide
5.1 Development Environment Setup
# Pico SDK + MCP2515 driver
5.2 Project Structure
can-sniffer/
├── firmware/
│ ├── main.c
│ ├── mcp2515.c
│ ├── can_parse.c
│ └── logger.c
└── README.md
5.3 The Core Question You’re Answering
“How do you bridge a complex industrial bus into a simple embedded system?”
5.4 Concepts You Must Understand First
- SPI mode and timing
- CAN frame structure
- Error handling and filters
5.5 Questions to Guide Your Design
- What bus speed will you support?
- Will you filter by IDs in hardware or software?
- How will you handle high bus load?
5.6 Thinking Exercise
Compute frame rate for 500 kbps and 128-bit frames.
5.7 The Interview Questions They’ll Ask
- What is the difference between standard and extended IDs?
- Why might SPI transfers fail?
- How do you recover from bus-off?
5.8 Hints in Layers
Hint 1: Verify SPI with MCP2515 CANSTAT register. Hint 2: Configure bit timing for the correct baud. Hint 3: Read RX buffer on interrupt. Hint 4: Add filters and error logs.
5.9 Books That Will Help
| Topic | Book | Chapter | |——-|——|———| | Serial protocols | “The Book of I2C” | Ch. 3 | | Embedded systems | “Making Embedded Systems” | Ch. 9 | | Debugging | “The Art of Debugging with GDB” | Ch. 2 |
5.10 Implementation Phases
Phase 1: Foundation (3-5 days)
- Bring up SPI and read registers. Checkpoint: CANSTAT reports normal/config mode.
Phase 2: Core Functionality (5-7 days)
- Configure CAN bit timing and parse frames. Checkpoint: frames appear in logs.
Phase 3: Filtering & Errors (3-5 days)
- Add filters and bus-off recovery. Checkpoint: survives error injection.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Bit rate | 125k/250k/500k | 500k (common automotive) | Practical target | | Filtering | Hardware vs software | Hardware | Reduces CPU load | | Logging | ASCII vs binary | ASCII for MVP | Easier debugging |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples | |———-|———|———-| | Unit Tests | Frame parsing | Known register dumps | | Integration Tests | Live bus capture | Test bench bus | | Edge Case Tests | Bus errors | Missing termination |
6.2 Critical Test Cases
- Standard frame: ID 0x123 parsed correctly.
- Extended frame: 29-bit ID parsed correctly.
- Bus-off recovery: reset and resume.
6.3 Test Data
ID=0x7DF DLC=8 DATA=02 01 0C 00 00 00 00 00
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |———|———|———-| | Wrong SPI mode | Garbage registers | Set CPOL=0, CPHA=0 | | Missing termination | Errors/bus-off | Add 120 ohm termination | | Ignoring filters | CPU overload | Configure masks and filters |
7.2 Debugging Strategies
- Use a CAN generator for known frames.
- Log error counters regularly.
7.3 Performance Traps
- Printing every frame at high speed can overwhelm serial output.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add timestamped CSV export.
- Add basic transmit command over serial.
8.2 Intermediate Extensions
- Add CAN FD support (if hardware allows).
- Add PC GUI viewer.
8.3 Advanced Extensions
- Implement ISO-TP decoding.
- Bridge CAN to USB-CAN tools.
9. Real-World Connections
9.1 Industry Applications
- Automotive diagnostics and ECU testing.
- Industrial automation buses.
9.2 Related Open Source Projects
- cantools and python-can
9.3 Interview Relevance
- CAN protocol knowledge is valuable in automotive roles.
10. Resources
10.1 Essential Reading
- MCP2515 datasheet
- CAN protocol overview
10.2 Video Resources
- CAN bus fundamentals courses
10.3 Tools & Documentation
- USB-CAN adapters for testing
10.4 Related Projects in This Series
- P04-logic-analyzer-debug-digital-signals.md for protocol decoding
- P05-smart-home-sensor-hub-wifi-connected-iot.md for error handling
11. Self-Assessment Checklist
11.1 Understanding
- I can explain SPI modes and timing.
- I can parse CAN frames accurately.
- I can handle bus-off recovery.
11.2 Implementation
- Frames are logged reliably.
- Filters reduce CPU load.
- Errors are detected and reported.
11.3 Growth
- I can add ISO-TP decoding.
- I can integrate with PC tools.
12. Submission / Completion Criteria
Minimum Viable Completion:
- Receive and log CAN frames at 500 kbps.
Full Completion:
- Transmit frames and recover from bus-off.
Excellence (Going Above & Beyond):
- ISO-TP decoder and GUI integration.
13. Additional Content Rules
13.1 Determinism
Use fixed baud rate and fixed filter set for demo. Log timestamps with each frame.
13.2 Outcome Completeness
- Success demo: §3.7.2
- Failure demo: §3.7.3
- CLI exit codes: host parser returns
0success,2serial open failure,4parse error.
13.3 Cross-Linking
Concept references in §2.x and related projects in §10.4.
13.4 No Placeholder Text
All sections are fully specified for this project.