It all started in the summer of 2020, when I bought an Audi TT 2002. Unlike my previous cars, it didn’t have a digital speedometer in the instrument cluster’s little LCD-display (Driver Information System DIS in Audi). Due to the lack of digital speedometer, I decided to implement it myself. To cut a long story short, I thought I could take advantage of existing GitHub-project [1] and wrote the code for that setup first. Though I soon realized the setup used in the existing project was different to mine, so I had to start it all over.
Basic IdeaAudi TT's DIS-display shows the radio station or the CD-track in the upmost third of the display. My plan was to replace the radio info with digital speedometer. From the previously mentioned project I found out that the radio sends information to the instrument cluster via three one-way data wires. When I pulled my radio out, I found out that my radio didn't use these three wires, but CAN bus instead.
Audi radios without CAN bus (-2001 )
- one-way data connection between the radio and the instrument cluster
- three wires for data transfer (DATA, CLOCK, ENABLE) (I thought my car had these)
Audi radios with CAN bus (2002-)
- two-way data connection (Half-duplex) between the radio and the instrument cluster implemented with CAN bus [2]
- data transmission rate 100 kbps (Infotainment CAN) [3][4]
- two wires for data transfer (CAN-High, CAN-Low) (My car had these)
Before I tapped into CAN bus with Arduino, my plan was to acquire the vehicle speed signal from the radio's GALA-wire (Graduated Audio Level Adjustment). GALA increases radio volume automatically according to vehicle speed. Since my car has CAN bus, there is no need for GALA-wire and the instrument cluster sends vehicle speed data on the bus every 200 ms. I'm used these CAN-messages in my digital speedometer solution.
Designing Fault Tolerant CAN-shield for ArduinoAfter doing a little research on the CAN bus (especially in VW-group cars), I found out that the bus used in my car was a so called Fault Tolerant CAN bus [4]. For me it meant that I couldn't use off-the-shelf CAN-shields for Arduino.
I decided to design a circuit board for the CAN-shield and integrate my Arduino Nano into it. Main components needed for the CAN-shield were
- MCP2515 Stand-Alone CAN Controller with SPI Interface (Easy to communicate with Arduino via SPI)
- TJA1055 Enhanced fault-tolerant CAN transceiver
- 16 MHz Crystal Oscillator
- resistors, capacitors and screw terminal block
- circuit board
TJA-1055T Application Hints [5] -document was useful when I dimensioned resistors and capacitors. I used Arduino CAN Tutorial [6] when I drew the connections between Arduino and MCP2515.
I designed the circuit board in Autodesk Eagle. I didn't pay too much attention to the circuit board design specifics, because I knew that manufacturing and shipping of the board would take its own time. I ordered the printed circuit board from Itead.cc.
I used this great MCP2515 library for Arduino, that allowed me to easily send and receive frames on the CAN bus. Using the library was very straightforward, since it was so well implemented. In the following piece of code are examples of receiving and sending CAN frames.
// Receive
struct can_frame canMsg;
void loop() {
if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) {
if(canMsg.can_id == 0x351){ // ID for speed information on CAN-Bus
lv = canMsg.data[1];
uv = canMsg.data[2]
velocity = ((uv<<8)+lv-1)/190;
}
}
}
// Send
struct can_frame canMsg2;
const uint8_t SPACE = 0x20;
const uint8_t KMH_MESSAGE [8] = {SPACE, SPACE, 0x4b, 0x4d, 0x2f, 0x48, SPACE, SPACE}; //ASCII for ' KM/H '
void setup() {
canMsg2.can_id = 0x263; // Message ID for the second row of the DIS
canMsg2.can_dlc = 8; // Length 8 bytes
for(int j=0; j<8; j++){
canMsg2.data[j] = KMH_MESSAGE[j];
}
void loop() {
mcp2515.sendMessage(&canMsg2);
delay(40); // The car radio sends it own radio station data every 0,8s to
// the instrument cluster, so I have to send my data with much
// higher rate, so the display won't start to blink
}
CAN IDsI found Audi CAN bus message identifier list on Canhack.de [7] and I found the IDs for the first and second text line of the DIS. I also discovered the ID for vehicle speed information on txboard.de [8].
Tested CAN IDs:
- 0x261 first DIS text line (length: 8 bytes; form: ASCII)
- 0x263 second DIS text line (length: 8 bytes; form: ASCII)
- 0x635 radio illumination (length: 3 bytes; from left to right: radio display backlight, radio buttons backlight, (?); (dimmest 0x0 - brightest 0x64)
CAN ID 0x351 (8 bytes from left to right):
- 1. Ignition: 0x8 Ignition on
- 2. LV Vehicle speed lower value ("Once the engine is running and the car is stationary, changes the lower value to 1.")
- 3. UV Vehicle speed upper value
- 4. Unknown
- 5. Unknown
- 6. Outside temperature 1: (Decimal value)/2 - 40 Celsius
- 7. Outside temperature 2: (Decimal value)/2 - 40 Celsius (two different outside temperature sensors?)
- 8. Unknown
Vehicle speed calculation:
speed in km/h = ((UV << 8) + LV - 1) / 200
Message with ID 0x351 captured on the CAN bus: 8 0 0 0 0 60 60 0
During the capture, car had ignition on, and engine wasn't running. CAN frame suggests that ignition was on, temperature in my garage was 8 degrees Celsius and vehicle speed was zero, which seems about right.
Timeline- 1/19/2021: I haven't been able to test my speedometer in practice, because it's winter and I only drive the car during summer. Luckily, the hardest part is over and I have the final product in my hands. The rest of this project is just programming and testing the speedometer in practice.
- 3/8/2021: The device is now installed under the dashboard and it's ready for testing and further development.
- 3/27/2021: I took the car for a spin and after I did some modifications to the code, the speedometer started working properly.
I used the following information from the previously mentioned GitHub-project [1] in my GALA Speedometer code:
- message length fixed 18-bytes
- last byte is checksum
- way to calculate the checksum
[1] GitHub: derpston/Audi-radio-DIS-reader
[2] Volkspage: VW Self Study Programme 186
[3] Volkspage: VW Self Study Programme 238
[4] Volkspage: VW Self Study Programme 269
[5] TJA-1055T Application Hints
[6] circuitdigest.com: Arduino CAN Tutorial
[7] canhack.de: Audi RNS-E CAN Aufschlüsselung
[8] tx-board.de: ID 0x351
AuthorJussi Ristiniemi
Student at Tampere University, Finland
2nd year in Electrical Engineering
Comments