OBD2 VIN auslesen

Hallo zusammen,
ich habe einen Arduino Uno und ein Seeed Can Shield 1.2.

Basierend auf diesem Beispiel:
https://github.com/Seeed-Studio/CAN_BUS_Shield/blob/master/examples/OBDII_PIDs/OBDII_PIDs.ino

kann ich im Mode 09 mit PID 02 die VIN abfragen.
Leider liefert die Funktion “CAN.readMsgBuf” in Zeile 97 nur 8 Bytes zurück.
Für die VIN liefern die Bytes 6 bis 8 die ersten brauchbaren Zeichen. In meinem Fall für Audi: WAU

Wie kann ich mehr als die 8 Bytes von dem Respons erhalten? Die VIN liefert 17 Bytes.
Reicht es in Zeile 93 den Wert “unsigned char buf[8];” auf “unsigned char buf[26];” zu erhöhen?
Oder liefert “readMsgBuf” eh nur 8 Bytes? Wenn ja, wie erhalte ich mehr Bytes?

hier als Vorschau der Code Ausschnitt:

void taskCanRecv()
{
    unsigned char len = 0;
    unsigned char buf[8];

    if(CAN_MSGAVAIL == CAN.checkReceive())                   // check if get data
    {
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

        Serial.println("\r\n------------------------------------------------------------------");
        Serial.print("Get Data From id: ");
        Serial.println(CAN.getCanId(), HEX);
        for(int i = 0; i<len; i++)    // print the data
        {
            Serial.print("0x");
            Serial.print(buf[i], HEX);
            Serial.print("\t");
        }
        Serial.println();
    }
}

Hi

Nach meinen Recherchen hat das CAN-Protokoll nur 8 Nutzbytes, Mehr gibt es nicht.
Im CAN-Bus ist es üblich, daß jeder Sensor eine eigene ID hat (im Standard 11bit, in Extended 29bit).
Diese ID dient auch der Priorisierung und Kollisionsvermeidung.
Somit denke ich, daß Du auf die empfangene ID prüfen musst, wie die 8 Byte Nutzdaten zu interpretieren sind.
Denke, Du bekommst mehrere CAN-IDs im Terminal angezeigt?

Leider nur Halbwissen, mit den 'nur 8 Byte' aber leidvolle Eigenerfahrung.

MfG

Danke für den Hinweis,
laut Spezifikation

werden 17 Bytes zurückgegeben. Die Funktion "readMsgBuf" liefert aber für "len" nur einen Wert von 8.
Also 8 Bytes.

Dann ist die von Dir genutzte Lib evtl. nicht für diesen Modus geeignet. Denn da steht drin:

#define CAN_MAX_CHAR_IN_MESSAGE (8)

Gruß Tommy

Dann kann logischerweise nicht mehr kommen. Danke Tommy.

in der Lib mcp_can.h steht:

#include "mcp_can_dfs.h"

#define MAX_CHAR_IN_MESSAGE 8

und in der mcp_can_dfs.h:

#define CAN_MAX_CHAR_IN_MESSAGE (8)

Da erst die mcp_can-dfs.h includiert wird und danach die Variable MAX_CHAR_IN_MESSAGE erneut definiert wird, sollte es doch funktionieren, wenn ich in der can.h den Wert von 8 auf 26 ändere?
Natürlich dann auch im Sketch selber die Werte für die Variablen mit zu ändern.

digidax:
... sollte es doch funktionieren, wenn ich in der can.h den Wert von 8 auf 26 ändere?

Nein. Bei CAN ist von der Definition und der Hardware-Implementierung her bei acht Byte Schluß. Ich habe von einer Erweiterung auf längere Nachrichten gehört, aber die hat sicher noch keinen Eingang in die OBD-II-Spezifikation gefunden.

Das Auto muss also die VIN in mehrere Nachrichten aufteilen. In diesem Blogbeitrag gibt es unten ein Beispiel.

In der englischen Wikipedia findet sich Mode 9 PID 1 "VIN Message count" - vielleicht hilft der, um die Anzahl Nachrichten herauszufinden.

