Reading data from CAN bus volkswagen

Apologies for reviving this old thread but I am looking for some help in retrofitting a VW electric steering rack from a MQB platform vehicle into a PQ platform one.
To do this I need some CANlogs from an MQB platform vehicle, which some on here have posted about.
Hopefully they can help.
Thanks
 
Who-Hoo, Thank You! To all the folks who opened and contributed on this post, esp. Mark for sharing all the code and CAN transcripts, and Paul S for research and the link to the ISO standard for CAN protocol (29 bit ID format).

I'm trying to build Teensy Tach- similar to you guys, reading obdii with my teensy and drawing/updating gauges on an 800x480 TFT display. My instrument cluster lacks RPM, ECT and inverter coolant temp (2022 Accord Hybrid), and I'd love to have these on my dash.

I lack the ability to connect my teensy to car and PC simultaneously, so I wrote a simple test program to output the data byte(s) of a CAN message with LED flashes. For days it never flashed that it read a message. I was operating on information all over the internet about using 7DF and 7E0 for msg. IDs. Then I found Mark's project here, and switched my test code to 29 bit IDs. Still no go. So I added the TX msg for init (2, 0x10, 3, ...) and my teensy flashed that it read a message! But still no go, it wasn't recognized. Then I added a 100 ms delay after sending the init message, and sending my request for ECT (2, 1, 5, ...), and POOF! That did the trick. I read that my cold engine temp was 0x1C = 28 deg. C = 82.4 deg. F. My dashboard reported 84 deg. F outdoor temp, under a rising sun.

For Honda, the entire internet is wrong, Mark is right-on.

PS- My CAN return messages don't come back on ID=18DAF110. I have to parse ID with (inMsg.id & 0xffffff00) == 0x18DAF100. I must attempt to LED-flash that last byte of reply ID.

My test code, for anyone interested:

Code:
/*  OBDIITach.ino - Uses CAN transceiver and 5" TFT LCD display to read rpm, engine temp, and
    inverter temp from OBDII port, and display as dial-gauge tachometer with temp gauges.

    dual CAN transceiver from pjrc.com store

    RAIO 5" TFT LCD display from buydisplay.com store, configured for 16 bit parallel, reconfigured
    for 4-wire SPI.  Also configured to use 5v for VDD, while like teensy, signal I/O is 3.3v.

  Kurt Funderburg - Jul 2025

  * Windows don't do all I thought they did.  setActiveWindow only contains text/graphics which start
  inside the window.  They do not implement their own coord system.  Modified to use screen coords
  instead of "window coords".

  *** THIS IS 29 BIT CAN ID VERSION ***
*/
#include <FlexCAN_T4.h>

//CAN request schedule, timeout
#define CAN_RD_TIMEOUT  100   // ms  (10-20mS typical, 100 is good threshold)

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> can1;
uint16_t currentPID = 0x00;

elapsedMillis replyTimer;    //timeout
enum RequestState { IDLE, WAITING_FOR_RESPONSE };
RequestState requestState = IDLE;
enum PIDType { EMPTY, RPM, COOLANT_TEMP, INVERTER_TEMP };
PIDType pendingRequest = EMPTY;

CAN_message_t initCanMsg, itempCanMsg;
 
uint rt;  // real-time testing

