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.
Components and supplies
1
16 MHz Crystal
2
Ceramic Disc Capacitor, 150 pF
1
Resistor 10k ohm
1
MCP2515
2
Resistor 4.75k ohm
2
Ceramic Disc Capacitor, 18 pF
1
Resistor 1k ohm
1
TJA1055
1
Screw Block Terminal 5-R3.5
1
Arduino Nano R3
Apps and platforms
1
Arduino IDE
1
Autodesk EAGLE
Project description
Code
Can Bus Speedometer
arduino
Code for the setup with CAN bus compatible radio (Chorus)
1#include <SPI.h> 2#include <mcp2515.h> 3 4struct can_frame canMsg, canMsg1, canMsg2; 5 6int velocity,lv,uv = 0; 7const int INTERVAL = 800; // ms 8const int REFRESH_RATE = 20; // ms 9bool init_state = true; 10 11 12const uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; // ASCII for numbers 0..9 13const uint8_t SPACE = 0x20; 14const uint8_t KMH_MESSAGE [8] = {SPACE, SPACE, 0xB, 0xD, 0x2f, 0x8, SPACE, SPACE}; //ASCII for ' km/h ' 15 16 17const uint8_t AUDI_TTe [8] = {65, 117, 4, 9, 32, 84, 84, 5}; // "Audi TTe" 18const uint8_t QUATTRO [8] = {113, 117, 0x01, 116, 116, 114, 0xF, 32}; // "quattro" 19const 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 20const int SPEED_ID = 0x351; 21 22MCP2515 mcp2515(10); 23 24 25void setup() { 26 27 canMsg1.can_id = 0x261; // Message ID for the first row of the DIS 28 canMsg1.can_dlc = 8; 29 30 canMsg2.can_id = 0x263; // Message ID for the second row of the DIS 31 canMsg2.can_dlc = 8; 32 for(int j=0; j<8; j++){ 33 canMsg2.data[j] = KMH_MESSAGE[j]; 34 } 35 36 37 mcp2515.setFilterMask(0, 0, MASK); 38 mcp2515.setFilter(0, 0, MASK); 39 mcp2515.setBitrate(CAN_100KBPS, MCP_16MHZ); 40 mcp2515.setNormalMode(); 41 42} 43 44void clear(){ 45 for(int i=0; i<8; i++){ 46 canMsg1.data[i] = SPACE; // Initialising the speed message (first row of 47 canMsg2.data[i] = KMH_MESSAGE[i];; // DIS) with SPACES 48 } 49} 50 51bool is_received(int id){ 52 if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) { 53 if(canMsg.can_id == id){ 54 return true; 55 } 56 return false; 57 } 58 59 60} 61 62 63void audi_tt(){ 64 65 for(int i=0; i<8; i++){ 66 canMsg1.data[i] = AUDI_TTe[i]; 67 canMsg2.data[i] = QUATTRO[i]; 68 } 69 for(int j=0; j<3000/REFRESH_RATE;j++){ 70 mcp2515.sendMessage(&canMsg1); 71 mcp2515.sendMessage(&canMsg2); 72 delay(REFRESH_RATE); 73 } 74 75 76} 77 78void loop() { 79 80 if(init_state){ 81 audi_tt(); 82 init_state = false; 83 clear(); 84 } 85 86 87 if (is_received(SPEED_ID)) { 88 89 clear(); 90 lv = canMsg.data[1]; 91 uv = canMsg.data[2]; 92 velocity = ((uv<<8)+lv-1)/200; 93 velocity = round(velocity); 94 95 96 if(velocity < 10){ 97 canMsg1.data[2] = ASCII[velocity]; 98 } 99 100 else if(velocity < 100){ 101 canMsg1.data[2] = ASCII[velocity/10]; // Splitting integer to single digits 102 canMsg1.data[3] = ASCII[velocity%10]; 103 } 104 else{ 105 canMsg1.data[2] = ASCII[velocity/100]; 106 canMsg1.data[3] = ASCII[(velocity/10)%10]; 107 canMsg1.data[4] = ASCII[velocity%10]; 108 109 } 110 111 int i = 0; 112 while(true){ 113 if(i >= INTERVAL){ 114 return; 115 } 116 117 else if(i % REFRESH_RATE == 0){ 118 mcp2515.sendMessage(&canMsg1); 119 mcp2515.sendMessage(&canMsg2); 120 } 121 i++; 122 delay(1); 123 } 124 125 126 127 } 128 // The car radio sends it own radio station data every 0,8s to 129 // the instrument cluster, so I have to send my data with much 130 // higher rate, so the display won't start to blink 131} 132
GALA Speedometer
arduino
Code for the setup with Concert-radio
1/* 2Digital speedometer to Audi Driver Information System 3 4Reads vehicle speed from Audi Concert Gala-wire ( 5Sends information to instrument cluster via Audi concert pins) 6 7 8*/ 9#define pulse_ip 2 10#define enable 6 11#define sck 7 12#define sda 8 13 14unsigned long ontime,offtime, period; 15float freq = 0; 16int final_speed = 0; 17float speed_ = 0; 18float circumference = 0.63719; // in meters 19uint8_t header = 0xf0; 20uint8_t command_byte = 0x1c; 21uint8_t message [18], checksum; 22 23// numbers from 0 to 9 in ASCII-form 24uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; 25 26uint8_t KMH [5] = {0x20,0x4B,0x4D, 0x2F,0x48}; // ASCII for: ' km/h' 27uint8_t SPACE = 0x20; // ASCII for: ' ' 28 29// the setup function runs once when you press reset or power the board 30void setup() { 31 32 pinMode(pulse_ip, INPUT); // pin for vehicle speed signal (GALA) 33 34 pinMode(enable, OUTPUT); 35 pinMode(sck, OUTPUT); 36 pinMode(sda, OUTPUT); 37} 38 39 40// the loop function runs over and over again forever 41void loop() { 42 43 ontime = pulseIn(pulse_ip,HIGH); 44 offtime = pulseIn(pulse_ip,LOW); 45 period = ontime+offtime; 46 freq = 1000000.0/(period*2); // 2 periods per one wheel rotation 47 speed_ = freq*circumference*3.6; 48 speed_ = round(speed_); 49 final_speed = speed_; 50 51 message[0] = header; 52 message[16] = command_byte; 53 54 message[1] = SPACE; // For alignment 55 message[2] = SPACE; 56 57 if(final_speed < 10){ 58 message[3] = ASCII[final_speed]; 59 message[4] = SPACE; 60 message[5] = SPACE; 61 } 62 63 else if(final_speed < 100){ 64 message[3] = ASCII[final_speed / 10]; 65 message[4] = ASCII[final_speed % 10]; 66 message[5] = SPACE; 67 } 68 69 else{ 70 message[3] = ASCII[final_speed / 100]; 71 message[4] = ASCII[final_speed / 10]; 72 message[5] = ASCII[final_speed % 10]; 73 } 74 75 for(int i = 6; i < 11; i++){ // for alignment 76 message[i] = SPACE; 77 } 78 79 80 message[11] = KMH[1]; 81 message[12] = KMH[2]; 82 message[13] = KMH[3]; 83 message[14] = KMH[4]; 84 85 message[15] = SPACE; 86 87 checksum = 0; 88 for(int j = 0; j < 17; j++){ 89 checksum += message[j]; 90 } 91 92 checksum ^= 0xff; 93 message[17] = checksum; 94 95 digitalWrite(enable, HIGH); 96 97 // Sending the data 98 for(int i = 0; i < 18; i++){ 99 uint8_t data = message[i]; 100 101 for(int k = 0; k < 8; k++){ 102 103 if(data & 0x80){ 104 digitalWrite(sda, HIGH); 105 } 106 else{ 107 digitalWrite(sda, LOW); 108 } 109 digitalWrite(sck, LOW); 110 digitalWrite(sck, HIGH); 111 data <<=1; //Shifting 1 left 112 } 113 } 114 digitalWrite(enable, LOW); 115 delay(500); 116 }
GALA Speedometer
arduino
Code for the setup with Concert-radio
1/* 2Digital speedometer to Audi Driver Information System 3 4Reads vehicle speed from Audi Concert Gala-wire ( 5Sends information to instrument cluster via Audi concert pins) 6 7 8*/ 9#define pulse_ip 2 10#define enable 6 11#define sck 7 12#define sda 8 13 14unsigned long ontime,offtime, period; 15float freq = 0; 16int final_speed = 0; 17float speed_ = 0; 18float circumference = 0.63719; // in meters 19uint8_t header = 0xf0; 20uint8_t command_byte = 0x1c; 21uint8_t message [18], checksum; 22 23// numbers from 0 to 9 in ASCII-form 24uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; 25 26uint8_t KMH [5] = {0x20,0x4B,0x4D, 0x2F,0x48}; // ASCII for: ' km/h' 27uint8_t SPACE = 0x20; // ASCII for: ' ' 28 29// the setup function runs once when you press reset or power the board 30void setup() { 31 32 pinMode(pulse_ip, INPUT); // pin for vehicle speed signal (GALA) 33 34 pinMode(enable, OUTPUT); 35 pinMode(sck, OUTPUT); 36 pinMode(sda, OUTPUT); 37} 38 39 40// the loop function runs over and over again forever 41void loop() { 42 43 ontime = pulseIn(pulse_ip,HIGH); 44 offtime = pulseIn(pulse_ip,LOW); 45 period = ontime+offtime; 46 freq = 1000000.0/(period*2); // 2 periods per one wheel rotation 47 speed_ = freq*circumference*3.6; 48 speed_ = round(speed_); 49 final_speed = speed_; 50 51 message[0] = header; 52 message[16] = command_byte; 53 54 message[1] = SPACE; // For alignment 55 message[2] = SPACE; 56 57 if(final_speed < 10){ 58 message[3] = ASCII[final_speed]; 59 message[4] = SPACE; 60 message[5] = SPACE; 61 } 62 63 else if(final_speed < 100){ 64 message[3] = ASCII[final_speed / 10]; 65 message[4] = ASCII[final_speed % 10]; 66 message[5] = SPACE; 67 } 68 69 else{ 70 message[3] = ASCII[final_speed / 100]; 71 message[4] = ASCII[final_speed / 10]; 72 message[5] = ASCII[final_speed % 10]; 73 } 74 75 for(int i = 6; i < 11; i++){ // for alignment 76 message[i] = SPACE; 77 } 78 79 80 message[11] = KMH[1]; 81 message[12] = KMH[2]; 82 message[13] = KMH[3]; 83 message[14] = KMH[4]; 84 85 message[15] = SPACE; 86 87 checksum = 0; 88 for(int j = 0; j < 17; j++){ 89 checksum += message[j]; 90 } 91 92 checksum ^= 0xff; 93 message[17] = checksum; 94 95 digitalWrite(enable, HIGH); 96 97 // Sending the data 98 for(int i = 0; i < 18; i++){ 99 uint8_t data = message[i]; 100 101 for(int k = 0; k < 8; k++){ 102 103 if(data & 0x80){ 104 digitalWrite(sda, HIGH); 105 } 106 else{ 107 digitalWrite(sda, LOW); 108 } 109 digitalWrite(sck, LOW); 110 digitalWrite(sck, HIGH); 111 data <<=1; //Shifting 1 left 112 } 113 } 114 digitalWrite(enable, LOW); 115 delay(500); 116 }
Can Bus Speedometer
arduino
Code for the setup with CAN bus compatible radio (Chorus)
1#include <SPI.h> 2#include <mcp2515.h> 3 4struct can_frame canMsg, 5 canMsg1, canMsg2; 6 7int velocity,lv,uv = 0; 8const int INTERVAL = 800; // 9 ms 10const int REFRESH_RATE = 20; // ms 11bool init_state = true; 12 13 14const 15 uint8_t ASCII [10] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; // ASCII 16 for numbers 0..9 17const uint8_t SPACE = 0x20; 18const uint8_t KMH_MESSAGE [8] 19 = {SPACE, SPACE, 0xB, 0xD, 0x2f, 0x8, SPACE, SPACE}; //ASCII for ' km/h ' 20 21 22const 23 uint8_t AUDI_TTe [8] = {65, 117, 4, 9, 32, 84, 84, 5}; // "Audi TTe" 24const 25 uint8_t QUATTRO [8] = {113, 117, 0x01, 116, 116, 114, 0xF, 32}; // "quattro" 26const 27 uint32_t MASK = 0xD440000; // Mask for id 0x351 = 0b 0110 1010 00 1 <=> 0xD440000 28 = 0b 0110 1010 00 1 0000 0000 0000 0000 00 . Extended from 11 to 29 bits 29const 30 int SPEED_ID = 0x351; 31 32MCP2515 mcp2515(10); 33 34 35void setup() { 36 37 38 canMsg1.can_id = 0x261; // Message ID for the first row of the DIS 39 canMsg1.can_dlc 40 = 8; 41 42 canMsg2.can_id = 0x263; // Message ID for the second row of the DIS 43 44 canMsg2.can_dlc = 8; 45 for(int j=0; j<8; j++){ 46 canMsg2.data[j] = 47 KMH_MESSAGE[j]; 48 } 49 50 51 mcp2515.setFilterMask(0, 0, MASK); 52 mcp2515.setFilter(0, 53 0, MASK); 54 mcp2515.setBitrate(CAN_100KBPS, MCP_16MHZ); 55 mcp2515.setNormalMode(); 56 57 58} 59 60void clear(){ 61 for(int i=0; i<8; i++){ 62 canMsg1.data[i] 63 = SPACE; // Initialising the speed message (first row of 64 65 canMsg2.data[i] = KMH_MESSAGE[i];; // DIS) with SPACES 66 67 } 68} 69 70bool is_received(int id){ 71 if (mcp2515.readMessage(&canMsg) 72 == MCP2515::ERROR_OK) { 73 if(canMsg.can_id == id){ 74 return true; 75 76 } 77 return false; 78 } 79 80 81} 82 83 84void audi_tt(){ 85 86 87 for(int i=0; i<8; i++){ 88 canMsg1.data[i] = AUDI_TTe[i]; 89 canMsg2.data[i] 90 = QUATTRO[i]; 91 } 92 for(int j=0; j<3000/REFRESH_RATE;j++){ 93 mcp2515.sendMessage(&canMsg1); 94 95 mcp2515.sendMessage(&canMsg2); 96 delay(REFRESH_RATE); 97 } 98 99 100} 101 102void 103 loop() { 104 105 if(init_state){ 106 audi_tt(); 107 init_state = false; 108 109 clear(); 110 } 111 112 113 if (is_received(SPEED_ID)) { 114 115 clear(); 116 117 lv = canMsg.data[1]; 118 uv = canMsg.data[2]; 119 velocity = ((uv<<8)+lv-1)/200; 120 121 velocity = round(velocity); 122 123 124 if(velocity < 10){ 125 126 canMsg1.data[2] = ASCII[velocity]; 127 } 128 129 else if(velocity 130 < 100){ 131 canMsg1.data[2] = ASCII[velocity/10]; // Splitting integer to 132 single digits 133 canMsg1.data[3] 134 = ASCII[velocity%10]; 135 } 136 else{ 137 canMsg1.data[2] = ASCII[velocity/100]; 138 139 canMsg1.data[3] = ASCII[(velocity/10)%10]; 140 canMsg1.data[4] = 141 ASCII[velocity%10]; 142 143 } 144 145 int i = 0; 146 while(true){ 147 148 if(i >= INTERVAL){ 149 return; 150 } 151 152 else 153 if(i % REFRESH_RATE == 0){ 154 mcp2515.sendMessage(&canMsg1); 155 mcp2515.sendMessage(&canMsg2); 156 157 } 158 i++; 159 delay(1); 160 } 161 162 163 164 165 } 166 // The car radio sends it own radio station data every 0,8s to 167 // 168 the instrument cluster, so I have to send my data with much 169 170 // higher rate, so the display won't start to blink 171} 172
Downloadable files
Board
Board

Schematics
Schematics

Schematics
Schematics

Comments
Only logged in users can leave comments