Reading data from CAN bus volkswagen

vgopaitsa

New member
Hello, I am new to microcontroller programming. But I really want to create an additional monitor with information output to the car (Volkswagen Jetta 6). I have Teensy 4.0 and have already been able to connect a 240x240 display. But here's how to get information from the CAN bus of a car - I don't have enough knowledge. Perhaps someone was engaged in a similar task, I will be grateful for any advice and hint. Sincerely.
 
I have experience with the MQB platform, but as your Jetta is on the PQ35 platform, I can't provide you with my request response sample as it's not the same.
What kind of data do you want to display on your tft?

What I suggest you do is purchase a VCDS HEX-CAN V2 from Aliexpress and do some can sniffing using the T4 hooked up to the OBD port with a Y splitter (don't forget to use a CAN transceiver between the T4 and the OBD port)
Set VCDS to poll a single parameter and catch the messages with the Teensy vis Serial monitor to see what a request looks like and what a response looks like.
Here is what it looks like:
IMG_5070.jpg

But you really need to learn how the CAN bus application layer works, and how UDS works.
Here are some links that will help you:
https://en.wikipedia.org/wiki/Unified_Diagnostic_Services
https://en.wikipedia.org/wiki/OBD-II_PIDs

I built a data monitor that does exactly what you are looking for

I can try help you out more once you've gotten the basic knowledge to pull the data from the car.
 
Rezo, you have a good project and it looks like this is exactly what I need! I want to display coolant temperature, transmission oil temperature and possibly a few more parameters. My dashboard doesn't display this information :(
The principle of working with a CAN bus through a getway is roughly clear to me, a request for a certain parameter is made and a response is obtained with data on it.
But I still can't find examples of the implementation of reading for T4.
Which transceiver should you use?
How to connect it to T4 and how to programmatically receive data?
I would be very grateful to you for such information and an example of your implementation of the code and connecting the transceiver to T4 and to the CAN bus. As for the difference in car platforms (on my Jetta PQ25, unfortunately), I think there should be no problems. PIDs must be the same.
 
standard pids are fine, but not OEM specific. each car and year can be different. Some data is already in stream while you have the option to request them. But not all requested pids are available, and require reverse engineering the stream or using UDS, which are manufacturer specific. Breakout boards are available for T4, you'll likely use 2 busses anyways (comfort bus and high speed bus) so you may look into those since theyre 12v ready to plug and play. Rezo may be using different hardware, but they all should work the same. Some transceivers have been shown to not be compatible though
 
As Tony mentioned, UDS is not easy to understand and it's not documented anywhere but it's got more enhanced data than standard OBD2 PIDs - that's why can sniffing individual PID's is the best option IMO to find what you are looking for over UDS.

This is my setup to do CAN sniffing:
canSniff_setup.jpg
1. HEX V2 used with VCDS to send requests and see realtime data
2. Teensy 3.2 + TI sn65hvd230 CAN transceiver placed in an OBD2 connector
3. Y splitter that connects to both devices to the cars OBD port

I use a simple sketch on the Teensy that print's every CAN frame it see's (from either end: car or VCDS) onto the Arduino serial monitor. I then log each frame (request/response) and value.

I must note, on the PQ25 you most probably have a Bosch MED ECU and it's not as fast as the MQBs Continental Simos, so request poll speed might have to be lower in your case.
I know for a fact that the MQB UDS requests don't work with the PQ35 platform as I have plugged my device into a VW Scirocco and it failed to return any positive UDS responses.


For the Teensy 4 I used the TI sn65hvd230 CAN transceiver on CAN1 (pins 22 and 23).
Here is a simple sketch that will request engine coolant temp and save it into a variable and print it out in your serial monitor:
Code:
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;

int engineCoolantTemp = 0; // Save the data here

void setup(){
  while (!Serial && millis() < 5000);
  Serial.begin(115200);
  Can0.begin();
  Can0.setBaudRate(500000);
  Can0.setMBFilter(REJECT_ALL);
  Can0.setMBFilter(MB1, 0x7E8);
  Can0.enableMBInterrupt(MB1);
  Can0.onReceive(canSniff);

}

const long loopDelay1 = 500; // Make a request every 500ms
unsigned long timeNow1 = 0;
void loop(){
  Can0.events();
  if (millis() > timeNow1 + loopDelay1)
    {
      timeNow1 = millis();
      canTx_OBD(); // Query data from CAN BUS via OBD
      //canTx_UDS(); // Query data from CAN BUS via UDS

    }
}


void canSniff (const CAN_message_t &msg){ // Global callback to catch any CAN frame coming in
  Serial.println(" ");
  Serial.print("MB: ");
  Serial.print(msg.mb);
  Serial.print("  ID: 0x");
  Serial.print(msg.id, HEX);
  Serial.print("  EXT: ");
  Serial.print(msg.flags.extended);
  Serial.print("  LEN: ");
  Serial.print(msg.len);
  Serial.print(" DATA: ");
  for (uint8_t i = 0; i < 8; i++)
    {
      Serial.print(msg.buf[i], HEX);
      Serial.print(" ");
    }
    if (msg.buf[1] == 0x01 && msg.buf[2] == 0x05){
        engineCoolantTemp = msg.buf[3] - 40;
        Serial.println(" ");
        Serial.printf("OBD: Engine Coolant Temp: %d c", engineCoolantTemp);
    }

    if (msg.buf[1] == 0x22 && msg.buf[2] == 0xf4 && msg.buf[3] == 0x05){
        engineCoolantTemp = msg.buf[4] - 40;
        Serial.println(" ");
        Serial.printf("UDS: Engine Coolant Temp: %d c", engineCoolantTemp);
    }

}


void canTx_OBD(){ // Request function to ask for Engine Coolant Temp via OBD request
  CAN_message_t msgTx, msgRx;
    msgTx.buf[0] = 0x02; 
    msgTx.buf[1] = 0x01;
    msgTx.buf[2] = 0x05;
    msgTx.buf[3] = 0; 
    msgTx.buf[4] = 0;
    msgTx.buf[5] = 0; 
    msgTx.buf[6] = 0;
    msgTx.buf[7] = 0;
    msgTx.len = 8;            // number of bytes in request
    msgTx.flags.extended = 0; // 11 bit header, not 29 bit
    msgTx.flags.remote = 0;
    msgTx.id = 0x7E0; // request header for OBD
    Can0.write(msgTx);
    Serial.println("canTx_OBD REQ sent"); 
  }

 void canTx_UDS(){ // Request function to ask for Engine Coolant Temp via UDS request
  CAN_message_t msgTx, msgRx;
    msgTx.buf[0] = 0x03; 
    msgTx.buf[1] = 0x22;
    msgTx.buf[2] = 0xf4;
    msgTx.buf[3] = 0x05; 
    msgTx.buf[4] = 0;
    msgTx.buf[5] = 0; 
    msgTx.buf[6] = 0; 
    msgTx.buf[7] = 0;
    msgTx.len = 8;            // number of bytes in request
    msgTx.flags.extended = 0; // 11 bit header, not 29 bit
    msgTx.flags.remote = 0;
    msgTx.id = 0x7E0; // request header for OBD
    Can0.write(msgTx); 
    Serial.println("canTx_UDS REQ sent"); 
  }

You can try first the OBD method of request, then you can try the UDS method by commenting out the OBD request and uncommenting the UDS request in the main loop.
 
Rezo, thank you very much for the information, fortunately I have this transceiver. Today I will try to connect according to your example.
 
By the way, I plug into the OBD port, not directly to any of the available busses in the car. I was after true P&P :D
 
Rezo, thanks for the hints and code, this works and I'm glad it does!
Well, I'm continuing to work on the project.
IMG_5868.jpg
IMG_5871.jpg
IMG_5873.jpg
 
vgopaitsa I'm really glad it's working for you!

If you want to make a nice GUI, I can suggest the following:
Ring gauge - this is fairly easy to adapt to your current display library, and looks cool with little effort
LVGL - this is complex to set up but looks really great, I've just started to port my own project to this graphics library


Show us the progress as you move along!
 
Hello, I am trying to read some data from my car (Honda Civic 2015) and I am following all the inrofmation you are giving but I don't succeed. Here I post my code, hoping you can see where I am wrong.

I am already tried a lot of request id and changing from 11 to 29 bits with "can_MsgTx.flags.extended = 0" tried with 0 and 1 but no answer from Canbus, I always have random numbers doesn't matter if I request something or not.

//CANBUS Library
#include "FlexCAN.h"
#include "kinetis_flexcan.h"

// Specify CAN baudrate
FlexCAN CANbus(500000);//1000000);//500000);
static CAN_message_t rxmsg;


#define PID_REQUEST 0x7DF
#define PID_REPLY 0x7E8
#define ENGINE_COOLANT_TEMP 0x05

void setup() {


//Initialize CAN
CANbus.begin();

Serial.begin(115200); //CANBUS Serial
(!Serial);

while (!Serial){
Serial.print("Waiting connection \n");
}


Serial.print("Start reading \n");
}

elapsedMillis DisplayTime; //Establish a timer to prevent unnecessary screen rewrite

void loop(void) {

///////////////Look for CAN broadcasts///////////////
CAN_message_t can_MsgTx;

can_MsgTx.buf[0] = 0x02;
can_MsgTx.buf[1] = 0x01;
can_MsgTx.buf[2] = ENGINE_COOLANT_TEMP;
can_MsgTx.buf[3] = 0;
can_MsgTx.buf[4] = 0;
can_MsgTx.buf[5] = 0;
can_MsgTx.buf[6] = 0;
can_MsgTx.buf[7] = 0;
can_MsgTx.len = 8;
//can_MsgTx.ext = 0;
can_MsgTx.flags.extended = 0;
can_MsgTx.flags.remote = 0;
can_MsgTx.id = PID_REQUEST;
//can_MsgTx.id = 0x222;
//can_MsgTx.id = 0x7E0;
// can_MsgTx.timeout = 500;
CANbus.write(can_MsgTx);

while ( CANbus.read(rxmsg) ) {
if ((rxmsg.id == PID_REPLY) or (rxmsg.buf[0] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[1] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[2] == ENGINE_COOLANT_TEMP)){
Serial.print("Answered \t");
Serial.print(rxmsg.buf[2]);
Serial.print("\t");
Serial.print(rxmsg.buf[3]);
Serial.print("\n");
if (rxmsg.buf[2] == 0x05){
Serial.print("Engine coolant temp: \t");
Serial.print(rxmsg.buf[3] - 40);
Serial.print("\n");
}
}
}


// if ( DisplayTime < 150 ) return;
// DisplayTime = 0;
}

Thank you
 
Hi Sandra, you may want to invest into a CAN bus analyzer like this one, it saved me a lot of time while debugging a Teensy-based CAN bus device.

Paul
 
Took me some fiddling and using the CAN analyzer to get your code working.
Apparently the way the FlexCan library is invoked/called makes a difference.
Here is the working code:
Code:
//CANBUS Library
#include "FlexCAN.h"
//#include "kinetis_flexcan.h"

// Specify CAN baudrate
//FlexCAN CANbus(500000);//1000000);//500000);
static CAN_message_t rxmsg;
static CAN_filter_t allPassFilter;


#define PID_REQUEST 0x7DF
#define PID_REPLY 0x7E8
#define ENGINE_COOLANT_TEMP 0x05

void setup() {
  //Initialize CAN
  allPassFilter.flags.extended = 0;  // 1 for extended/29 bit IDs
  [COLOR="#FF0000"]Can0.begin(500000, allPassFilter);[/COLOR]

  Serial.begin(115200); //CANBUS Serial
//  (!Serial);
//
//  while (!Serial) {
//    Serial.print("Waiting connection \n");
//  }
  Serial.print("Start reading \n");
}

//elapsedMillis DisplayTime; //Establish a timer to prevent unnecessary screen rewrite

void loop() {

  ///////////////Look for CAN broadcasts///////////////
  CAN_message_t can_MsgTx;

  can_MsgTx.buf[0] = 0x02;
  can_MsgTx.buf[1] = 0x01;
  can_MsgTx.buf[2] = ENGINE_COOLANT_TEMP;
  can_MsgTx.buf[3] = 0;
  can_MsgTx.buf[4] = 0;
  can_MsgTx.buf[5] = 0;
  can_MsgTx.buf[6] = 0;
  can_MsgTx.buf[7] = 0;
  can_MsgTx.len = 8;
  //can_MsgTx.ext = 0;
  can_MsgTx.flags.extended = 0;
  can_MsgTx.flags.remote = 0;
  can_MsgTx.id = PID_REQUEST;
  //can_MsgTx.id = 0x222;
  //can_MsgTx.id = 0x7E0;
  // can_MsgTx.timeout = 500;
  Can0.write(can_MsgTx);

  if (Can0.read(rxmsg)) {
    if ((rxmsg.id == PID_REPLY) or (rxmsg.buf[0] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[1] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[2] == ENGINE_COOLANT_TEMP)) {
      Serial.print("Answered \t");
      Serial.print(rxmsg.buf[2]);
      Serial.print("\t");
      Serial.print(rxmsg.buf[3]);
      Serial.print("\n");
      if (rxmsg.buf[2] == 0x05) {
        Serial.print("Engine coolant temp: \t");
        Serial.print(rxmsg.buf[3] - 40);
        Serial.print("\n");
      }
    }
  }

  delay(500);
  // if ( DisplayTime < 150 ) return;
  // DisplayTime = 0;
}

This is the serial monitor output:

Serial monitor.png

I had the CAN bus analyzer set to auto-answer whenever a PID_REQUEST frame came in and send out a PID_REPLY frame with rxmsg.buf[2] set to 0x05:

analyzer.png

Hope this helps.
Paul
 
Here is a photo of the setup I used for testing:

setup.jpg

And yes, you do need a CAN bus transceiver, like this.

Paul
 
Thank you for you answer PaulS, I was checking and no luck but finally I change my transceiver for a new one (I alredy had it but I didn't remember) and now it connects and I have some communication, but the results are not the desired ones.
I would like to have the ones you have on your post but mine are different, for ENGINE_COOLANT_TEMP I gost this:

Captura.JPG

I tried to changed to RPM (0X0C) and I got something better:

Captura2.JPG

the thing is that when the car is off I get the pid on buf 2 but when engine is on I get the pid on buf 3, the numbers seem to be good ones.

code is the same but just printing all id and bufs as seen in the image, so first number is id, next one is buf[0] and so on...:

Serial.print("Answered \t");
Serial.print(rxmsg.id);
Serial.print("\t");
Serial.print(rxmsg.buf[0]);
Serial.print("\t");
Serial.print(rxmsg.buf[1]);
Serial.print("\t");
Serial.print(rxmsg.buf[2]);
Serial.print("\t");
Serial.print(rxmsg.buf[3]);
Serial.print("\t");
Serial.print(rxmsg.buf[4]);
Serial.print("\t");
Serial.print(rxmsg.buf[5]);
Serial.print("\t");
Serial.print(rxmsg.buf[6]);
Serial.print("\t");
Serial.print(rxmsg.buf[7]);
Serial.print("\n");

Thanks
 
From this page I read: "1 - 1,0C,2,"Engine RPM",0,16383.75,rpm,((A*256)+B)/4".
It's apparently generic OBD2. The RPM PID 0x0C only returns 2 bytes, so buf[0] and buf[1]. You can verify that by printing Serial.print(rxmsg.len);.
The formula is given as ((A*256)+B)/4. When I fill in buf[1] for A and buf[0] for B, I got something like 769RPM. Sounds reasonable.

Paul
 
Thanks for your answer PaulS, I am not able to get this 769rpm, getting any of the buf combination. As far as I can read on wikipedia and some other pages buf[0] is the number of additional bytes in the response, buf[1] should be 0x41 as it's the mode I am reading on, buf[2] should return the pid I asked for, in this case 0x0C and buf[3] and 4 should be the answer applying the formula.

https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_09_-_Request_vehicle_information

Captura2.JPG

But this is the answer I get (first number is the len of the answer):

Captura.JPG

I don't get the reply code (id) I should, on the buf[0] I get 4 additional bytes which seems reasonable, 2 are the answer, one the pid I asked for and the other one the method I am using to get the data. But on buf[1] I should get 0x41 which is 65 and I get the pid I asked for, on buf[2] I get nothing, and on buf 3 and 4, where I should get the data I get numbers that using the formula are bigger than 2000 when my car ir just beow 1000 (around 900).

On the webage you posted the link I realized that I cannot check the coolant temp (unfortunatelly) but,rpm should workd.

Thanks :)
 
code is the same but just printing all id and bufs as seen in the image, so first number is id, next one is buf[0] and so on...:
That is a good idea. But do you still have this line in?
Code:
if ((rxmsg.id == PID_REPLY) or (rxmsg.buf[0] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[1] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[2] == ENGINE_COOLANT_TEMP)) {
Perhaps you should change that to:
Code:
if ((rxmsg.id == PID_REPLY){
And print out the length of the CAN reply message as well for diagnostic purposes:
Code:
Serial.print(rxmsg.len);
Serial.print("\t");

Paul
 
But do you still have this line in? [CODE said:
if ((rxmsg.id == PID_REPLY) or (rxmsg.buf[0] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[1] == ENGINE_COOLANT_TEMP) or (rxmsg.buf[2] == ENGINE_COOLANT_TEMP)) {[/CODE]

Yes I have it, if I remove all or conditions and just leave rxmsg.id nothing is showed. No answer as the reply is not the one it should.
 
Hmm, if you don't get a PID_REPLY at all, then something else is wrong. Perhaps it's a hardware/wiring issue?
I think you really need a CAN analyzer now to debug the issue.

Paul
 
Thanks for the help, I will try to get one and check. I was thinking it could be something regarding the hardware/wiring but I was already using this hardware/wiring on another project and it worked using canbus.
 
Hello,

I already got an analyzer and I am trying to test, once I was able to see the data on the program and everything was good but when I tried to configure a result I lost all communication and now I cannot see anything on the analyzer (welll, the green light of receiving is blinking correctly), maybe I forgot something on the configuration of the analyzer.

Captura.JPG

Thanks
 
Your screen looks similar to my screen in message #12. Did you click the "Set and Start" button?
If it is still not working, exit the program. Unplug and replug the USB cable of the analyzer. Then start the program again.
Check the correct COM port [don't confuse it with the Teensy COM port] and click the "Open" button. Then click the "Set and Start" button.
That should be it.

Paul
 
It's strange, I was able to see the values once but now only green led is blinking and no way to see anything , without changing any wires, I will keep trying.
Thanks
 
A few weeks ago, my USB CAN analyzer acted up weird.
I then resetted the analyzer as described here and it was working normally again.
Perhaps give that a try?

Paul
 
Dear Paul,

thank you for your answer, I was on a busniess trip and no way to test it. I did the reset and worked again, now I see the arduino code is ok, as it is working with CAN analyzer, so I need to figure out what's happening so it's not working with my car.

Thank you :eek:
 
Back
Top