void setup() {
//  Serial.begin(115200);
//  while (!Serial);
//  if (Serial) Serial.println("Serial started.");
  // Initialize CAN connection
  can1.begin();
  can1.setBaudRate(500000);
  delay(1000);
  //can1.setMBFilter(REJECT_ALL);
  //can1.setMBFilterRange(MB1, 0x18DAF100, 0x18DAF110);
//  can1.setMBFilterRange(MB1, 0x7E8, 0x7EF);
//  Serial.println("CAN OBD-II Reader Started");

  // * Itemp CAN request message  NOTE: 1 or 2 other ECTs from Mode 1, PID x67 req.
  // * The diagnostic reader initiates a query using CAN ID 7DFh, which acts as a broadcast address,
  // and accepts responses from any ID in the range 7E8h to 7EFh. ECUs that can respond to OBD queries
  // listen both to the functional broadcast ID of 7DFh and one assigned ID in the range 7E0h to 7E7h.
  // Their response has an ID of their assigned ID plus 8 e.g. 7E8h through 7EFh.
  // * This establishes a diagnostic session with the ECU and is usually necessary before any other
  // commands can be sent.  IDH: 07, IDL: E0, Len: 08, Data: 02 10 03 00 00 00 00 00
  // * 29-bit ID wake-up message worked on a 2020 Civic (followed by 29-b ID RPM request)
  // ID: 0x18DB33F1  OVERRUN: 0  MSG: 0x02 0x10 0x03 0x00 0x00 0x00 0x00 0x00

  initCanMsg.id = 0x18DB33F1;     // 29b ID equiv. to 7DF.
  initCanMsg.len = 8;
  initCanMsg.flags.extended = 1;
  initCanMsg.flags.remote = 0;
  initCanMsg.buf[0] = 2;
  initCanMsg.buf[1] = 0x10;
  initCanMsg.buf[2] = 0x3;    //CAN ECU (gateway?) init
  memset(&initCanMsg.buf[3], 0xCC, 5);
  can1.write(initCanMsg);   //Not sure I need this, since Mike got his 2020 Civic working w/o it.
  delay(100);

  itempCanMsg.id = 0x18DB33F1;     // ChatGPT sez 7E0, or possibly 7E1 targets ECU on mode 22.
  itempCanMsg.len = 8;
  itempCanMsg.flags.extended = 1;
  itempCanMsg.flags.remote = 0;
  itempCanMsg.buf[0] = 2;
  itempCanMsg.buf[1] = 1;
  itempCanMsg.buf[2] = 0x5;    //ECT
  memset(&itempCanMsg.buf[3], 0xCC, 5);

  pinMode(13, OUTPUT);  //led init code
  digitalWrite(13, 1);
  delay(500);
  digitalWrite(13, 0);
  delay(2000);
}  // setup()

void loop() {
  // If no request is in progress, check if time to launch. If launching
  // request, reset schedule and timeout timers, flag listening
  if (requestState == IDLE && pendingRequest == EMPTY) {
    can1.write(itempCanMsg);
    currentPID = itempCanMsg.buf[2];
    replyTimer = 0;
    pendingRequest = INVERTER_TEMP;
    requestState = WAITING_FOR_RESPONSE;
  }
 
  // Read CAN (int)round(RPM data/10), (int)round(Temp data/2)
  // Read can msg, if valid and if listening, update corresponding gauge
  CAN_message_t inMsg;
  if (can1.read(inMsg)) {
  digitalWrite(13, 1);  //read a msg code on led
  delay(250);
  digitalWrite(13, 0);
  delay(250);
  digitalWrite(13, 1);
  delay(250);
  digitalWrite(13, 0);
  delay(250);
    // 18DAF110 targets F1 (used as source in request messages), and comes from ECU10 which I suspect is OBD gateway.
    if (requestState == WAITING_FOR_RESPONSE && //inMsg.id == 0x18DAF110 &&
         inMsg.buf[1] == 0x41 && inMsg.len >= 3) {
      uint16_t pid;
      pid = (inMsg.buf[2]);
      if (pid == currentPID) {      //if in msg pid same as last requested
        showItemp(inMsg.buf[3] - 40);
        requestState = IDLE;
        pendingRequest = EMPTY;
      }
    }
  }

  // if reply timeout, reset state to resume request schedule
  if(replyTimer >= CAN_RD_TIMEOUT && requestState == WAITING_FOR_RESPONSE) {
    requestState = IDLE;
    pendingRequest = EMPTY;
  }

  delay(10);    // no need to scream through loop at 600MHz.  Hopefully this will keep cpu cooler.

}  // loop()

