protocol: Protokoll

obd_diag.protocol.elm327

.. py:module:: obd_diag.protocol.elm327

Minimaler ELM327-Treiber: Befehl senden, Antwort bis zum Prompt lesen.

.. py:exception:: ForbiddenCommandError :module: obd_diag.protocol.elm327

Bases: :py:class:Exception

Ein Befehl außerhalb der Freigabeliste sollte gesendet werden.

Das ist ein Programmierfehler, kein Adapterproblem: bewusst kein ElmError, damit ihn kein Aufrufer als „Angabe nicht verfügbar“ abfängt. Gesendet wurde nichts.

.. py:function:: is_read_only(cmd) :module: obd_diag.protocol.elm327

True für Adapter-Befehle und lesende OBD-Anfragen der Freigabeliste.

.. py:exception:: ElmError :module: obd_diag.protocol.elm327

Bases: :py:class:Exception

Der Adapter hat einen Befehl abgelehnt oder keine Daten geliefert.

.. py:exception:: NoDataError :module: obd_diag.protocol.elm327

Bases: :py:class:~obd_diag.protocol.elm327.ElmError

NO DATA: kein Steuergerät hat geantwortet (z. B. keine Fehlercodes).

.. py:exception:: UnknownCommandError :module: obd_diag.protocol.elm327

Bases: :py:class:~obd_diag.protocol.elm327.ElmError

?: der Adapter kennt den Befehl nicht oder unterstützt ihn nicht.

.. py:exception:: NoConnectionError :module: obd_diag.protocol.elm327

Bases: :py:class:~obd_diag.protocol.elm327.ElmError

UNABLE TO CONNECT: der Adapter findet kein Steuergerät, mit dem er sprechen kann.

Laut Datenblatt (ELM327DSJ, „Error Messages and Alerts“) hat er dann alle Protokolle durchprobiert; meist ist die Zündung aus. Am ersten echten Auto (W177) war genau das die Ursache, HINT sagt dem Nutzer daher, was zu tun ist.

.. py:class:: ObdProtocol(number, name, inferred=None) :module: obd_diag.protocol.elm327

Bases: :py:class:object

Fahrzeugprotokoll laut ATDPN/ATDP.

obd_diag.protocol.frames

.. py:module:: obd_diag.protocol.frames

Zerlegt ELM327-Antworten (ohne Header) in Nachrichten, eine je Steuergerät.

Bei CAN setzt der Adapter mehrteilige ISO-TP-Nachrichten selbst zusammen und gibt sie so aus (ATH0, mit oder ohne Leerzeichen)::

   008
   0: 43 03 01 33 03 00
   1: 01 71 00 00 00 00 00

Die erste Zeile ist die Nutzdatenlänge in Bytes (hex), dann folgen die Frames mit fortlaufender Nummer 0 bis F (danach wieder 0). Der letzte Frame ist aufgefüllt und wird auf die Länge gekürzt. Alle anderen Zeilen sind je eine vollständige Nachricht; sie dürfen auch zwischen den Frames einer mehrteiligen Nachricht stehen (Einzel-Frame eines anderen Steuergeräts).

Senden zwei Steuergeräte gleichzeitig mehrteilige Nachrichten, mischt der ELM327 ohne Header deren Frames (Datenblatt ELM327DS, „Multiline Responses“, Beispiel 09 04); eine Zuordnung ist dann unmöglich. Das wird an Lücken in der Frame-Nummerierung und an unvollständigen Nachrichten erkannt und als FrameSequenceError (ein ValueError) gemeldet, statt falsche Daten zu liefern. Die Dienste wiederholen die Anfrage dann mit Headern (ATH1) und ordnen die Frames über die CAN-ID zu (protocol.headers).

.. py:exception:: FrameSequenceError :module: obd_diag.protocol.frames

Bases: :py:class:ValueError

Frames einer mehrteiligen Nachricht fehlen, sind vertauscht oder vermischt.

