The CAN (Controller Area Network) bus provides a 'Data Link' and 'Physical' layer for mulitipoint communication over a relatively short distance.
It is designed to be robust and reliable, and so is used in many industiral applications. Notably, it is used in the OBD2 and UDS diagnostic protocols which in turn are used by all car manufactures.
If you know how to 'hop on the CAN bus', you can apparently do pretty cool things with your car...
Because the whole point of this is so I can get a job at ARM!
But also, the STM32Fxxx family of ARM® Cortex® Microcontrollers are cheap, easy to use, and widely accessible. This firmware is written for the STM32F446ZE Nucleo Board (+ any CAN transiever. I used this) because that is what I already had, but can easily be ported to a different board...
The BluePill is a cheap and very popular dev board for hobbists & hackers. The issue is that it's CAN Rx/Tx are assigned to the same pins as the USB peripheral.
It is, of course, possible to use the UARTs with a USB ↔ UART bridge. If you want to use a BluePill, check out this project.
To get started, all you need is a board with a 'compatible' STM32 MCU (e.g. a Nucleo Board) and a CAN transiever. Here's my set up:
PCs rarely have a CAN bus. They do often have a Serial bus (a universal one, even). SLCAN defines a serial translation of CAN bus frames, as well as some command instructions. I couldn't find a proper 'standard' definition, but I did find this PDF listing the commands.
This project turns a Nucleo Board into a USB ↔ CAN bridge by translating incoming data from the CAN bus to SLCAN and sending it over USB, and vice versa. Linux provides some utilities for making use of CAN devices, including SLCAN devices.
The device itself is pretty much Plug&Play, so making use of the firmware comes down to knowing what CAN is, what SLCAN is, and how to use the relevant utilites/libraries.
(Note: To decode the 'MM' 'Get Filter' command corretly, you should know how the Filter & Mask regieristers work internally. See [3] Page 1044. In short, registers set by left aligning IDs by left-shifting (<<) (32 - ID_LENGTH) places)
On the Nucleo F446ZE, there are 3 LEDs: Red, Green and Blue.
- The Red LED flashes once when data is sent out to the CAN Bus (Rx from USB, Tx to CAN). It flashes 3 times if there was an error (e.g. Tx mailbox full).
- The Green LED flashes once when data is recieved from the CAN Bus (Tx from USB, Rx from CAN).
- The Blue LED flashes when a command has been successfully parsed.
When the board is first powered on, it goes through a test sequence. If this test fails, the Red LED stays on for 5 seconds.
Once you have the device pluged in, you can send SLCAN commands to the VCP using whatever program you like (I've tested it with Minicom.) Note that newline characters are not included at the end of a response, only a carrige return.
SocketCAN is a set of open source CAN drivers and a networking stack contributed by Volkswagen Research to the Linux kernel. We can use the deamon slcand
included in can-utils to create a CAN networking interface (similar to eth0, wlan2, etc.) linked to the SLCAN device. Code can then be written to use the interface like any other networking interface in Linux.
The can-utils package in Linux provides some examples of tools you can create using the SocketCAN stack. cansend
and candump
in partcular are very useful. Check the man pages for usage instructions.
If you want to work with car data, speaking CAN is not enough. You need to be able to send UDS and/or OBD2 requests and parse the response. ISO 15765-2 defines a higher layer Transport and Networking protocol that is built on top of the Physical and Data Link layers provided by CAN. This is ISOTP. OBD2/UDS requests and responses are ISOTP packets.
To use the ISOTP utilites, a LKM not included with the standard kernel must be loaded. (See [1]). After this, can use isotpdump
, isotprecv
, and isotpsniffer
from the can-utils package to speak ISOTP. Thank you Volkswagen.
There are a couple modules we can use to help us on our car hacking adventure:
- sockets: The Linux version of Python has native support for SocketCAN interfaces. If know how UNIX sockets work, and how to use the Python stdlib sockets module, then you can bind a socket to your SLCAN interface from there you can use the socket API as you would normally.
- isotp: Python doesn't make using ISOTP easy. This module does.
- udsoncan: I haven't had time to check this out properly, but (as far as I can tell) this is where we get to. Easily sending and recieving complete UDS requests and responses by name. Fully abstacted.
All examples where run on an Acer Aspire A315 and an up-to-date Artix Linux Distribution (except for the client side of Server example.)
(Note: 'Local Echo' & 'Add Newline Character' Options set in Minicom.)
(Note: To use the ISOTP utilites, an LKM not included with the standard kernel must be loaded. See [1]. )
- Messages seem to be duplicated (especially when in test mode).
Messages sent out via a socketCAN interface are locally echoed. I feel like this has something to do with it. When in test mode, CAN frames are looped back in hardware, on top of the local echo. RAW_CAN sockets in Linux apparently provide a way to disable the software loopback, but I couldn't figure it out. (See [2] Section 4.1.3 for more information.) - The test always fails.
The test will fail unless the device is opened ready to receive the SLCAN data. But the device cannot be opened until the board has powered on. This means I have to power the board, open the Minicom connection, and then restart the board to get a useful test result...TODO - I closed the connection, but transmitted messages still trigger the Red LED.
There are 3 Tx Mailboxes. These can be filled while the connection is closed, and then are flushed when the connection is reopened. Adding a message to the Tx mailbox triggers the LED, but it should really trigger when the data is sent to the bus...TODO. - Sometimes sending invalid data 'works', sometimes it doesn't.
If whatever happens to be in the 'SLCAN_CommandStrRx' buffer is valid when the next carrige return is seen, then the string gets parsed, even if you didn't put that data there...TODO.
- https://github.com/hartkopp/can-isotp (can-isotp Kernel Module Github Repo)
- https://www.kernel.org/doc/Documentation/networking/can.txt (SocketCAN Kernel Documatation)
- https://www.st.com/resource/en/reference_manual/dm00135183-stm32f446xx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf (D0390: STM32F446xx Reference Manual)
- https://en.wikipedia.org/wiki/SocketCAN (SocketCAN Wikipedia)
- https://github.com/linux-can/can-utils#socketcan-userspace-utilities-and-tools (can-utils Github Repo)
- http://www.can232.com/docs/can232_v3.pdf (LAWICEL SLCAN Commands)
- https://en.wikipedia.org/wiki/ISO_15765-2 (ISO 15765-2 Wikipedia)