Gruß Walter

Das mit den 17 Bytes Datengröße stimmt schon, die Daten werden mit ISO-TP übertragen

Daten bis 7 Bytes kommen bei der Diagnose als einzelne CAN-Message (Single Frame), das 1. Byte der CAN-Message wird fürs Protokoll gebraucht.

Eine Übertragung von 17 Bytes ist komplizierter, da kommt erst ein "First Frame" als CAN-Message, darauf muss die Gegenseite mit einem "Flow Control Frame" antworten, dann kommen die restlichen Bytes als "Consecutive Frames". (Jeder Frame passt in eine CAN-Message.)

Edit:
Hier ist der Aufbau der CAN-Messages in Bildern dargestellt
https://www.emotive.de/doc/car-diagnostic-systems/protocols/tp/isotp

Vielen Dank erst einmal für Eure Hilfe. Ihr habt vollkommen recht und ich kann die Funktionsweise nun nachvollziehen. Bei der Implementation bräuchte ich Eure Hilfe, da ich mich erst seit einer Woche mit “C” beschäftige und aus der PHP Ecke komme.

Der First-Frame sollte dann so aussehen:

// send MSG
unsigned char tmp[8] = {0x02, 0x09, 0x02, 0, 0, 0, 0, 1};
CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);

Hier habe ich diesen Code gefunden, leider nur für den Raspberry PI:
https://github.com/carloop/app-vin-reader/blob/master/src/app-vin-reader.cpp

Der für mich relevante Teil beginnt bei Zeile 58.

Aktuell empfange ich die Message wir folgt:

CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf		
for(int i = 5; i<len; i++)    // print the data beginning with byte 5
{			
     lcd.print((char)buf[i]);
}

Auf dem LCD Display erscheint dann “WVW”, die ersten 3 Zeichen der VIN für VW (Byte 5,6 und 7). Soweit so gut.

In der OBDMessage.cpp finde ich in Zeile 79 die Funktion “CANMessage OBDMessage::flowControlMessage()”
Dort (so verstehe ich den Code) wird an das ECU mit der ID 0x7E0 (Moto-SG) mit einer Länge von 8 Byte mit Byte 0 etwas geschickt: msg.data[0] = FLOW << 4;
Der Wert der Variable wird mittels Linksshift um 4 Bit verschoben, nur welchen Wert hat “FLOW”?

Wie baue ich das in meine TX Message an der Stelle ??? ein:

unsigned char tmp[8] = {???, 0, 0, 0, 0, 0, 0, 0};
CAN.sendMsgBuf(0x7E0, 0, 8, tmp);

Danke für jede Hilfe hierfür.

digidax:
nur welchen Wert hat "FLOW"?

Das steht in meinem zweiten Link

Das ers­te Byte beginnt in den obe­ren 4 Bits mit dem Wert 3, der anzeigt, dass es sich um einen Flow-Con­trol han­delt.

Eigentlich müsste es reichen, als Flow Control Frame eine Message mit den drei Bytes 0x30, 0x00 und 0x00 zu senden. Das steht für "schick mir den ganzen Rest so schnell es geht" (Block Size = 0 entspricht alles, STmin = 0 entspricht keine zusätzliche Pause zwischen den Messages)

ArduFE:
Eigentlich müsste es reichen, als Flow Control Frame eine Message mit den drei Bytes 0x30, 0x00 und 0x00 zu senden.

Ach ich glaube meinen Denkfehler erkannt zu haben. Der Sender bin nicht ich, sondern das antwortende Steuergerät- richtig?

Also frage ich ganz normal wie bisher Mode 09 PID 02 an.
Das SG schickt mir die ersten 3 Zeichen: WVW

ich weiß aber, dass da noch mehr kommen muss. Also schicke ich:

unsigned char tmp[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0};
CAN.sendMsgBuf(0x7E0, 0, 8, tmp);

Damit ist die erste Antwort des SG das First Frame, welches mir in der Datenbotschaft die Anzahl von Bytes der Diagnosebotschaft mitteilt. Ich habe ja alles angefragt.
Somit müßte in der Datenbotschaft der Wert 17 erscheinen, die 17 Bytes für die VIN.