Ohne Header nicht zu unterscheiden: verlorene Frames oder gleichzeitige mehrteilige Antworten mehrerer Steuergeräte. Mit Headern (ATH1) lässt sich das klären.

.. py:function:: split_messages(response) :module: obd_diag.protocol.frames

Liefert die Nutzdaten jeder Nachricht in response.

Wirft ValueError bei Zeilen, die keine Hex-Daten sind, und FrameSequenceError (Unterklasse von ValueError) bei mehrteiligen Nachrichten mit fehlenden, vertauschten oder vermischten Frames.

obd_diag.protocol.headers

.. py:module:: obd_diag.protocol.headers

Antworten mit Headern (ATH1) zerlegen und je Steuergerät zusammensetzen.

Ohne Header kann der ELM327 gleichzeitige mehrteilige Antworten mehrerer Steuergeräte nicht trennen (Datenblatt ELM327DS, „Multiline Responses“, S. 45). Mit Headern trägt jede Zeile ihren Absender, und die Frames lassen sich je Absender nach ISO 15765-2 (ISO-TP) zusammensetzen. Formate einer Zeile (mit oder ohne Leerzeichen):

  • CAN 11 Bit: 7E8 10 0A 43 04 01 33 03 00: 3 Hex-Ziffern CAN-ID, dann das CAN-Datenfeld samt PCI-Byte(s).

  • CAN 29 Bit: 18 DA F1 10 06 41 00 BE 3F A8 13: 4 Bytes CAN-ID (OBD-Antworten 18 DA F1 <Quelle>), dann das Datenfeld.

  • J1850/ISO 9141/KWP: 48 6B 10 41 00 BE 3E B8 11 FA: 3 Header-Bytes (Priorität, Ziel, Quelle), Nutzdaten, 1 Prüfsummen-Byte (Datenblatt S. 44). Die Prüfsumme wird entfernt, aber nicht geprüft (J1850 nutzt eine CRC, ISO 9141/KWP eine Summe; der ELM327 verwirft fehlerhafte Nachrichten ohnehin bzw. meldet <DATA ERROR).

PCI-Byte (ISO 15765-2): 0L Einzel-Frame mit L Bytes; 1L LL erster Frame mit 12-Bit-Länge, dann 6 Bytes; 2N Folge-Frame Nummer N (1, 2, … F, 0, …) mit bis zu 7 Bytes; 3x Flusssteuerung (sendet nur der Tester, wird übersprungen). Füllbytes nach dem Ende einer Nachricht werden abgeschnitten.

Reihenfolge des Ergebnisses: nach Steuergeräte-Adresse (EcuMessage.ecu), bei gleicher Adresse in der Reihenfolge, in der die Nachrichten (ihr erster Frame) eingetroffen sind. Ohne Header gilt dagegen die Eingangsreihenfolge.

.. py:class:: HeaderFormat(*values) :module: obd_diag.protocol.headers

Bases: :py:class:~enum.StrEnum

.. py:class:: EcuMessage(ecu, header, data) :module: obd_diag.protocol.headers

Bases: :py:class:object

Eine vollständige Nachricht eines Steuergeräts.

.. py:function:: header_format(response, *, can=None) :module: obd_diag.protocol.headers

Format der ersten Zeile einer Antwort mit Headern; None, wenn unlesbar.

.. py:function:: parse_header_response(response, *, can=None) :module: obd_diag.protocol.headers

Zerlegt eine Antwort mit Headern in vollständige Nachrichten je Steuergerät.

can: True/False, wenn das Protokoll bekannt ist, None erkennt das Format je Zeile an der Form (siehe Modul-Doku). Wirft ValueError bei Zeilen ohne Hex-Daten, ungültigen PCI-Bytes, Folge-Frames ohne ersten Frame, Lücken in der Nummerierung und unvollständigen Nachrichten.

obd_diag.protocol.dtc_decode

.. py:module:: obd_diag.protocol.dtc_decode

Dekodierung von Fehlercodes (DTC) nach SAE J2012 aus Mode-03/07/0A-Antworten.

