Digital Speedometer To Car's Instrument Cluster Via CAN Bus

Hackster.ioLearning Hardware Community. Projects× Log inSign up ProjectsProjectsChannelsChannelsNewsContestsEventsVideos ×

Embed the widget on your own site

Add the following snippet to your HTML:

Digital Speedometer to Car's Instrument Cluster via CAN Bus

Implementing Arduino CAN bus shield and digital speedometer to car's LCD-display in instrument cluster.

Read up about this project on Hackster.io

Digital Speedometer to Car's Instrument Cluster via CAN Bus

Jussi Ristiniemi34 40,852
  • Overview
  • Things
  • Story
    • Start of the Project
    • Basic Idea
    • Reading vehicle speed data
    • Designing Fault Tolerant CAN-shield for Arduino
    • Receiving and sending CAN-frames
    • CAN IDs
    • Timeline
    • Sources
    • Author
  • Schematics
  • Code
  • Credits
  • Comments(46)
Jussi RistiniemiJussi RistiniemiPublished © GPL3+Digital Speedometer to Car's Instrument Cluster via CAN Bus

Implementing Arduino CAN bus shield and digital speedometer to car's LCD-display in instrument cluster.

IntermediateFull instructions providedOver 2 days40,852Digital Speedometer to Car's Instrument Cluster via CAN Bus

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
  • Buy from Newark
  • Buy from CPC
MCP2515
×1
NXP TJA1055
×1
16 MHz Crystal
16 MHz Crystal
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Resistor 1k ohm
Resistor 1k ohm
×1
Resistor 4.75k ohm
Resistor 4.75k ohm
R3, R4 Bus Termination Resistors
×2
Ceramic Disc Capacitor, 150 pF
Ceramic Disc Capacitor, 150 pF
Bus capacitors for EMI improvements
×2
Ceramic Disc Capacitor, 18 pF
Ceramic Disc Capacitor, 18 pF
Capacitors for 16MHz crystal oscillator
×2
Screw Block Terminal 5-R3.5
×1

Software apps and online services

Arduino IDE
Arduino IDE
Autodesk EAGLE
Autodesk EAGLE

Story

Start of the Project

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 Idea

Audi 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)
Audi Chorus Connector: CAN-Low (Orange-Brown) and CAN-High (Orange-Purple) Audi Chorus Pinout

Reading vehicle speed data

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 Arduino

After 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.

Circuit Board Design in Autodesk Eagle Final Printed Circuit Board Final Product

Receiving and sending CAN-frames

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.

// Receivestruct 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 IDs

I 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].

TestedCAN 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)