void showItemp(uint16_t val) {
  for(int i=0; i<10; i++) {   //quick-flash 10 times
    digitalWrite(13,1);
    delay(50);
    digitalWrite(13,0);
    delay(100);
  }
  delay(2000);

  for(int i=0; i<16; i+=4) {   //flash 4 bits at a time
    for(int j=0; j<4; j++) {
      if(((val << (i+j)) & 0x8000) == 0x8000) {
        digitalWrite(13, 1);
        delay(750);
        digitalWrite(13,0);
        delay(500);
      } else {
        digitalWrite(13, 1);
        delay(100);
        digitalWrite(13,0);
        delay(1150);
      }
    }
    delay(4000);
  }
} //showItemp
 
Last edited:
I discovered that my looped requests for ECT from odbii results in 3 different reply messages for mode 0x41 and PID 5. They come from 3 different message IDs: 18DAF10C, 18DAF102, and 18DAF101. They arrive sequentially, in that order, and sequence repeats. Strange!
 
@KurtF:

Thanks for your post. I am very pleased that someone else is able to benefit from my learning path as well. Like yourself, I accomplished what I did with the help of others. That's one of the greatest strengths of this particular forum: @PaulStoffregen created a great family of devices with great support, and a forum full of experts who willingly contribute their time & knowledge for the benefit of the entire community !!

Mark J Culross
KD5RXT
 
The easiest way to figure out what message is the the one you need, and how to figure out the values is to use a diagnostic tool that can display that value, and sniff the bus while doing so and read out the messages.

This is what I did to figure out messages and values for various VW cars
 
Yup, I've come to realize that too: binary Morse code is not a good way to see what's going on. Since I don't like to buy a tool I'll only use once, I've decided to temporarily relocate my PC to a corner bedroom, and park my car outside it's window, and run a 12' USB cord to my Teensy on the OBDii port. I've already written the sketch for that mode of testing. The long extension cord works great with my webcam and keyboard, so I'm expecting it to work for the Teensy-Arduino IDE link.

I'm getting CAN replies in triplicate!
 
Code:
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2     1     C    CC    CC    CC    CC    CC
RX:    18DAF10E     8     4    41     C     0     0    55    55    55
RX:    18DAF101     8     4    41     C     0     0    55    55    55
RX:    18DAF102     8     4    41     C     0     0    55    55    55
RX:    18DAF106     8     4    41     C     0     0    55    55    55
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2     1     5    CC    CC    CC    CC    CC
RX:    18DAF101     8     3    41     5    7F    55    55    55    55
RX:    18DAF102     8     3    41     5    7F    55    55    55    55
RX:    18DAF106     8     3    41     5    7F    55    55    55    55
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2     1    67    CC    CC    CC    CC    CC
RX:    18DAF10E     8     5    41    67     3    7F    59    55    55

Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2     1    67    CC    CC    CC    CC    CC
RX:    18DAF10E     8     5    41    67     3    7F    5A    55    55
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2    22    34     0    CC    CC    CC    CC
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2    22    42    34    CC    CC    CC    CC
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2    22    42    14    CC    CC    CC    CC
Serial started.
CAN OBD-II Reader Started
       ID           LEN   0     1     2     3     4     5     6     7
TX:    18DB33F1     8     2    22    10    29    CC    CC    CC    CC

Strange results from finally getting my PC hooked up to my Accord via Teensy. Above, I logged the replies from requesting RPM and ECT from 2 different PIDs related to coolant temp. PID 67 returns 2 coolant temp sensor readings with one request, with the first temp value duplicating that read from PID 5, the main coolant temp PID. I got these PIDs from the wikipedia list.

As you can see, the RPM request (PID C) returns 4 replies from 4 different ECUs on the bus. PID 5 returned 3 replies and PID 67 just 1. There is no single request ID which answers all 3 requests. But 18DAF10E replies to both the RPM and the "double" ECT request, and getting both temps in one request simplifies the logic of my tach program. So that is what I will parse requests on in my tachometer code.

The last 4 requests above were my attempts to find inverter coolant temp on mode 22, following an obscure internet reference and ChatGPT making a bad guess (hallucinated on this). Shit. I just realized I had the wrong buf[0]=length values for the mode 22 requests. Invalid result. Mode22 uses 2-byte PIDs.
 
Try mode 22 with mode 01 PIDs
On the VW the 1st byte of each pid was F4
So F4XX for standard OBD pids in mode 22
 
Back
Top