.. py:exception:: NegativeDtcResponse(mode, nrc) :module: obd_diag.protocol.dtc_decode

Bases: :py:class:ValueError

Ein Steuergerät hat die Anfrage abgelehnt oder noch nicht beantwortet.

Anders als „Mode nicht unterstützt“ heißt das nicht „keine Codes“: der Fehlerspeicher dieses Steuergeräts ist unbekannt (z. B. 21 beschäftigt, 22 Bedingungen nicht erfüllt, 78 Antwort folgt).

.. py:function:: decode_dtc(high, low) :module: obd_diag.protocol.dtc_decode

Zwei Rohbytes in einen Code wie P0133 umwandeln.

.. py:function:: parse_dtc_response(response, mode=3, *, can=False) :module: obd_diag.protocol.dtc_decode

Liest alle Codes aus einer Adapter-Antwort.

response ist der Hex-Text des ELM327 ohne Header, mit oder ohne Leerzeichen, eine Nachricht pro Steuergerät; mehrteilige CAN-Nachrichten (0: …/1: …) werden zusammengesetzt. Bei CAN (ISO 15765-4) folgt auf das Mode-Byte ein Zählbyte (SAE J1979); Bytes nach den gezählten Codes sind Füllbytes. Ältere Protokolle füllen stattdessen mit 00 00 auf. Negative Antworten (7F) einzelner Steuergeräte werden nur übersprungen, wenn sie „Mode nicht unterstützt“ heißen (UNSUPPORTED_NRCS); jede andere ergibt NegativeDtcResponse, damit eine Ablehnung nie als „keine Codes“ gelesen wird.

ValueError bei unerwarteten Antworten; FrameSequenceError (Unterklasse), wenn Frames mehrerer Steuergeräte vermischt sind (dann mit Headern neu lesen).

.. py:function:: parse_dtc_messages(messages, mode=3, *, can=False) :module: obd_diag.protocol.dtc_decode

Wie parse_dtc_response, aber für bereits zerlegte Nachrichten (je Steuergerät).

obd_diag.protocol.obd

.. py:module:: obd_diag.protocol.obd

OBD-II-Dienste über den ELM327.

Bis auf clear_dtcs (Mode 04) nur lesend.

.. py:function:: read_with_headers(elm, cmd, reason) :module: obd_diag.protocol.obd

Wiederholt die lesende Anfrage cmd mit Headern und zerlegt je Steuergerät.

Für Antworten, deren Frames sich ohne Header nicht zuordnen lassen (reason). Reihenfolge: nach Steuergeräte-Adresse, dann Eingang (siehe protocol.headers). Wirft ElmError, wenn auch das nicht gelingt oder diesmal keine Antwort kommt.

.. py:function:: read_dtcs(elm, mode, *, can, pending_timeout=5.0) :module: obd_diag.protocol.obd

Fehlercodes aller Steuergeräte für Mode 03, 07 oder 0A.

NO DATA heißt: kein Code gespeichert, also eine leere Liste. Sind Frames mehrerer Steuergeräte vermischt (ohne Header nicht zuzuordnen), wird die Anfrage einmal mit Headern (ATH1) wiederholt; die Codes kommen dann nach Steuergeräte-Adresse geordnet (z. B. 7E8 vor 7E9), sonst in Eingangsreihenfolge.

Meldet ein Steuergerät 7F <Mode> 78 (Antwort folgt) und steht die Antwort nicht schon dabei, wird ohne erneutes Senden bis pending_timeout Sekunden weitergelesen. Kommt sie nicht, oder lehnt ein Steuergerät aus anderem Grund ab als „Mode nicht unterstützt“, gibt es NegativeResponseError statt einer leeren Liste: der Fehlerspeicher ist dann unbekannt, nicht leer.

.. py:exception:: NegativeResponseError(mode, nrc) :module: obd_diag.protocol.obd

Bases: :py:class:~obd_diag.protocol.elm327.ElmError

