Using a CANPico as a USB CAN bus adapter for Wireshark
Here I will explain how one of our CANPico boards can be made into a USB CAN adapter firmware to display CAN frames on the bus in Wireshark.
First, let’s look at building the simplest possible CAN bus monitor:
This function just receives all the CAN frames via the recv()
call then prints them
out. Really easy. It’s included in
the canpico.py
Python examples library.
Also included is a simple function called periodic()
that just sends a CAN frame periodically with a
4-byte payload that’s an incrementing integer, which saves us a bit of typing and we can test
out the monitor by altering the rate we receive CAN frames.
It’s very simple to run too: just invoke mon()
in the library instance.
If the bus traffic is high then this monitor doesn’t work very well: the time taken
by the print()
function is huge: it has to work out how to print a frame, copy them to
a buffer, then push them over a serial port (that’s connected to the REPL command line),
which can take several milliseconds, far longer than the time between frames. So we want a
better monitor and to move the displaying of frames to a faster host. The host I’m using is
a Raspberry Pi 4.
To get data from a microcontroller to a host PC requires a serial link and a protocol to run over the link. In our case the serial link is a second USB serial port (this is baked into the CANPico MicroPython firmware). The protocol for the serial line is the MIN protocol. This was designed a few ago back to address a fairly common problem: a way for an embedded microcontroller (including small 8-bit ones) to talk reliably to a host (like a PC) over serial.
All the common serial protocols are either too heavyweight to run in software (like HDLC) or require deep control of the serial line (like LIN) or are just inappropriate for a small device with tiny amounts of RAM (like zmodem). So Microcontroller Interconnect Network was born. It’s been used for a while, and Canis Labs has used it for automating tests by sending commands to and from a CAN-connected device. It’s so useful that we embedded it into the MicroPython firmware for our CANPico boards.
MIN comes in two layers: the bottom layer is a simple framing system that uses character stuffing to delimit frame headers (avoiding the common problem of starting communication part way through a frame and mistaking a payload for a start of frame). This frame protocol is designed to be secure, with a strong CRC so that a frame is discarded if it has errors. It suits an application where a small microcontroller is sending sensor data periodically, and dropping a frame from noise isn’t a problem because the next reading will be ready shortly. But some applications require much higher reliability (e.g. uploading logs) and so MIN has the MIN-T layer: a reliable transport protocol with a sliding window and acknowledgements. This is the API baked into the CANPico MicroPython firmware.
Back to turning CANPico into a USB CAN bus adapter. It’s really simple: just use
the minmon()
function in the canpico.py
library. This is a really simple function:
We just initialize an instance of the MIN
class, then read the CAN frames with the normal
MicroPython CAN function recv()
but with one extra parameter: as_bytes=True
. This
tells the CAN API to return received CAN frames not as a list of CANFrame
instances but
as a block of bytes (each CAN frame is encoded as 19 bytes). The bytes are then sent over
MIN to the host. Here a MIN ID of 1 is used to indicate the payload contains byte-encoded
CAN frames (in the future we are going to use other MIN IDs to add support for transmitting
frames from the host and for indicating when frame was been transmitted).
The MIN repository includes a Python implementation of MIN which the host can use to talk
to the embedded side. I’ve just uploaded to the MIN repository a simple commandline tool in
Python that connects to a CANPico board over a serial port, receives MIN frames containing
CAN frames from the minmon()
function running on the CANPico board. This tool
is implemented in canpcap.py
and is straightforward. The MIN handling is really easy:
We just open a connection to the board (sending it a transport reset to sync the transport layer in the CANPico). The MIN frame payloads are then sent to be decoded into CAN frames and then encoded into the PCAPNG packet capture format that Wireshark understands.
To run the commandline tool just specify the serial port to use (normally the second of a pair created when the CANPico is plugged in) and then pipe the output into Wireshark:
And that’s it! Wireshark will display the CAN frames on the bus:
I’ve made a short video demo of this working:
This bus analyzer is already quite sophisticated: the timestamps are to microsecond resolution (taken directly from the CAN controller) and it even handles error frames. There are some things that need to be done to polish it, such as creating 64-bit timestamps rather than just passing in the raw 32-bit timestamps (which overflow and become negative after 35 minutes). But it’s the basis of a sophisticated open-source CAN tool.
The CANPico board is a really versatile CAN solution: it can be used as a probe for a logic analyzer (especially when used with our sophisticated Sigrok can2 CAN protocol decoder) and now it can be used as a bus monitor with Wireshark. And we have future plans to add support for making our CAN Python API available on a host by using MIN to communicate with a proxy on the CANPico.