Teensy 3.2 CAN-Bus ECU Reader with OLED: VIN request

Status
Not open for further replies.

digidax

Member
Hello,

I just have ordered one "Teensy CAN-Bus Breakout Board Include Teensy 3.2". Have taken a look to the source code
https://github.com/skpang/Teensy_CAN-Bus_ECU_reader/blob/master/canbus_ecu_reader.ino
and wondering, if it would be possible to read the VIN (Vehicle Identification Number).

The VIN comes from a request to the ECU with Mode 0x09 and PID 0x02 but has a length of 17 Bytes. A CAN message can only have a length of 8 Bytes, so the protocol uses segmentation mode.
https://en.wikipedia.org/wiki/ISO_15765-2
Does the flexcan-lib can handle segmentation flow control?

Normally, the response would send in the first datagram the Bytes 6,7,8 with the 1st three chars of the VIN, following by two datagrams with Byte 2 to 8 with the next 7 chats of the VIN, so 3 + 7 + 7 = 17 chars of VIN.

For the demo sketch, it would be a nice feature to display in setup() the VIN of the connected car.

thanks.
 
on my vehicle for instance, you can “request” a vin via ECU, but, its also being broadcasted every few seconds as 3 payloads of 8 bytes, you have to assemble them, but its there...
 
okay, sounds good.

So do I understand right, there are two ways:

1. active: sende a request and get back the respons

or

2. passive: listen on BUS to get every few seconds the vin?

If yes, how do you have implement the 2nd way? For my application I do need only after starting up the device reading on time the VIN and compare it with a known one, to keep safe, that the injection I will do is for the right vehicle.

Thanks for help.
 
theres 2 ways, second method is not guarenteed as canbus data changes with every vehicle for every year, but for the most part VIN is always broadcasted. my car uses BCAN and HSCAN, so it has 2 networks, BCAN, surprisingly hosts realtime speedometer compares to the HSCAN which the car mainly uses for it’s instrument cluster, which explains why the gauges run at 1Hz but BCAN is realtime. Again it depends on the car you have, if you want to rely on canbus for portability, dont count on it, datasets are different per vehicle. even the ECU here uses 29bit canids while regular traffic runs on 11bit canids, on the same network.
 
Thanks, than I will do it conservativ with a request on Mode 09 with PID 02.
So does anybody knows a lib based on the FlexCAN lib that extend it with ISO-TP ?
 
Hi,

Yes, Mode 09 PID 02 gives me at all cars i tested the VIN.
Took a long time until i found out how, and how to get the other parts of the message but reading the documentation about the "standard" protocoll again was very helpfull :)

FlexCan standard lib does everything you need.
 
Hi,
FlexCan standard lib does everything you need.

So really only send Mode 09 PID 02 to the ECU and then get back the 17 Byte ASCII coded VIN?

At the moment I get with my arduino uno and can shield on the first reply in the last 3 Bytes the first chars of the VIN,
then sending Flow Frame request, getting the the next 8 Bytes from the ECU where the Bytes 1 to 7 are the next chars of the VIN,
then sending again Flow Frame request, getting the the next 8 Bytes from the ECU where the Bytes 1 to 7 are the last chars of the VIN.
 
So really only send Mode 09 PID 02 to the ECU and then get back the 17 Byte ASCII coded VIN?

At the moment I get with my arduino uno and can shield on the first reply in the last 3 Bytes the first chars of the VIN,
then sending Flow Frame request, getting the the next 8 Bytes from the ECU where the Bytes 1 to 7 are the next chars of the VIN,
then sending again Flow Frame request, getting the the next 8 Bytes from the ECU where the Bytes 1 to 7 are the last chars of the VIN.

Exactly, you have to send the request for the other messages.
That is "the standard" and every OBD2-Tester works like that.
But you just need the send 1st request, when receive that 3 first Bytes of the VIN request the other parts and next both messages comes, without sending again the "Flow Frame Request".

If you have access to the "real" car can-bus message protocoll you will find that situation in almost every request.

First send the request. Then wait for the answer and tell the ECU that you want now all the other parts.
 
Okay Thomas,

then I haven't read the manual not right. I do this at the moment:

1) send {0x02, 0x09, 0x02, 0, 0, 0, 0, 0}; // Mode 9 PID 2 for the VIN
2) get in the 3 last bytes of the resonse the first 3 chars of the VIN: WVW
3) send {0x30, 0x00, 0, 0, 0, 0, 0, 0}; // Flow Frame Request => give me more please
4) get in the first byte the CF id with the number of sequenz 1: 0x21, then the next 7 Bytes of the VIN
5) send {0x30, 0x00, 0, 0, 0, 0, 0, 0}; // Flow Frame Request => give me more please
6) get in the first byte the CF id with the number of sequenz 2: 0x22, then the next 7 Bytes of the VIN