Ein Steuergerät hat mit 7F <Mode> <NRC> abgelehnt.

.. py:function:: clear_dtcs(elm, *, pending_timeout=10.0) :module: obd_diag.protocol.obd

Mode 04: gespeicherte und ausstehende Codes, Freeze Frame und Readiness löschen.

Schreibender Befehl! Vorbedingungen prüfen und sichern muss der Aufrufer (siehe services.clear). Erfolg heißt: mindestens ein Steuergerät bestätigt mit 44 und keines lehnt ab. Wirft NegativeResponseError bei 7F 04 xx, NoDataError ohne Antwort und ElmError bei unerwarteter Antwort.

7F 04 78 (ISO 14229: Anfrage erhalten, Antwort folgt) ist keine Ablehnung: steht danach noch keine endgültige Antwort, wird ohne erneutes Senden weitergelesen, bis 44 oder 7F 04 xx kommt oder pending_timeout Sekunden (ab dem Senden) vergangen sind. Ohne Bestätigung bis dahin ElmError („nicht bestätigt“); ist bereits ein anderes Steuergerät bestätigt, gilt das Löschen als bestätigt (mit Log-Warnung). Mode 04 wird nie wiederholt.

.. py:function:: read_pid(elm, pid) :module: obd_diag.protocol.obd

Mode 01: Datenbytes des ersten Steuergeräts, das pid beantwortet.

None, wenn kein Steuergerät antwortet (NO DATA) oder alle ablehnen.

.. py:function:: decode_rpm(data) :module: obd_diag.protocol.obd

PID 0C: Motordrehzahl in 1/min, ((A*256)+B)/4.

.. py:function:: read_rpm(elm) :module: obd_diag.protocol.obd

Motordrehzahl in 1/min oder None, wenn sie nicht lesbar ist.

.. py:function:: read_rpms(elm) :module: obd_diag.protocol.obd

Motordrehzahl je Antwort auf 010C, in der Reihenfolge der Antworten.

Antworten mehrere Steuergeräte (z. B. Motor und Getriebe), steht jedes einzeln darin; None für eine Antwort, die keine gültige Drehzahl ist (Ablehnung, zu kurz, andere PID). Leer bei NO DATA.

.. py:class:: FreezeFrame(dtc=None, raw=, values=) :module: obd_diag.protocol.obd

Bases: :py:class:object

Momentaufnahme (Mode 02, Frame 00) beim Setzen eines Fehlercodes.

raw enthält die Antworten wie empfangen (Befehl -> Hex-Text), values die daraus dekodierten Werte. Unbeantwortete PIDs fehlen in beiden. Die Schlüssel von raw zeigen das verwendete Anfrageformat: 020C00 (SAE J1979, mit Frame-Nummer) oder 020C (ohne, Rückfallebene für manche Steuergeräte).

.. py:function:: read_freeze_frame(elm) :module: obd_diag.protocol.obd

Liest Frame 00 von Mode 02: auslösender Code und einige häufige Werte.

Angefragt wird nach SAE J1979 mit Frame-Nummer (02 <PID> 00). Antwortet das Fahrzeug auf 020200 mit NO DATA oder 7F 02 12 (Format nicht unterstützt), wird 0202 ohne Frame-Nummer versucht, wie es python-OBD sendet und manche Steuergeräte bzw. Adapter erwarten. Liefert das eine Antwort, folgt der ganze Freeze Frame in diesem Format; sonst (auch bei ? oder unbrauchbarer Antwort) bleibt es beim J1979-Format für die übrigen PIDs.

obd_diag.protocol.pids

.. py:module:: obd_diag.protocol.pids

Mode-01-PIDs für Live-Daten nach SAE J1979: Tabelle, Dekodierung, unterstützte PIDs.

Nur lesend: gesendet werden ausschließlich 01xx-Anfragen über Elm327.query.

