Laden...

[gelöst] OBD(II) über seriell bedienen

Erstellt von Trekki1990 vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.854 Views
Trekki1990 Themenstarter:in
503 Beiträge seit 2008
vor 3 Jahren
[gelöst] OBD(II) über seriell bedienen

Hallo und guten Morgen!

Ich habe mir mal wieder ein kleines Hobbyprojekt auferlegt.
Ziel ist es, mir eine eigene Multimediaoberfläche im Star Trek Stil (LCARS) zu bauen. Eingebaut wird das ganze im Doppel-DIN Schacht meines Kfz (Subaru BRZ, BJ. 2016).
Ich möchte auch weiterhin einige Live Werte meines Kfz über die OBD Schnittstelle auslesen. Dabei ist mir inzwischen bewusst wie das mit dem CAN BUS funktioniert. Ich schicke einen Request und das entsprechende Steuergerät antwortet.

Folgende Werte möchte ich dann auf meiner eigenen Oberfläche darstellen (Balkendiagramme usw.):- Außentemperatur (°C)

  • verbleibende Treibstoffmenge (%)
  • Motoröltemperatur (°C)

Am OBD Port meines Kfz steckt ein OBDII Bluetooth Modul, dass bereits mit einer Android App aus dem Playstore wunderbar zusammenarbeitet und ich bekomme auch die oben genannten Werte ausgelesen. Also grundsätzlich funktionell.

Ich habe dann den OBD Adapter mit meinem PC verbunden. Registriert hat sich der Dongle auf COM4. Habe mir eine kleine Test GUI geschrieben, die mir serielle Kommunikation ermöglicht. An der Stelle habe ich jetzt mein Problem. Wenn ich das Ganze mit Putty steuere, bekomme ich entsprechend korrekte Antworten zurück. Gebe ich beispielsweise den Befehl "ATZ" ein bekomme ich die ELM Version zurückgeliefert. Mache ich das mit meinem Tool, dann bekomme ich nur "?>". Wenn ich jedoch mit einem COM Port Emulator zwei Ports paare und schicke in Putty etwas los, kommt es in meinem Tool ohne Probleme an.

Anbei meine Methoden zum Senden und Empfangen:


private void btn_send_Click(object sender, EventArgs e)
{
    if (port.IsOpen == true) port.Write(txt_send.Text);
    else txt_receive.AppendText(DateTime.Now.ToLongTimeString() + " - Port ist nicht geöffnet!\r\n");
}


private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var serialPort = (SerialPort)sender;
    var data = serialPort.ReadExisting();

    txt_receive.AppendText(DateTime.Now.ToLongTimeString() + " - " + data + "\r\n");
}

Anbei habe ich noch einen Screenshot um zu verdeutlichen was ich meine. Wäre über einen Denkanstoß sehr dankbar.

VG
trekki

185 Beiträge seit 2005
vor 3 Jahren

Wie schließt du die Zeile in PUTTY ab?
Häng doch mal ein CRLF an deine Daten an.

Trekki1990 Themenstarter:in
503 Beiträge seit 2008
vor 3 Jahren