CANID0x351 (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.
Designing a casing for the device The Final Casing Connecting the device to the wiring harness The Device mounted under the dash next to the light switch
  • 3/27/2021: I took the car for a spin and after I did some modifications to the code, the speedometer started working properly.
Fully functioning Digital Speedometer

Sources

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

Author

Jussi Ristiniemi

Student at Tampere University, Finland

2nd year in Electrical Engineering

Read more

Schematics

Board

Schematics

Code

  • Can Bus Speedometer
  • GALA Speedometer

Can Bus Speedometer

ArduinoCode for the setup with CAN bus compatible radio (Chorus)#include <SPI.h> #include <mcp2515.h> struct can_frame canMsg, canMsg1, canMsg2; int velocity,lv,uv = 0; const int INTERVAL = 800; // ms const int REFRESH_RATE = 20; // ms bool init_state = true; const uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; // ASCII for numbers 0..9 const uint8_t SPACE = 0x20; const uint8_t KMH_MESSAGE [8] = {SPACE, SPACE, 0xB, 0xD, 0x2f, 0x8, SPACE, SPACE}; //ASCII for ' km/h ' const uint8_t AUDI_TTe [8] = {65, 117, 4, 9, 32, 84, 84, 5}; // "Audi TTe" const uint8_t QUATTRO [8] = {113, 117, 0x01, 116, 116, 114, 0xF, 32}; // "quattro" const uint32_t MASK = 0xD440000; // Mask for id 0x351 = 0b 0110 1010 00 1 <=> 0xD440000 = 0b 0110 1010 00 1 0000 0000 0000 0000 00 . Extended from 11 to 29 bits const int SPEED_ID = 0x351; MCP2515 mcp2515(10); void setup() { canMsg1.can_id = 0x261; // Message ID for the first row of the DIS canMsg1.can_dlc = 8; canMsg2.can_id = 0x263; // Message ID for the second row of the DIS canMsg2.can_dlc = 8; for(int j=0; j<8; j++){ canMsg2.data[j] = KMH_MESSAGE[j]; } mcp2515.setFilterMask(0, 0, MASK); mcp2515.setFilter(0, 0, MASK); mcp2515.setBitrate(CAN_100KBPS, MCP_16MHZ); mcp2515.setNormalMode(); } void clear(){ for(int i=0; i<8; i++){ canMsg1.data[i] = SPACE; // Initialising the speed message (first row of canMsg2.data[i] = KMH_MESSAGE[i];; // DIS) with SPACES } } bool is_received(int id){ if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) { if(canMsg.can_id == id){ return true; } return false; } } void audi_tt(){ for(int i=0; i<8; i++){ canMsg1.data[i] = AUDI_TTe[i]; canMsg2.data[i] = QUATTRO[i]; } for(int j=0; j<3000/REFRESH_RATE;j++){ mcp2515.sendMessage(&canMsg1); mcp2515.sendMessage(&canMsg2); delay(REFRESH_RATE); } } void loop() { if(init_state){ audi_tt(); init_state = false; clear(); } if (is_received(SPEED_ID)) { clear(); lv = canMsg.data[1]; uv = canMsg.data[2]; velocity = ((uv<<8)+lv-1)/200; velocity = round(velocity); if(velocity < 10){ canMsg1.data[2] = ASCII[velocity]; } else if(velocity < 100){ canMsg1.data[2] = ASCII[velocity/10]; // Splitting integer to single digits canMsg1.data[3] = ASCII[velocity%10]; } else{ canMsg1.data[2] = ASCII[velocity/100]; canMsg1.data[3] = ASCII[(velocity/10)%10]; canMsg1.data[4] = ASCII[velocity%10]; } int i = 0; while(true){ if(i >= INTERVAL){ return; } else if(i % REFRESH_RATE == 0){ mcp2515.sendMessage(&canMsg1); mcp2515.sendMessage(&canMsg2); } i++; delay(1); } } // 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 }

GALA Speedometer

ArduinoCode for the setup with Concert-radio/* Digital speedometer to Audi Driver Information System Reads vehicle speed from Audi Concert Gala-wire ( Sends information to instrument cluster via Audi concert pins) */ #define pulse_ip 2 #define enable 6 #define sck 7 #define sda 8 unsigned long ontime,offtime, period; float freq = 0; int final_speed = 0; float speed_ = 0; float circumference = 0.63719; // in meters uint8_t header = 0xf0; uint8_t command_byte = 0x1c; uint8_t message [18], checksum; // numbers from 0 to 9 in ASCII-form uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; uint8_t KMH [5] = {0x20,0x4B,0x4D, 0x2F,0x48}; // ASCII for: ' km/h' uint8_t SPACE = 0x20; // ASCII for: ' ' // the setup function runs once when you press reset or power the board void setup() { pinMode(pulse_ip, INPUT); // pin for vehicle speed signal (GALA) pinMode(enable, OUTPUT); pinMode(sck, OUTPUT); pinMode(sda, OUTPUT); } // the loop function runs over and over again forever void loop() { ontime = pulseIn(pulse_ip,HIGH); offtime = pulseIn(pulse_ip,LOW); period = ontime+offtime; freq = 1000000.0/(period*2); // 2 periods per one wheel rotation speed_ = freq*circumference*3.6; speed_ = round(speed_); final_speed = speed_; message[0] = header; message[16] = command_byte; message[1] = SPACE; // For alignment message[2] = SPACE; if(final_speed < 10){ message[3] = ASCII[final_speed]; message[4] = SPACE; message[5] = SPACE; } else if(final_speed < 100){ message[3] = ASCII[final_speed / 10]; message[4] = ASCII[final_speed % 10]; message[5] = SPACE; } else{ message[3] = ASCII[final_speed / 100]; message[4] = ASCII[final_speed / 10]; message[5] = ASCII[final_speed % 10]; } for(int i = 6; i < 11; i++){ // for alignment message[i] = SPACE; } message[11] = KMH[1]; message[12] = KMH[2]; message[13] = KMH[3]; message[14] = KMH[4]; message[15] = SPACE; checksum = 0; for(int j = 0; j < 17; j++){ checksum += message[j]; } checksum ^= 0xff; message[17] = checksum; digitalWrite(enable, HIGH); // Sending the data for(int i = 0; i < 18; i++){ uint8_t data = message[i]; for(int k = 0; k < 8; k++){ if(data & 0x80){ digitalWrite(sda, HIGH); } else{ digitalWrite(sda, LOW); } digitalWrite(sck, LOW); digitalWrite(sck, HIGH); data <<=1; //Shifting 1 left } } digitalWrite(enable, LOW); delay(500); }

Credits

Jussi Ristiniemi

Jussi Ristiniemi

0 projects • 11 followersFollow ContactContact

Comments

Related channels and tags
  • Autodesk
  • NXP
  • Vehicles
  • Vehicles
  • automotive
  • monitoring

Từ khóa » Vw Can Bus Id List