Die Formeln folgen SAE J1979 (Anhang B, Service $01) in der Schreibweise der Wikipedia-Tabelle „OBD-II PIDs“: A, B, C, D sind die Datenbytes nach 41 <PID>. minimum/maximum sind der volle Wertebereich der Kodierung (alle Bits 0 bzw. 1), nicht der physikalisch plausible Bereich. Die Werte werden nicht gerundet; das ist Sache der Anzeige.

Eine PID kann mehrere Werte liefern (z. B. Lambdasonde: Spannung und Trimm); dann gibt es je Wert eine PidSpec mit derselben PID, und read_values fragt die PID nur einmal ab. decode bekommt immer die ersten size Datenbytes (also auch ein vorangehendes Statusbyte) und liefert None, wenn der Wert laut Antwort nicht gilt (Statusbit nicht gesetzt, Sonde nicht im Trimm).

Die Lambdasonden 14-1B, 24-2B und 34-3B heißen hier Sonde 1 bis 8 in der Reihenfolge der PIDs. Welche Bank und Position das ist, legt das Fahrzeug über PID 13 (2 Bänke zu je 4) oder 1D (4 Bänke zu je 2) fest; eine feste Zuordnung wie „B1S1“ wäre bei 1D falsch.

Die PIDs 68, 6C, 6D, 70 und 85 meldet der Mercedes A 180 d (W177) als unterstützt (Bitmasken 0160/0180 in tests/fixtures/traces/mercedes_w177), Antworten darauf gibt es aber noch keine vom Auto: am Fahrzeug noch nicht geprüft. Ihr Aufbau (Statusbyte A, welche Werte es gibt, dann die Werte) folgt J1979 in zwei unabhängigen Darstellungen, die übereinstimmen: der Signalliste von OBDb (github.com/OBDb/SAEJ1979, signalsets/v3/default.json) und der PID-Liste von DashLogic (dashlogic.com/docs/technical/obdii_pids). 68 gibt es auch in einer älteren, kürzeren Form mit zwei Sensoren (Bit 0 und 1, Bytes B und C); sie deckt sich mit den ersten beiden Sensoren der langen Form, Sensoren dahinter bleiben bei der kurzen Antwort None (zu wenige Bytes).

Bewusst nicht aufgenommen (offen):

  • PIDs mit Statusbyte, deren Aufbau in den frei zugänglichen Quellen nicht eindeutig ist oder die das Testauto nicht meldet: 69 (AGR), 6A, 6B, 6E, 6F, 71-79, 7A-7C (DPF), 7D-84, 86, 87, 89 und höher (außer A6).

  • Reine Status- und Bitfelder: der Regelstatus in Byte J von 70, 88 (SCR-Aufforderungssystem), 92 (Regelkreise der Kraftstoffanlage).

  • Bitfelder und Aufzählungen ohne Messwert: 01, 03, 12, 13, 1C, 1D, 1E, 41, 51, 65.

  • 53/54 (Absolut-/Relativdruck Tankentlüftung): Kodierung von 54 ist in den frei zugänglichen Quellen widersprüchlich (Zweierkomplement oder Offset).

.. py:class:: PidSpec(pid, key, label, unit, size, decode, minimum, maximum, number=None) :module: obd_diag.protocol.pids

Bases: :py:class:object

Ein Live-Wert: PID, Schlüssel, Bezeichnung, Einheit, Dekodierung.

label ist deutsch und darf {n} enthalten (Sonde, Stützpunkt), das number füllt; name ist die Bezeichnung in der eingestellten Sprache (obd_diag.i18n).

.. py:function:: pid_by_key(key) :module: obd_diag.protocol.pids

Der Wert zu key; KeyError mit verständlicher Meldung, wenn unbekannt.

.. py:function:: parse_supported(base, data) :module: obd_diag.protocol.pids

PIDs aus der Bitmaske einer Antwort auf 01<base> (base = 0x00, 0x20, …).

