Welcome back! In our last lesson, you mastered the crucial skill of parsing incoming CAN messages, extracting the sender's ID and data payload using professional, bitwise techniques. This was the final component skill needed to build a complete CAN application.
Today, we're putting it all together. This lesson marks the culmination of the entire CAN module. Our goal is to build a functional, two-node CAN bus demo. You will take on the role of a systems integration engineer, wiring up two separate microcontrollers and programming them to communicate with each other in a real-world scenario. This is a significant milestone and a fantastic piece for your project portfolio.
Let's get started.
1. System Architecture and Physical Setup
Before writing any code, we must define our system and wire it correctly. Our goal is to create a simple distributed system:
- Node 1 (Transmitter): Will read an analog input (like a potentiometer, simulating a sensor) and continuously transmit its value over the CAN bus.
- Node 2 (Receiver): Will listen for messages from Node 1, receive them, parse the sensor value, and use it to control an actuator (an LED).
This simple topology is the basis for countless automotive and industrial systems. The image below shows a finished setup very similar to what you'll be building.

Wiring the CAN Bus
Correct physical wiring is non-negotiable for a stable CAN bus. A mistake here will prevent any communication, regardless of how perfect your code is.
You will need two of your Blue Pill boards and two CAN transceiver modules (like the TJA1050 or MCP2551).
The following article from ControllersTech provides a clear wiring diagram and explanation for connecting two STM32 boards via CAN transceivers. Pay close attention to the termination resistor.
Please read the section titled "STM32 MCP2551 Wiring Diagram". Focus on the two tables showing the connections from the STM32 to the transceiver, and from the transceiver to the CAN bus itself. Ensure you understand where the 120Ω termination resistor goes.
To summarize the key wiring points for each node:
- STM32 to Transceiver:
- STM32
PA12(CAN_TX) -> TransceiverTXDpin. - STM32
PA11(CAN_RX) -> TransceiverRXDpin. - Provide 5V and GND to the transceiver module.
- STM32
- Transceiver to Bus:
- Connect the
CAN_Hpins of both transceivers together. - Connect the
CAN_Lpins of both transceivers together. Using a twisted pair of wires for this is highly recommended to improve noise immunity. - Crucially, place a 120Ω termination resistor across the
CAN_HandCAN_Llines. For a simple two-node bus, one resistor at one end is usually sufficient, but placing one at each end is the correct network topology. Some transceiver modules may have a built-in resistor you can enable with a jumper.
- Connect the
2. A Guided Two-Node Implementation
Now that the hardware is connected, we will program the two nodes. The following video provides an excellent, step-by-step walkthrough using the exact Blue Pill board you have. We will follow its structure to build our project.
The video demonstrates a project where a potentiometer on the transmitter node controls the blinking speed of an LED on the receiver node. This is a perfect hands-on exercise.
115. STM32CubeIDE MCP2551 CAN BUS with STM32 F103C8T6
This video by MicroPeta will guide you through the entire process of setting up a two-node CAN bus. We will watch it in segments to configure our transmitter and receiver boards.
You don't need to watch the full video at once. We will refer to specific parts as we build each node.
Step 2.1: The Transmitter Node
First, connect your ST-Link debugger to the board you've designated as the transmitter.
115. STM32CubeIDE MCP2551 CAN BUS with STM32 F103C8T6
Let's begin by configuring the transmitter project in STM32CubeIDE.
Watch the video from 02:29 to 08:04. This covers: CubeIDE Setup (02:29 - 05:03): Follow along to create a new project. Pay attention to setting the system clock, enabling the ADC (if you're using a potentiometer), and activating the CAN peripheral. The bit timing calculation we covered in a previous lesson is applied here to set the bus speed. Transmitter Code (05:03 - 08:04): Follow the instructions to add the C code. The key logic is in the while(1) loop: it reads the ADC value, scales it down to an 8-bit value (0-255), places it in the TxData array, and transmits the message using HAL_CAN_AddTxMessage().
After completing this step, you will have one board programmed to continuously broadcast a CAN message containing a sensor value. Now, let's program the other board to listen.
Step 2.2: The Receiver Node
Move your ST-Link debugger to the second Blue Pill board.
115. STM32CubeIDE MCP2551 CAN BUS with STM32 F103C8T6
Now, we will configure the second board to be our receiver. The setup is similar, but with a critical focus on filtering and interrupt handling.
Watch the video from 08:04 to 13:47. This covers: CubeIDE Setup (08:04 - 09:50): Create a second project for the receiver. The CAN bit timing parameters must exactly match the transmitter's. The most important step here is configuring the acceptance filter to match the transmitter's ID (0x456 in the video's example) and enabling the CAN RX interrupt in the NVIC settings. Receiver Code (09:50 - 13:47): Add the C code for the receiver. The logic here brings together our last few lessons. The HAL_CAN_RxFifo0MsgPendingCallback is implemented. Inside, it calls HAL_CAN_GetRxMessage to retrieve the data, extracts the payload byte (RxData[7]), and uses this value to control the LED blink rate. The video also shows a great debugging tip using the 'Live Expressions' view to monitor the received data.
Step 2.3: Test and Verify
Once both boards are programmed, power them up. If everything is configured correctly, turning the potentiometer on the transmitter node should change the blinking speed of the LED on the receiver node, just as shown in the video's final demonstration (from the start to 00:49).
Congratulations, you have built a working CAN network!
3. Alternative: Using a CAN-to-USB Analyzer
If you only have one STM32 board available, you can still complete this exercise using a CAN-to-USB analyzer. The analyzer acts as the second node, controlled by your PC.
-
Verifying the Transmitter:
- Program your STM32 board with the Transmitter code from Step 2.1.
- Connect your CAN-to-USB analyzer to the CAN bus (CAN_H, CAN_L, GND).
- Use the analyzer's PC software to monitor the bus. You should see a stream of messages with the ID you configured, and the payload should change as you adjust the potentiometer.
-
Verifying the Receiver:
- Program your STM32 board with the Receiver code from Step 2.2.
- Connect the analyzer to the bus.
- Use the analyzer's software to manually create and transmit a CAN message. Set the ID to match what your receiver's filter is expecting. Set the payload bytes to command the LED.
- You should see the LED on your STM32 board respond to the messages you send from your PC.
This method is also extremely valuable for professional debugging.
4. Advanced Verification with a Logic Analyzer
As an automotive firmware engineer, you need to trust but verify. The ultimate ground truth is what's happening on the physical wires. Your logic analyzer is the perfect tool for this.
STM32 CAN Communication Explained | Send & Receive Data Between Two Microcontrollers
The following video from ControllersTech demonstrates a similar two-node project but includes an excellent segment showing how to use a logic analyzer to decode the CAN traffic.
Watch the clip from 14:39 to 19:03. The demonstrator connects a logic analyzer to the bus. Notice how the analyzer software can decode the raw electrical signals into a readable format, showing the message ID, DLC, and data payload for each transmission. This allows you to verify with 100% certainty that your transmitter is sending the correct data and that your timing is correct.
Connecting your own logic analyzer to your CAN bus and verifying the messages is a fantastic exercise to practice your debugging skills.
Conclusion
In this capstone lesson for the CAN module, you have successfully integrated all the individual concepts—bit timing, transmission, filtering, interrupts, and parsing—to build a complete, functional CAN network. This is a non-trivial task and a significant step in your journey to becoming a professional embedded engineer.
Key Takeaways:
- A functional CAN bus requires a solid physical layer: correct wiring, twisted pairs for H/L lines, and proper 120Ω termination.
- All nodes on the bus must be configured with the exact same bit timing to communicate.
- A receiver must have its acceptance filters configured correctly to receive messages from a specific transmitter.
- Integrating individual software modules (ADC driver, CAN driver, application logic) into a cohesive system is a core engineering skill.
- Professional verification involves using tools like debuggers (Live Expressions) and logic analyzers to confirm on-chip and on-wire behavior.
You have now built a communication system from the ground up. In our next module, we will shift our focus from peripherals to the core of the microcontroller itself. The first lesson will be on analyzing a linker script, which will help you understand how your firmware is organized in memory—a critical topic for building complex systems with bootloaders and optimized memory usage.