but if do understand you right, sending of 5) is not needed?

best regards and thanks for your help,
Frank
 
That is right, i just checked my "old" sources and when i did it with the UDS standard it was like your way but without Step 5.

After message from 0x21 there comes 0x22 part, maybe with a delay of some milliseconds.

Maybe something different in your way how to read the can messages?
 
Thats the part of the method to get the VIN which I do still use. In the last step, I have add a WAIT message but withou any changes.
The result on the serial output you find below the code. The first request is not answered. On the 2nd request I get the answer of request 1 and so on.
It seems, that anything is stuckin in the buffer. I use the latest master-branch of the Seeed CAN Shield lib. I have declared a lot of double needed var's to separate the error by reading form memory, I know - it's not needed. Only for debugging.

void ReadVIN()
{
// send MSG for VIN Request
unsigned char len = 0;
unsigned char buf1[8];
unsigned char buf2[8];
unsigned char buf3[8];
unsigned char buf4[8];
unsigned char tmp[8] = {0x02, 0x09, 0x02, 0, 0, 0, 0, 0}; // Mode 9 PID 2 is the VIN
unsigned char flow_get[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0}; // Flow Frame Request
unsigned char flow_wait[8] = {0x31, 0x00, 0, 0, 0, 0, 0, 0}; // Flow Frame Wait

lcd.setCursor(0,0);
lcd.print("VIN: ");


Serial.println("--------------------------------------------------------------------");
Serial.println("START, SEND: ");
Serial.println("--------------------------------------------------------------------");
Serial.println("response: ");

CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp); // normal request init for 3 Bytes
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_wait); // please wait
// recive MSG
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if get data
{
CAN.readMsgBuf(&len, buf1); // read data, len: data length, buf: data buf
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_get); // cont. with request for 1st 7 Bytes
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_wait); // please wait
}




Serial.println("--------------------------------------------------------------------");
Serial.println("Flow 1, SEND: ");
Serial.println("--------------------------------------------------------------------");

// recive MSG2
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if get data
{
CAN.readMsgBuf(&len, buf2); // read data, len: data length, buf: data buf
Serial.println("Response Message 2:");

for(int i = 5; i<len; i++) // print the data beginning with byte 1
{
lcd.print((char)buf2);
Serial.print(buf2,HEX);
Serial.print("\t");
}
len = 0;
Serial.print("\r\n");
Serial.println("--------------------------------------------------------------------");


CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_get); // cont. with request for the 2nd 7 Bytes
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_wait); // please wait
}

lcd.setCursor(0,1);

Serial.println("--------------------------------------------------------------------");
Serial.println("Flow 2, SEND: ");
Serial.println("--------------------------------------------------------------------");

// recive MSG3
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if get data
{
CAN.readMsgBuf(&len, buf3); // read data, len: data length, buf: data buf
Serial.println("Response Message 3:");

for(int i = 1; i<len; i++) // print the data beginning with byte 1
{
lcd.print((char)buf3);
Serial.print(buf3,HEX);
Serial.print("\t");
}
len = 0;
Serial.print("\r\n");
Serial.println("--------------------------------------------------------------------");

CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_get); // cont. with request for the 2nd 7 Bytes
//CAN.sendMsgBuf(CAN_ID_PID, 0, 8, flow_wait); // please wait

}

// recive MSG4
if(CAN_MSGAVAIL == CAN.checkReceive()) // check if get data
{
CAN.readMsgBuf(&len, buf4); // read data, len: data length, buf: data buf
Serial.println("Response Message 4:");

for(int i = 1; i<len; i++) // print the data beginning with byte 1
{
lcd.print((char)buf4);
Serial.print(buf4,HEX);
Serial.print("\t");
}
len = 0;
Serial.print("\r\n");
Serial.println("--------------------------------------------------------------------");



}


Serial.print("END");
Serial.println("--------------------------------------------------------------------");



delay(3000);
lcd.clear();
}

#########################################################################################
Serial Output:

START, SEND:
--------------------------------------------------------------------
response:
4B 30 46
--------------------------------------------------------------------
send Flow Frame Request:
--------------------------------------------------------------------
--------------------------------------------------------------------
Flow 1, SEND:
--------------------------------------------------------------------
Response Message 2:
I W A U
--------------------------------------------------------------------
--------------------------------------------------------------------
Flow 2, SEND:
--------------------------------------------------------------------
Response Message 3:
A 1 2 4 0 9 6
--------------------------------------------------------------------
END--------------------------------------------------------------------
 
Ummmm,

why so complicated?

I don't know the Seed Can shield lib.

My lib (almost 100% compatible to FlexCan) can handle like that:
Not real program, just snippets :)

Code:
// var declaration:

unsigned char ReadVIN1st[8]       = { 0x02, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
unsigned char ReadVIN_2nd[8]    = { 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
msg.len = 8;

// send request first part (no loop)
memcpy(msg.buf, ReadVIN1st, msg.len);
Can.write(msg);

// structure for can has msg (sending) and rxmsg (receiving) and buf[8] to store the message // Note: there will always come 8 bytes so msg.len is always 8
// can also be "if Can message is available"
while (Can.read(rxmsg))
        {
             if ((rxmsg.id == YOUR ID YOU LISTEN TO) && (rxmsg.buf[0] == 0x10))
             {
                  Char.VIN[0] = rxmsg.buf[5];
                  Char.VIN[1] = rxmsg.buf[6];
                  Char.VIN[2] = rxmsg.buf[7];
                  memcpy(msg.buf, ReadVIN_2nd, msg.len);
                  Can.write(msg);
             }
             if ((rxmsg.id == YOUR ID YOU LISTEN TO) && (rxmsg.buf[0] == 0x21))
             {
                  Char.VIN[3] = rxmsg.buf[1];
                  Char.VIN[4] = rxmsg.buf[2];
                  Char.VIN[5] = rxmsg.buf[3];
                  Char.VIN[6] = rxmsg.buf[4];
                  Char.VIN[7] = rxmsg.buf[5];
                  Char.VIN[8] = rxmsg.buf[6];
              }

               if ((rxmsg.id == YOUR ID YOU LISTEN TO) && (rxmsg.buf[0] == 0x22))
             {
                  Char.VIN[9] = rxmsg.buf[1];
                  Char.VIN[10] = rxmsg.buf[2];
                  Char.VIN[11] = rxmsg.buf[3];
              }

        }

And there you go, VIN is stored in the Char.VIN array.

Hope that helps a bit :)
 
Okay, I will try to implement the code. I'm coming from the PHP corner, since one week I'm fighting with C++ but it will be go better and better each day.
Thanks. I will report.
 
okay, problem:

For your while (Can.read(rxmsg))

I have to use: CAN.readMsgBuf(&len, buf);

but did not work for me, if I do so with it, the while loop didn't starts to run. If I use it without "while" I get the data.
But then I have to do send / receive / send / receive.
But on the other hand, you're right: the consecutive Frames come without without a request.

I use the Seeed CAN shield: http://wiki.seeed.cc/CAN-BUS_Shield_V1.2/ with MCP2515.
The FlexCAN Lib supports the MCP2515. But where can I set the CS Pin Number? I would try the FlecCAN Lib instead of the MCP_CAN Lib.
 
I have my own PCB with a teensy on top.
There i use the MCP2551 as can transceiver.
No need to set anything up like CS Pin.

I have no experience with the can-shield, because of the easy connection with a can transceiver to the teensy can-port.

With the FlexCan Lib you just need to add a few lines to have a "sniffer" and it is also easy to write messages to the bus.

But i can't help with the can-shield :( sorry
 
Have get the board but have problem for compiling the demo: https://github.com/skpang/Teensy_CAN-Bus_ECU_reader

Code:
C:\Users\frank\AppData\Local\Temp\arduino_build_542565\sketch\ecu_reader.cpp: In member function 'uint8_t ecu_reader_class::request(uint8_t, int*)':

ecu_reader.cpp:35: error: 'CAN_message_t {aka struct CAN_message_t}' has no member named 'flags'
   can_MsgTx.flags.extended = 0; 

             ^

ecu_reader.cpp:36: error: 'CAN_message_t {aka struct CAN_message_t}' has no member named 'flags'
   can_MsgTx.flags.remote = 0;

As a C++ beginner, it seems to me that "flag" was not definied? Hope anybody can help me to get the Demo running.

Thanks and best regards.
 
Thomas, actual status is source code running on the Teensy but getting only "80" displayed. Mybe you can help me to find out what's going wrong:

Based on https://github.com/skpang/Teensy_CAN-Bus_ECU_reader

If have add to the class in ecu_reader.cpp a 2nd function:
Code:
uint8_t ecu_reader_class::request_vin(unsigned char *VIN) 
{

	CAN_message_t can_MsgRx,can_MsgTx1,can_MsgTx2;
	
	// first request
	can_MsgTx1.buf[0] = 0x02;  
	can_MsgTx1.buf[1] = 0x09; // Mode 09
	can_MsgTx1.buf[2] = 0x02; // Pid 02
	can_MsgTx1.buf[3] = 0;
	can_MsgTx1.buf[4] = 0;  
	can_MsgTx1.buf[5] = 0;
	can_MsgTx1.buf[6] = 0;  
	can_MsgTx1.buf[7] = 0;
	can_MsgTx1.len = 8;  
	can_MsgTx1.flags.extended = 0; 
	can_MsgTx1.flags.remote = 0;  
	can_MsgTx1.id = 0x7E0; // Motor-SG
	
	//  flow frame request	
	can_MsgTx2.buf[0] = 0x30;  // flow control frame
	can_MsgTx2.buf[1] = 0x00; 
	can_MsgTx2.buf[2] = 0x00; 
	can_MsgTx2.buf[3] = 0;
	can_MsgTx2.buf[4] = 0;  
	can_MsgTx2.buf[5] = 0;
	can_MsgTx2.buf[6] = 0;  
	can_MsgTx2.buf[7] = 0;
	can_MsgTx2.len = 8;  
	can_MsgTx2.flags.extended = 0; 
	can_MsgTx2.flags.remote = 0;  
	can_MsgTx2.id = 0x7E0; // Motor-SG
		
	// send request first part (no loop)
	Can0.write(can_MsgTx1);
  
	elapsedMillis waiting;     // "waiting" starts at zero
  
	while (waiting < 1000) {   //Check for timeout

	while (Can0.read(can_MsgRx)) 
    { 
 
            if ((can_MsgRx.id == 0x02) && (can_MsgRx.buf[0] == 0x10))
            {
                VIN[0] = (char)can_MsgRx.buf[5];
                VIN[1] = (char)can_MsgRx.buf[6];
                VIN[2] = (char)can_MsgRx.buf[7];

                Can0.write(can_MsgTx2);
            }
			
            if ((can_MsgRx.id == 0x02) && (can_MsgRx.buf[0] == 0x21))
            {
                VIN[3] = (char)can_MsgRx.buf[1];
                VIN[4] = (char)can_MsgRx.buf[2];
                VIN[5] = (char)can_MsgRx.buf[3];
                VIN[6] = (char)can_MsgRx.buf[4];
                VIN[7] = (char)can_MsgRx.buf[5];
                VIN[8] = (char)can_MsgRx.buf[6];
				VIN[9] = (char)can_MsgRx.buf[7];
            }
            if ((can_MsgRx.id == 0x02) && (can_MsgRx.buf[0] == 0x22))
            {
                VIN[10] = (char)can_MsgRx.buf[1];
                VIN[11] = (char)can_MsgRx.buf[2];
                VIN[12] = (char)can_MsgRx.buf[3];
				VIN[13] = (char)can_MsgRx.buf[4];
				VIN[14] = (char)can_MsgRx.buf[5];
				VIN[15] = (char)can_MsgRx.buf[6];
				VIN[16] = (char)can_MsgRx.buf[7];
            }
			
			
            return 1;     
    }

 
  } // while
  return 0;

}

in the test.ino I have this:
Code:
void loop() {

  unsigned char engine_data;
  
  if(ecu_reader.request_vin(&engine_data) == 1)   // Get VIN and display on LCD
  {
	  display.setCursor(40,0);    // x,y		
	  display.println("VIN:");
	  // display.setFont(&FreeSansBold12pt7b);
	  Serial.println(engine_data);
	  //display.fillRect(70,30,55,22,BLACK);  //x,y,width,height

	  display.setCursor(0,30);    // x,y		
	  display.println(engine_data);
	  display.display();

              
          
  } 
  else
  {
	  display.setFont(&FreeSansBold12pt7b);
	  Serial.println(engine_data);
	  //display.fillRect(70,30,55,22,BLACK);  //x,y,width,height

	  display.setCursor(20,40);    // x,y		
	  display.println("no data");
	  display.display();



   }
  
  delay(3000);   
  display.clearDisplay();
  
}
 
Last edited:
Probelm solved. My bad. I have mismatched the ECU response ID with the PID response ID. Everything is working fine. Thanks to Thomas and Sukkin.
 
Status
Not open for further replies.
Back
Top