data sind die vier Datenbytes; Bit 7 des ersten Bytes steht für base + 1, Bit 0 des vierten für base + 0x20 (der nächste Block wird unterstützt). Weitere Bytes werden ignoriert. ValueError bei weniger als vier Bytes oder einer base, die kein Vielfaches von 0x20 zwischen 0x00 und 0xE0 ist.

.. py:function:: read_supported_pids(elm) :module: obd_diag.protocol.pids

Alle PIDs, die mindestens ein Steuergerät unterstützt (Vereinigung).

Fragt 0100 und folgt der Kette (0120, 0140 …), solange ein Steuergerät das Bit für den nächsten Block setzt, höchstens bis 01C0. NO DATA ergibt eine leere Menge (bzw. beendet die Kette). Jede Nachricht zählt einzeln; Antworten ohne 41 <base> oder mit weniger als vier Datenbytes (z. B. 7F 01 12) werden ignoriert. Die Block-PIDs selbst (0x20, 0x40, …) sind im Ergebnis enthalten. ElmError bei Adapterfehlern geht an den Aufrufer.

.. py:function:: read_values(elm, specs) :module: obd_diag.protocol.pids

Werte derselben PID mit einer Anfrage, je Schlüssel.

Jeder Wert kommt vom ersten Steuergerät, das ihn gültig meldet: eine Nachricht 41 <pid> mit mindestens spec.size Datenbytes (überzählige werden ignoriert), deren Dekodierung nicht None ist. None bei NO DATA, Ablehnung (7F 01 xx), zu kurzer Antwort oder ungültigem Wert; ElmError bei Adapterfehlern geht an den Aufrufer. Vermischen sich mehrteilige Antworten mehrerer Steuergeräte, wird die Anfrage einmal mit Headern (ATH1) wiederholt; gelingt auch das nicht, bleiben die Werte None (TransportError, wenn sich ATH0 nicht wiederherstellen lässt, geht durch). Verschiedene PIDs in specs sind ein Programmierfehler (ValueError), ebenso eine leere Liste.

.. py:function:: read_value(elm, spec) :module: obd_diag.protocol.pids

Ein Wert (siehe read_values).

obd_diag.protocol.vehicle_info

.. py:module:: obd_diag.protocol.vehicle_info

Mode 09 (Fahrzeuginformationen) je Steuergerät: Name, Kalibrierungs-IDs, CVN und Überwachungszähler (In-Use Performance Tracking). Die FIN (InfoType 02) liest services.vehicle.read_vin.

Aufbau der Antworten nach SAE J1979 / ISO 15031-5 (Übersicht in Wikipedia „OBD-II PIDs“, Service 09, abgerufen 2026-10-11; das Beispiel 09 04 im Datenblatt ELM327DSJ S. 43):

  • 49 00 AA BB CC DD: unterstützte InfoTypes 01 bis 20, Bit 7 von A = 01.

  • 49 04 NN + NN Kalibrierungs-IDs (CALID) zu je 16 ASCII-Zeichen, hinten mit 00 aufgefüllt.

  • 49 06 NN + NN Kalibrierungs-Prüfnummern (CVN) zu je 4 Bytes, als Hex angezeigt.

  • 49 08 NN bzw. 49 0B NN + NN Zähler zu je 2 Bytes (Otto- bzw. Dieselmotor): OBDCOND, IGNCNTR, dann je Monitor ein Paar „Test abgeschlossen“ / „Bedingungen erfüllt“ (SPARK_COUNTERS, COMPRESSION_COUNTERS).

  • 49 0A 01 + 20 ASCII-Zeichen Steuergerätename (ECM + 00, -, Langname).

NN ist bei CAN die Zahl der Datenelemente (ISO 15765-4). Bei den älteren Protokollen (J1850, ISO 9141, KWP) steht an dieser Stelle eine laufende Nummer, und jede Nachricht trägt 4 Datenbytes; die Daten werden dann nach Nummer aneinandergehängt.

Weil mehrere Steuergeräte antworten und ohne Header die Reihenfolge der Antworten wechselt (beim W177 bei jeder Anfrage anders), liest read_ecu_info alles mit Headern (ATH1) und ordnet nach Absender.