Nun sende ich wieder ein Flow Control Frame.
Darauf hin erhalte ich ein Consecutive Frame mit 8 Bytes für die nächsten Zeichen der VIN.
Nun sende ich wieder ein Flow Control Frame.

Jetzt müßte der Rest der VIN kommen.

digidax:
Ach ich glaube meinen Denkfehler erkannt zu haben. Der Sender bin nicht ich, sondern das antwortende Steuergerät- richtig?

Ja.

Nur bei der Anzahl der Bytes muss es noch irgendeinen Unterschied geben. Du schreibst ja, dass da nur 3 Zeichen der VIN kommen, im First Frame sind aber eigentlich 6 Datenbytes. Da muss noch was davor stehen. Auf jeden Fall steht ja im zweiten Byte, wie groß die Daten sind, die da kommen.

digidax:
Nun sende ich wieder ein Flow Control Frame.
Darauf hin erhalte ich ein Consecutive Frame mit 8 Bytes für die nächsten Zeichen der VIN.
Nun sende ich wieder ein Flow Control Frame.

Nein. Wenn du die anderen Einträge im Flow Control Frame auf 0 setzt, kommen alle übrigen Consecutive Frames ohne Pause nacheinander. Wenn du die einzeln haben willst, muss du die BS und STmin Bytes im Flow Control Frame entsprechend setzen.

Und der Flow Control Frame ist nur 3 Bytes groß, du sendest aber 8.

Danke ArduFE,

es kommen nur 3 Bytes, da ich zwar 8 Bytes empfange:

CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf		
for(int i = 5; i<len; i++)    // print the data beginning with byte 5
{			
     lcd.print((char)buf[i]);
}