Hallo Martin, ach Gott ist das peinlich X(
Ich haue ja bei Putty schön auf ENTER... Danke. dafür!

Jetzt kommen die Ergebnisse. Ich habe anscheinend jetzt noch ein Formatierungsproblem. Wie bekomme ich denn jetzt die CAN Messages als Byte Array in eine Liste um die zu analysieren? Er spuckt mir die ja teilweise zerhackt oder an einem Stück aus...

Liegt es eventuell an der Methode wie ich die Daten hole?

serialPort.ReadExisting();

Muss ich hier wie bei der Eingabe prüfen ob ein CRLF dazwischen liegt?

VG
trekki

185 Beiträge seit 2005
vor 3 Jahren

Da es immer sein kann, das die Daten nicht am Stück kommen, musst du auf jeden fall die Daten puffern.
Was das Ende-Zeichen ist, kann ich dir nicht sagen, das weiß ich nicht.
Aber bei Seriellen Übertragungen gigt es normalerweise immer Start- und Ende-Zeichen, eventuell auch noch Länge, BCC usw.

T
708 Beiträge seit 2008
vor 3 Jahren

Hallo zusammen,

leider bin ich absoluter Nerd in diesem Thema 😉

Es wird immer ein Carriage Return gesendet (char 13).
Ob noch ein Linefeed (char 10) folgt, kannst Du mit "ATL" + 0 oder 1 entsprechend de- oder aktivieren. Standardmäßig ist es aus.

Anschließend folgt noch der Abschluss des Responses und die Bereitschaft ein neues Kommando zu empfangen.
Dies geschieht über ein Prompt ">" (char 0x3E)

Ansonsten passiert die Kommunikation standardmäßig, aus Performancegründen, ohne Header und Checksumme. Darum kümmert sich dann der ELM327 (Der Chip im Bluetooth-Dongle).

Es ist auf jeden Fall wichtig, die Daten wie Martin schon schreibt, zu puffern. Dazu gibt es auch ein tolles Tutorial hier im Forum 😄
Template SerialPort

Dann kannst Du einfach Deine SID (0x21) mit der entsprechenden PID (z.B. 0x0C RPM) absenden.
Der Rückgabewert wird dann so aussehen:
61 0C 08 AE

Die 61 steht für die Service ID 0x21 + 0x40.
0C wiederholt die Parameter ID.
0x08 = 8
0xAE = 174
8 * 256 + 174 = 2222 Umdrehungen pro Minute.

Die PID 02, welche Du im Screenshot abprüfst steht für den Fehlerspeicher.
Wenn ich das im Wust der Daten richtig sehe, hast Du noch das "Echo" aktiviert. Also die Wiederholung Deiner Anfrage.
Stell das am besten ab: "ATE0"

Dann empfehle ich noch die Hex-Werte immer zweistellig auszugeben. Am besten noch mit einem Leerzeichen dazwischen. Sonst steigt man ja gar nicht mehr durch 😃

Zu guter Letzt:
Du bekommst keine CAN-Message. Du verwendest die Diagnose Schnittstelle nach OBD2. Der CAN-Bus ist die interne Übertragungsart der Steuergeräte im Auto.
Könnte z.B. auch K-Line sein. Das übernimmt aber der ELM327 für Dich.
Wenn Du expliziete CAN-Bus Nachrichten sehen möchtes, kannst Du ja mal den Befehl "ATMA" ausprobieren 😛

Trekki1990 Themenstarter:in
503 Beiträge seit 2008
vor 3 Jahren

Hallo Trib,

danke für deine ausführliche Nachricht!
In der Zwischenzeit (vor deinem Post) habe ich ein wenig rumprobiert.

Als ich das Thread.Sleep eingebaut hab, wurden die Daten nicht mehr zerstückelt und ich kann die einzelnen Einträge als items in einer Liste speichern wo man dann das Analysieren anfangen könnte:


Thread.Sleep(500); 
            var serialPort = (SerialPort)sender;

            int buffSize = 4096;
            byte[] bff = new byte[buffSize];
            string data = string.Empty;
            data += System.Text.Encoding.Default.GetString(bff, 0, serialPort.Read(bff, 0, buffSize));

            data_list = data.Split(new char[] { '\r' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string d in data_list)
            {
                if (txt_filter.Text != "")
                {
                    if (d.Contains(txt_filter.Text)) txt_receive.AppendText(d + "\n");
                }
                else
                {
                    txt_receive.AppendText(d + "\n");
                }
            }
            txt_receive.SelectionStart = txt_receive.Text.Length;
            txt_receive.ScrollToCaret();
        }

Habe jetzt nach deinem Post die einzelnen AT Kommandos mal ausprobiert.
Ich habe jetzt alles ausgeschaltet, also AT x0 und erhalte diese Nachrichten wie im hier zu sehen. Woher kommen denn immer noch diese 0:, 1:, 2: usw.?


15:59:53 - Befehl: 2101
7F2112
016
0:6101FFE00000
01E
0:6101000003FF
03D
0:6101FFFFFFFF
033
0:6101FFFFFBC0
1:09215A06061103
1:0C38004299D538
2:00000000E37534
1:BB172426480300
1:00023202320204
2:0000000A002502
3:04200000000000
2:040F530E0E0E0E
2:00007D003501BC
3:3D041939D0FFD0
3:05C04100000000
3:0E0E0E000FCC44
4:FF610000000000
4:00000000600000
4:CC010000900002
5:000001F4000001
5:3BEC00023AA900
6:A5000000000000
6:00D4300000CCD4
7:05BD0000000000
7:008371D60D0188
8:0000000003E800
>


Die PID 2101 - 2105 werden dazu genutzt um die einzelnen Batteriezellenspannungen auszulesen (sollte halt mir nur als Beispiel dienen um die Thematik grundsätzlich zu verstehen). Als Testobjekt nutze ich meinen Hyundai IONIQ electric. Der kann nämlich die ganze Zeit ohne Probleme eingeschaltet in der Garage stehen. 😄

Das ATH1 hatte ich mit Absicht gesetzt, da ohne die Header nicht ersichtlich ist von welchem Steuergerät die Daten kommen, so viel mir das Filtern leichter.

Gruß
trekki

T
708 Beiträge seit 2008
vor 3 Jahren

Ihh, Thread.Sleep()!!!

Das klappt natürlich so weit, als dass der Buffer weiterhin gefüllt wird und die Wahrscheinlichkeit erhöht, dass dort nun ein abschließender Zeilenumbruch enthalten ist.
Aber ne Programmierung sollte nicht auf dem Prinzip "Hoffnung" basieren, daher lege ich Dir nochmal den Link auf das SerialPort Template nahe. 😃

Dein erster Befehl läuft auf einen Fehler:
7F2112
7f Bedeutet immer Fehler
21 Deine angefragte SID
12 "Sub Function Not Supported - Invalid Format"

Lag vielleicht an mir, denn ich bringe gerne mal SID 01 mit 12 durcheinander, da ich in erster Linie mit dem KWP2000 arbeite, dem Vorgänger.

Hattest Du vorher "ATMA" ausprobiert? Dann bekommst Du nämlich ungefragt alle CAN-Bus Nachrichten ungefiltert angezeigt. Daher ggf. die X:-Nachrichten.

Soweit ich weiß, werden andere Steuergeräte anders Adressiert. Und zwar in dem Teil der Nachricht, den Dir der ELM327 vorenthält.
Die echte Nachricht beginnt nämlich mit einem Format (0x80 / 0x81) und dann dem Adressaten, Sender und der Länge der folgenden Nachricht.
Diesen Adressaten kannst Du wiederum nur mit AT-Befehlen ändern.
Für mich scheint die SID 21 eher eine Herstellerspezifische ServiceID zu sein, damit sie einfach über die Diagnoseschnittstelle den Status der Batterien abgreifen können.

185 Beiträge seit 2005
vor 3 Jahren

Wenn du mal in Google ODB c# eingibst, findest du fertige Libs für die Kommunikation.

Trekki1990 Themenstarter:in
503 Beiträge seit 2008
vor 3 Jahren

@trib...

Ich bin jetzt schlauer. Ich habe das eben an dem gewünschten anderen Auto ausprobiert. Da funktioniert alles wie es soll!

Ich gebe ein: 01 5C (Öltemperatur °C)
und erhalte: 41 5C XX

Ich gebe ein: 01 2f (Tankinhalt %)
und erhalte: 41 2f XX

Ich gebe ein: 01 46 (Außentemperatur °C)
und erhalte: 41 46 XX

Die bei Hyundai müssen irgendwas anders machen... Keine Ahnung.

Da ich die Werte nicht im Millisekundenbereich abfragen will, reicht mir hier alle 10 Sekunden. Ich verbinde das einfach mit einem Timer. Für meine kleine "Quick and Dirty" Lösung reicht mir das.

Danke an alle, für den letzten Denkanstoß. 😉
Bzgl. Puffer werde ich mir dann noch mal ansehen.

VG
trekki

T
708 Beiträge seit 2008
vor 3 Jahren

Erstmal sehr schön, dass es klappt!

Die bei Hyundai müssen irgendwas anders machen... Keine Ahnung.

OBD2 ist ein Standard. Der ist verpflichtend für jedes in den USA oder Europa zugelassenes Auto.
In Europa mittlerweile sogar für Motorräder vorgeschrieben, aber ausgesetzt, da die Hersteller das nicht gebacken kriegen.

Im Wiki findet man eigentlich alle nötigen Infos:
OBD-II PIDs

Vielleicht nutzt der Hyundai ein anderes Protokoll, welches auch der ELM327 für Dich automatisch suchen kann:
http://www.obdtester.com/elm-usb-commands

Trekki1990 Themenstarter:in
503 Beiträge seit 2008
vor 3 Jahren

Zum Thema Hyundai: https://stackoverflow.com/questions/57306799/how-to-decode-obd-2-data-from-hyundai-ioniq-ev

Die Ausgabe aus meinem Tool passt wohl so. Nur kommt da als Rückgabe nicht "41" sondern die IDs der Steuergeräte "7EC" und dann die Nutzdaten.

Ich habe das Gefühl die Jungs bei Hyundai wollen nicht dass man "rumschnüffelt" und selbst irgendwas "baut" für den OBD Port.

Gruß
trekki