.. py:class:: PerformanceCounter(key, completed, conditions) :module: obd_diag.protocol.vehicle_info

Bases: :py:class:object

Ein Monitor im In-Use Performance Tracking: wie oft der Test abgeschlossen wurde und wie oft die Fahrbedingungen dafür gegeben waren.

.. py:property:: PerformanceCounter.name :module: obd_diag.protocol.vehicle_info :type: str

  Deutsche Bezeichnung (msgid); zur Anzeige ``tr(counter.name)``.

.. py:property:: PerformanceCounter.ratio :module: obd_diag.protocol.vehicle_info :type: float | None

  Überwachungsquote abgeschlossen / Bedingungen; ``None`` ohne Bedingungen.

.. py:class:: InUsePerformance(compression_ignition: bool, obd_conditions: int, ignition_cycles: int, counters: tuple[obd_diag.protocol.vehicle_info.PerformanceCounter, …] = ()) :module: obd_diag.protocol.vehicle_info

Bases: :py:class:object

.. py:class:: EcuInfo(ecu, name=None, calibration_ids=(), cvns=(), performance=None, supported=()) :module: obd_diag.protocol.vehicle_info

Bases: :py:class:object

Was ein Steuergerät über Mode 09 meldet; nicht gemeldete Angaben bleiben leer.

.. py:function:: parse_supported(message) :module: obd_diag.protocol.vehicle_info

InfoTypes aus 49 00 AA BB CC DD (Legacy mit Nummer davor); sonst None.

.. py:function:: parse_calibration_ids(messages) :module: obd_diag.protocol.vehicle_info

InfoType 04: Kalibrierungs-IDs eines Steuergeräts (leere übersprungen).

.. py:function:: parse_cvns(messages) :module: obd_diag.protocol.vehicle_info

InfoType 06: Kalibrierungs-Prüfnummern als 8 Hex-Ziffern (z. B. 1A2B3C4D).

.. py:function:: parse_ecu_name(messages) :module: obd_diag.protocol.vehicle_info

InfoType 0A: z. B. ECM-EngineControl; None bei leerem Namen.

.. py:function:: parse_performance(info_type, messages) :module: obd_diag.protocol.vehicle_info

InfoType 08 (Otto) oder 0B (Diesel): Überwachungszähler.

Gelesen werden OBDCOND, IGNCNTR und so viele Paare, wie gemeldet sind (J1979: 16, 18 oder 20 Werte, ältere Steuergeräte weniger); mehr Paare als bekannt oder eine ungerade Zahl von Werten sind ein ValueError.

.. py:function:: read_ecu_info(elm) :module: obd_diag.protocol.vehicle_info

Mode 09 je Steuergerät, sortiert nach Adresse; None ohne Mode-09-Antwort.

Fragt 0900 und dann jeden InfoType aus READ_INFO_TYPES, den mindestens ein Steuergerät als unterstützt meldet, alles in einem Block mit Headern (ATH1 … ATH0). Lehnt ein Steuergerät ab oder ist seine Antwort nicht normgerecht, fehlt nur diese Angabe (mit Log-Eintrag). ElmError, wenn schon 0900 unlesbar ist; TransportError bricht ab.

obd_diag.protocol.monitor_tests

.. py:module:: obd_diag.protocol.monitor_tests

Mode 06: Ergebnisse der On-Board-Überwachungstests mit Grenzwerten (nur CAN).

Aufbau nach SAE J1979 / ISO 15031-5 für ISO 15765-4 (CAN):

  • 06 00, 06 20 … 06 E0: unterstützte Monitor-IDs (OBDMID), Antwort 46 <MID> AA BB CC DD wie bei Mode 01 PID 00; das letzte Bit meldet den nächsten Bereich.

  • 06 <MID>: Antwort 46 und je Test 9 Bytes <MID> <TID> <UASID> <Wert 2> <Min 2> <Max 2>. Die Test-ID (TID) ist unter $80 genormt, ab $80 legt sie der Hersteller fest. Die UASID (Unit and Scaling ID) sagt, wie die drei Zahlen umzurechnen sind (UNITS): $01 bis $7F ohne Vorzeichen, $80 bis $FE mit Vorzeichen (Zweierkomplement). Bestanden ist ein Test, wenn Min ≤ Wert ≤ Max.

