CAN Bus Mini-Workshop — ESP32 + Arduino UNO + MCP2515
What is it?
A hands-on, beginner-friendly workshop to learn CAN bus basics through three short tasks and a final game. Teams program ESP32 nodes while a facilitator runs the CAN master (Arduino UNO).
This workshop is designed for learning through doing. You'll split into 3 teams (up to 4 people each), where each team programs an ESP32 node equipped with a DS18B20 temperature sensor. The facilitator runs the CAN master (Arduino UNO with MCP2515) that sniffs and coordinates the network. Through four progressive tasks, you'll understand CAN IDs, payload encoding, priority arbitration, and mailbox messaging.
What makes it engaging? It's not just theory—it's a game. Teams compete to have the highest average temperature over 60 seconds, with the winner receiving a private CAN message revealing the prize location. The workshop blends education with friendly competition, making CAN bus concepts tangible and memorable.
CAN Bus Mini-Workshop Guide
Learn CAN bus fundamentals through hands-on tasks and a temperature-based competition.
Introduction
Welcome to the CAN Bus Mini-Workshop! This workshop is designed to teach CAN bus fundamentals through hands-on practice. You'll work in teams to program ESP32 nodes that communicate over a CAN bus network, learning about IDs, payload encoding, priority arbitration, and messaging along the way.
The workshop is structured as four progressive tasks, culminating in a competitive game where teams compete to have the highest average temperature. It's beginner-friendly—no prior CAN bus experience required—and emphasizes learning through doing.
Hardware Overview
Nodes (ESP32)
Each team programs one ESP32 node with a DS18B20 temperature sensor. The nodes are positioned at different points along the CAN bus:
| Team | MCU | DS18B20 Data Pin | CAN Position |
|---|---|---|---|
| 1 | ESP32 | D32 |
Bus end |
| 2 | ESP32 | D21 |
Middle |
| 3 | ESP32 | D25 |
Middle |
Master (Arduino UNO + MCP2515)
The facilitator runs the CAN master using an Arduino UNO with an MCP2515 CAN controller module. This acts as a sniffer and referee for the network.
| Pin / Signal | Connects to | Notes |
|---|---|---|
| CS | Pin 10 | Chip select |
| SI (MOSI) | Pin 11 | SPI data out |
| SO (MISO) | Pin 12 | SPI data in |
| SCK | Pin 13 | SPI clock |
| INT | Pin A2 | Interrupt |
⚠️ Important: Put 120 Ω termination resistors at both ends of the bus (Team 1 node end + Master end).
Software Setup
On all 4 computers (3 teams + facilitator), install the following via Arduino Library Manager:
mcp_can(MCP2515 CAN) — e.g., "mcp_can by Cory J. Fowler"OneWireDallasTemperature
Board Manager: Install "esp32 by Espressif" for ESP32 boards.
Workshop Flow
You'll switch tasks by toggling simple flags on the Master. Teams adjust their IDs on the Node code as instructed.
Task A — ID Chaos
Goal: Show that when all nodes share the same ID, you can't tell who's who.
Master (UNO):
- Use the master with task toggles and set:
ENABLE_TASK_A 1 ENABLE_TASK_B 0 ENABLE_TASK_C 0 ENABLE_TASK_D 0 - Open Serial Monitor at 115200.
Teams (ESP32 Nodes):
- Set all teams to the same ID, e.g.
#define NODE_ID 0x100. - Set all teams' temperature data bus (ONE_WIRE_BUS) and mailbox (MAILBOX_ID) to the correct ones. The data bus must correspond to the physical connection and the mailbox can be arbitrary, as long as it matches the ones in CAN Master code.
- Upload node code and let it run (sends temp once per second).
You'll see:
CAN init ok
ID: 0x100 len=2 data: 0x09 0xF6
ID: 0x100 len=2 data: 0x09 0x4A
...
Three sensors, one ID — you can't tell which is which. The teams need to figure out why and change their IDs to see the difference.
Task B — Payload Mystery (Decode to °C)
Goal: Decode the 2-byte payload into real degrees Celsius and give each team a unique ID. Ask the teams what are you seeing. Is it actual temperature data or are the frames corrupted? Why does it look like that and how do I decode it?
Master (UNO):
- Toggle:
ENABLE_TASK_A 0,ENABLE_TASK_B 1,ENABLE_TASK_C 0,ENABLE_TASK_D 0 - The decode block prints human-readable temps.
Teams (ESP32 Nodes):
- Assign unique IDs (example):
- Team 1 →
0x100 - Team 2 →
0x101 - Team 3 →
0x102
- Team 1 →
- You may need to change these in your code for master CAN
(
const unsigned long NODE_IDS[3] = {0x103, 0x101, 0x102};) based on what the teams set up in the previous Task.
Concept: Payload is big-endian int16 * 100.
Example: bytes [0x09, 0xF6] → (0x09<<8 | 0xF6) = 2550 →
25.50 °C.
You'll see:
ID: 0x101 Temp: 24.50 °C
ID: 0x100 Temp: 21.30 °C
ID: 0x102 Temp: 26.10 °C
Task C — Priority & Filtering
Goal: Show that lower CAN ID = higher priority (wins arbitration when simultaneous).
Master (UNO):
- Toggle:
ENABLE_TASK_A 0,ENABLE_TASK_B 1(optional),ENABLE_TASK_C 1,ENABLE_TASK_D 0 - The master sends a periodic TICK (ID 0x7FF), then prints which node arrives first.
Teams (ESP32 Nodes):
- Comment out the periodic sender and uncomment Task C: TICK Reply so nodes send only when TICK arrives.
Note: Without perfect sync, you'll see different winners. CAN priority (lower ID wins) only shows up when frames start at the exact same instant. Our nodes aren't perfectly synchronized to the microsecond, so often the 'winner' is just whoever hits the bus first. That's why you'll see different winners — it's timing jitter, not CAN breaking. In real systems you'd align timing with shared clocks/interrupts or hardware timers.
Task D — Final Game: 1-Minute Average (Hottest Team Wins)
Goal: For 60 seconds, the master averages each team's reported temperature. The highest average wins. The winner gets a private CAN DM to its mailbox telling them where the beer is!!! (or other non-alcoholic prize)
Master (UNO):
- Toggle:
ENABLE_TASK_A 0,ENABLE_TASK_B 0/1(your choice),ENABLE_TASK_C 0,ENABLE_TASK_D 1 - Optional knobs at top:
START_DELAY_MS— pre-start countdown (e.g.,10000for 10s)WINDOW_MS— averaging window (default60000= 1 min)
Teams (ESP32 Nodes):
- Set IDs and mailboxes:
- Team 1 →
NODE_ID 0x100(or 0x101),MAILBOX_ID 0x210 - Team 2 →
NODE_ID 0x101(or 0x102),MAILBOX_ID 0x211 - Team 3 →
NODE_ID 0x102(or 0x103),MAILBOX_ID 0x212
- Team 1 →
- Keep the winner-only mailbox listener (prints any text sent to your mailbox). No spoilers about rules printed by the node.
End of round:
- Master prints each team's final average.
- Winner chosen (highest average; tie-break = lowest node ID).
- Master sends DM only to the winner:
go get the beer in the fridge! you won
Installation & Build Notes
- Baud: 115200 for Serial Monitors.
- Bitrate: 500 kbps on MCP2515.
- Crystal define:
#define MCP_CLOCK_IS_8MHZ 1(set to0if your MCP2515 is 16 MHz). - ESP32 DS18B20 tip:
sensors.setResolution(9);for faster updates (~93 ms).
Troubleshooting
- DS18B20 reads 85.00 °C or −127.00 °C: Check wiring and 4.7 kΩ pull-up from data to 3.3 V.
- CAN is silent: Verify termination, shared GND, correct CS pins, matching bitrate/crystal.
- Winner message not seen: Ensure each team's MAILBOX_ID matches the master's mapping (0x210/0x211/0x212).
Quick FAQ (for teams)
- How do we recognize the end of the bus?
The physical ends have 120 Ω termination resistors. In this workshop: Team 1 node and the Master are the ends. - Why do we sometimes see different "winners" in priority?
CAN arbitration (lower ID wins) only shows when frames start at the exact same instant. Our software timing isn't microsecond-perfect, so "first to speak" can win—this is expected.
Code & Resources
📦 All code is available on GitHub:
Repo Structure:
CAN-workshop/
├─ Master/ # UNO master sketches (task toggles version)
├─ Nodes/ # ESP32 node sketch (workshop format)
├─ wiring/ # images or diagrams (optional)
└─ README.md # this file
Credits / License
Based on MCP2515 library mcp_can by Cory J. Fowler and
DallasTemperature/OneWire. Use/modify freely for educational workshops.
Appendix: What the Master Prints (examples)
Task A (raw):
ID: 0x100 len=2 data: 0x09 0xF6
Task B (decoded):
ID: 0x101 Temp: 25.50 °C
Task C (priority demo):
--- TICK --- (all nodes send NOW)
Winner this tick: 0x050
Task D (final):
=== FINAL AVERAGES (1 minute) ===
Node 0x100: 26.12 °C
Node 0x101: 25.47 °C
Node 0x102: 24.98 °C
Winner: 0x100 avg=26.12 °C
📣 Announced to mailbox 0x210