aber in der Schleife bei 5 (also beim 6. Byte anfange, den Wert auszugeben.

Also meine Hausaufgabe:
Schleife ändern in "for(int 0 = 5; i<len; i++) " und dann den Output seriell als HEX auswerten:

for(int i = 0; i<len; i++)    // print the data beginning with byte 5
 {			
     Serial.print(buf[i],HEX);
     Serial.print("\t");
 }

um zu sehen, was im zweiten Byte steht, nämlich wie groß die Daten sind, die da kommen.

1000 Dank, so langsam macht der CAN Bus Spaß dank Deiner Hilfe,
ich melde mich mit dem Ergebnis.

So, der Request für Mode 09 auf PID 02 liefert 8 Bytes:

10 14 49 02 01 57 41 55 (HEX)

Byte 2: 49 HEX = 73 DEC => es kommen also noch 73 Bytes an Daten.
Bytes 5, 6 und 7 ergeben: WAU (für Audi), die ersten 3 Zeichen der VIN

Ich müßte nun folgende Anfrage senden:

unsigned char tmp[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0};
CAN.sendMsgBuf(0x7E0, 0, 73, tmp);

und diese auswerten, dabei beachten, dass ich 73 Bytes puffern muss?

Fast richtig.

erstes Byte (i=0): 0x10 --> 1 für first frame, 0 für Bit 12...9 der Länge
zweites Byte: 0x14 --> für die restlichen acht Bit der Länge.

Das macht lt. der von ArduFE verlinkten ISO-TP Beschreibung 20 Byte, nicht 73. Das sind die 17 Byte für die VIN und die drei Byte 0x49 0x02 0x01. Was die bedeuten habe ich auf die Schnelle noch nicht rausbekommen.

Gruß Walter

Danke Walter.

Die 0x49, die 9 ist bestimmt die Antwort, dass es sich um den Mode 9 handelt, 0x02 scheint wie bei den 8 Single Frame Anfragen die angefragte PID zu sein.

Ich teste jetzt mal auf diese Antwort die Anfrage

unsigned char tmp[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0};
CAN.sendMsgBuf(0x7E0, 0, 17, tmp);

Mal sehen, was kommt. Versuch macht klug.

Juhu! Es funktioniert.
Die folgenden Antworten haben im Byte 0 die Seqeunznummer und dann 7 Bytes mit dem nächsten Teil der VIN.

Somit liefert die 1. Antwort 3, die 2. Antwort 7 und die 3. Antwort nochmal 7 Bytes.

Vielen Dank an ArduFE und wno158 für die Hilfe und die Erklärung, damit ich es verstanden habe.

lg Frank

Hmmm, irgendwie kommen die Antworten in einer verzögerten Reihenfolge, irgendwie asynchron zur Anfrage.

Anfrage 1 wird nicht beantwortet,
Auf Anfrage 2 kommt Antwort von Anfrage 1
Auf Anfrage 3 kommt Antwort von Anfrage 2
Die 3. Antwort fehlt.

Hier der zusammengeschusterte Code, wie gesagt meine ersten Versuche mit C:

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 tmp[8] = {0x02, 0x09, 0x02, 0, 0, 0, 0, 0};  // Mode 9 PID 2 is the VIN
	unsigned char tmp1[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0};  // Flow Frame Request
	
	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);	
	// recive MSG
    if(CAN_MSGAVAIL == CAN.checkReceive())                   // check if get data
    {		
		CAN.readMsgBuf(&len, buf1);    // read data,  len: data length, buf: data buf		
		
		
		for(int i = 5; i<len; i++)    // print the data beginning with byte 5
        {			
			lcd.print((char)buf1[i]);
			Serial.print(buf1[i],HEX);
			Serial.print("\t");
        }	
		Serial.print("\r\n");
		Serial.println("--------------------------------------------------------------------");
		Serial.println("send Flow Frame Request:");
		Serial.println("--------------------------------------------------------------------");
				
    }
		
		
	lcd.setCursor(0,1);
	
	CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp1);
	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 = 1; i<len; i++)    // print the data beginning with byte 1
        {			
			lcd.print((char)buf2[i]);
			Serial.print((char)buf2[i]);
			Serial.print("\t");
        }	
		Serial.print("\r\n");
		Serial.println("--------------------------------------------------------------------");
		
	
				
    }

	CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp1);	
	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[i]);
			Serial.print((char)buf3[i]);
			Serial.print("\t");
        }	
		Serial.print("\r\n");
		Serial.println("--------------------------------------------------------------------");
		
	
				
    }	
		CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp1);
	
	Serial.print("END");
	Serial.println("--------------------------------------------------------------------");
	
	
	
	delay(1000);
	lcd.clear();
}

Das ist die Ausgabe des seriellen Monitors:
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--------------------------------------------------------------------

Was mache ich da falsch? Danke für die Hilfe.

So ganz auf die Schnelle:

Du schreibst auf den SerialPort, dass Du FlowFrame sendest. Das machst Du aber nicht, sondern fragst hinter der ersten CAN_MSGAVAIL-Klammer nochmal PID 9 an.

So gewollt?

Es gibt zwei Variablen: tmp und tmp1

unsigned char tmp[8] = {0x02, 0x09, 0x02, 0, 0, 0, 0, 0}; // Mode 9 PID 2 is the VIN
unsigned char tmp1[8] = {0x30, 0x00, 0, 0, 0, 0, 0, 0}; // Flow Frame Request

bei der ersten Anfrage wird tmp verwendet:

CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);

bei der Zweiten und Dritten dann tmp1:

CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp1);

oder meinst Du etwas Anderes? Sollte doch richtig sein?

Ich habe jetzt mal eine Schleife mit zwei Single Frame Anfragen programmiert: Geschwindigkeit und Motordrehzahl. Frage ich Fahrzeug Geschwindigkeit ab erhalte ich die Motordrehzahl, bei der Motordrehzahl die Geschwindigkeit. Es scheint, als ob die Antworten versetzt irgendwie im Puffer hängen und damit um +1 verschoben kommen. Wenn ich nur eine Singleframe Anfrage schicke passt es aber.

Kann es eventuell sein, dass ich in der FlowControl Nachricht ein Wait (0x31 im 1.Byte) gleich nach der
nach der Anfrage senden sollte, weil der Arduino eventuell zu langsam mit der Verarbeitung ist?