Die Namen der Monitor-IDs folgen J1979 Anhang D, die Umrechnungen Anhang E; beides gegengeprüft mit python-OBD (obd/commands.py Mode 06, obd/UnitsAndScaling.py), siehe tests/verification/test_monitor_tests_differential.py.

Ältere Protokolle (J1850, ISO 9141, KWP) haben für Mode 06 ein anderes, nicht genormt umrechenbares Format; read_test_results liefert dort None.

.. py:class:: Unit(scale, unit=‘‘, offset=0.0) :module: obd_diag.protocol.monitor_tests

Bases: :py:class:object

Umrechnung einer UASID: Wert = Rohwert · scale + offset.

.. py:function:: is_signed(uasid) :module: obd_diag.protocol.monitor_tests

UASID $80 bis $FF: Werte mit Vorzeichen (J1979 Anhang E).

.. py:function:: mid_name(mid) :module: obd_diag.protocol.monitor_tests

Bezeichnung einer Monitor-ID in der eingestellten Sprache.

.. py:function:: tid_name(tid) :module: obd_diag.protocol.monitor_tests

Bezeichnung einer Test-ID; ab $80 herstellerspezifisch.

.. py:class:: TestStatus(*values) :module: obd_diag.protocol.monitor_tests

Bases: :py:class:~enum.StrEnum

.. py:class:: TestResult(ecu, mid, tid, uasid, value_raw, min_raw, max_raw) :module: obd_diag.protocol.monitor_tests

Bases: :py:class:object

Ein Testergebnis aus Mode 06, roh gespeichert und bei Bedarf umgerechnet.

.. py:property:: TestResult.value :module: obd_diag.protocol.monitor_tests :type: float | None

  Umgerechneter Wert; ``None`` bei unbekannter UASID.

.. py:property:: TestResult.decimals :module: obd_diag.protocol.monitor_tests :type: int

  Sinnvolle Nachkommastellen zur Anzeige: so viele, wie die Auflösung hat.

.. py:property:: TestResult.status :module: obd_diag.protocol.monitor_tests :type: ~obd_diag.protocol.monitor_tests.TestStatus

  ``TestStatus``; verglichen wird mit den Rohwerten samt Vorzeichen, so geht das
  auch bei unbekannter UASID (die Umrechnung ist immer steigend).

.. py:function:: parse_supported(message, base) :module: obd_diag.protocol.monitor_tests

MIDs aus 46 <base> AA BB CC DD; None, wenn die Nachricht nicht passt.

.. py:function:: parse_results(message, mid) :module: obd_diag.protocol.monitor_tests

Testergebnisse aus 46 + Blöcken zu 9 Bytes; ValueError, wenn die Länge nicht aufgeht oder ein Block eine andere MID trägt als angefragt.

.. py:function:: read_test_results(elm) :module: obd_diag.protocol.monitor_tests

Mode 06 aller Steuergeräte, nach Absender, MID und Reihenfolge der Antwort.

None, wenn kein Steuergerät Mode 06 beantwortet oder das Protokoll kein CAN ist; eine leere Liste, wenn MIDs gemeldet sind, aber keine lesbaren Ergebnisse kommen. Alles läuft in einem Block mit Headern (ATH1 … ATH0), damit jedes Ergebnis seinem Steuergerät zugeordnet ist. Lehnt ein Steuergerät eine MID ab (7F 06 xx) oder ist eine Antwort nicht normgerecht, fehlt nur diese (mit Log-Eintrag). ElmError, wenn schon die unterstützten MIDs unlesbar sind.