RP2040 CANbus implementation

I have an experimental development branch that supports the CANbus protocol on Raspberry Pi rp2040 micro-controllers. The code is available at: GitHub - KevinOConnor/klipper-dev at work-can2040-20220522 . The code is highly experimental at this time - likely only of interest to other developers and board designers.

This branch allows an rp2040 to utilize CANbus for Klipper communication. It requires a CAN transceiver chip (such as the SN65HVD230) to be wired to the rp2040.

The code utilizes a “software CANbus” implementation. The rp2040 chips do not have a dedicated CANbus hardware implementation. The implementation is instead done mostly in software (utilizing a mixture of the rp2040s PIO hardware, DMA hardware, and ARM cores).

This is an initial development announcement. I have successfully tested basic communication between the rp2040 and an stm32f072 chip. Klipper is able to successfully enumerate, contact, initialize and command the rp2040 via CANbus. More testing is needed and there are some low-level details that still need to be implemented. I expect notable code changes over the next few weeks.



This looks like it might be a viable dev board for the CAN transceiver: https://www.amazon.com/dp/B00KM6XMXO

It looks like you’re going to make the GPIO pins for the transceiver configurable? I have an Adafruit MacroPad I tried to use as a display/gcode button device but ran into USB timeouts when running Klipper on it directly; if I could hook the transceiver up to GPIO20/21 I could test this out. Is it just a matter of modifying the definition of CAN_RX_GPIO and CAN_TX_GPIO?

FYI, I used that “Waveshare” can transceiver board from amazon in my testing.

Is it just a matter of modifying the definition of CAN_RX_GPIO and CAN_TX_GPIO?

Yes - any two gpio pins can be used. (At some point I’ll update Kconfig to make it configurable via the menu.)


Man, this is amazing. Implementing such a complicated protocol like CAN on Pico’s PIOs? Wow… How far did you manage to go yet? Do you have arbitration, error frames, retransmission, error counters?

Clock synchronization, line arbitration, CRC checking, and ack injection are all implemented in the PIO. The ARM core implements bit stuffing/unstuffing and CRC calculation.

Error frames are honoured (they will halt an active transmission), but they are not currently generated.

Error counters are not implemented, but it would be trivial to do so in the ARM C code.

A failed message transmit will be automatically retransmitted (by the ARM C code). I have temporarily placed a limit on retransmission attempts (32), so as to improve debugging.

Although line arbitration is implemented (the PIO code will sync start of transmissions and halt a transmission on a recessive/dominant conflict) it is unlikely the current code would “win” at arbitration due to a delay in scheduling transmissions. Some details at can2040/TODO.md at master · KevinOConnor/can2040 · GitHub . I have a plan to address this.


Very cool.

Looking at your TODO:

Possibly support “remote” frames?

I suggest not wasting time on this, because RTR frames are somewhat deprecated and not recommended for new designs.

FYI, I’ve updated the development branch (commit 04c2a24d). The latest code has a number of fixes and enhancements. Notably, transmit timing is much improved. I’ve also used a logic analyzer to confirm that the code is now successfully participating in line arbitration.


At this rate, you’ll be done by the weekend :smiley: I still can’t believe it is possible to do such things on Pico. I knew PIO was kinda cool, but this is the first time I actually see some badass real-life application.

We’ll see how it performs in real usage scenarios, though.

Out of curiosity, may I ask you what were the reasons for doing CAN on RPI (other than it is very cool)? It is a complicated and time-consuming project, and there will be annoying bugs in the future. Chips like MCP2518 cost a dollar or two, just slapping one in would not be an issue for people buying toolhead boards. Any other reasons maybe?..

Chip shortages. It’s currently difficult to find chips with CANbus support - both MCU chips and SPI-to-CANbus chips. The rp2040 has had good availability and this project adds another option for those utilizing CANbus.


Well, now that you are about to add CAN to Pico, I think its availability will severely degrade :smiley:

1 Like

FYI, I have updated and rebased the work-can2040-20220522 branch to be on top of the “usb to can bridge” work discussed at Experimental "USB to CANbus bridge" mode .

With the latest code the rp2040 can now be used as a canbus Klipper client or as a “usb to can” adapter.


I cloned this branch to test the bridge option and I’m having trouble getting flash_can.py or canbus_query.py to see a board in bootloader mode (Canboot). I was able to build and flash the bridge firmware to an SKR Pico, and the board appears to function as expected. I’ve connected a Waveshare SN65HVD230 to the GPIO0 and GPIO1 pins which I selected for CANRX and CANTX when building the firmware. I’ve tried a number of things, but I can’t see the CAN board (EBB42). I’ve verified that I can see and flash this board using a CANable on the main Klipper branch.

It’s hard to say what the issue could be. I’ve done local testing with the rp2040 in bridge mode, so it works in at least some situations.

There is some useful things to check and report on:

  1. What is the Klipper git version for the bridge board and the ebb board? What is the Kconfig settings for the bridge board and the ebb board?
  2. What does lsusb report on the host machine?
  3. What does canbus_query.py report on the host machine? Are you able to identify, contact, and communicate with the bridge mcu itself?

The “can-utils” system package has some useful utilities for diagnosing canbus issues. In particular, I use the command candump -t z -Ddex can0,#FFFFFFFF to dump traffic on the bus. Another useful command is “cansend” which can generate raw canbus packets - for example commands like: cansend can0 123#AB12CC00.

If you have a regular usb2can adapter, you can try hooking it up in parallel on the canbus. It should then be possible to bring up both can0 and can1 interfaces. One can bring up two terminals running “candump” on both can0 and can1, and then use another terminal to issue “cansend” commands to see if traffic is being seen by each adapter.


Thanks for all the info. I was able to get everything working on a Pi Pico dev board, so now I just need to troubleshoot the SKR Pico.

Well, I’m giving up on the SKR Pico. I’ve had nothing but problems with this board even before attempting to use it as a CAN bridge. It won’t start or reset reliably, and nothing I’ve tried has enabled the CAN bridge to work correctly. The CAN bridge works fine on a Pi Pico, so I’m attributing my issues to hardware at this point and moving on. Thanks for all the work on this, and I’m looking forward to seeing the remainder of the new CAN features merged.

In the latest submission, all temperatures provided by the RP2040 are 0 degrees, including the MCU temperature which is also 0 degrees.

I made a little rp2040 based USB CAN dongle as an exercise on that MCU:

The implementation is very stable now and performs exactly as a candleLight firmware STM32 board.


There was a bug found that could explain this issue. I suggest retrying with the latest code.


FYI, this work has been committed to the Klipper master branch. rp2040 "software canbus" support by KevinOConnor · Pull Request #5613 · Klipper3d/klipper · GitHub



Kevin, have you looked into CAN licensing requirements? Last time I checked (5+ years ago), there were royalties to pay to Bosch for every chip (~10-20 cents) shipped. At least for commercial applications. Not sure about hobbyists, though…