Überblick
Das OBD-Adapter-BRIDGE-Projekt liest Fahrzeugdaten über einen handelsüblichen BLE-OBD-Dongle aus und stellt sie gleichzeitig auf drei unabhängigen Kanälen bereit: OLED-Display, TCP/WLAN und LoRa-Funk. Smartphone, App oder Abo sind nicht erforderlich.
Das System besteht aus vier eigenständigen Modulen, die über gemeinsame Header-Dateien (Symlinks) konfiguriert werden. Alle Module sind über ein einheitliches Telnet-CLI steuerbar — entweder per USB-Serial oder per TCP auf Port 1234.
Voraussetzungen
Hardware
| Modul | Hardware | Chip | Hinweis |
|---|---|---|---|
| MOD-01 Bridge | Heltec WiFi LoRa 32 (V3) | ESP32-S3 + SX1262 | SSD1306 OLED onboard |
| MOD-02 Receiver | Heltec WiFi LoRa 32 (V3) | ESP32-S3 + SX1262 | Identische Hardware wie MOD-01 |
| MOD-03 GUI | PC / Linux / macOS / Windows | — | Python 3, tkinter |
| MOD-04 Simulator | ESP32 WROOM-32 + MCP2515 | ESP32 + TJA1050 | CAN-Transceiver TJA1050 |
| Dongle | BLE-OBD-II-Dongle | ELM327-kompatibel | UUID 0x18F0 / 0xFFE0 / 0xFFF0 … |
Software / Entwicklungsumgebung
- Arduino IDE ≥ 2.x mit ESP32-Boardpaket (espressif/arduino-esp32)
- Heltec ESP32 Boards-Bibliothek
- NimBLE-Arduino 1.4.x
- LoRaWan_APP / Heltec LoRa-Bibliothek (für SX1262)
- MCP_CAN-Bibliothek (nur für MOD-04)
- Python 3 mit tkinter (Standard, für MOD-03)
arduino-clifürobd_flash.sh(optional, aber empfohlen)
Quickstart
- Datei
wifi_config.him Projektroot mit eigenen WLAN-Daten befüllen (SSID, Passwort, AP-Credentials) - Bridge flashen:
obd_identify.sherkennt den Port automatisch, dannobd_flash.sh bridge - Heltec starten, OLED zeigt Bootsequenz — Bridge verbindet sich ins WLAN oder öffnet eigenen AP (
192.168.4.1) - Mit
nc <IP> 1234verbinden undlive oneingeben — Fahrzeugdaten werden live ausgegeben - Python-GUI starten:
python3 obd_gui2.py --bridge <IP> - Optionaler LoRa-Receiver: Receiver-Firmware flashen, zweites Heltec-Board starten
demo-Befehl im CLI) oder
MOD-04 Simulator verwenden. Der Simulator antwortet auf alle OBD-Anfragen des Dongles via CAN-Bus.
Systemaufbau
Alle Verbindungen laufen über offene Protokolle auf Standard-Hardware. Der Simulator ermöglicht vollständige Tests ohne laufendes Fahrzeug.
Gestrichelt: optionaler Test-Pfad — MOD-04 (Simulator + MCP2515) ersetzt das Fahrzeug via CAN-Bus.
Kommunikationskanäle
| Kanal | Protokoll | Port / Frequenz | Richtung |
|---|---|---|---|
| BLE | NimBLE 1.4.x (GATT Client) | 2,4 GHz | Dongle → Bridge |
| TCP | Rohdaten-Stream + CLI | 1234 | Bridge/Receiver → PC/GUI |
| HTTP | Einfacher JSON-/HTML-Server | 80 | Bridge/Receiver → Browser |
| LoRa | SX1262, Extended-Pakete (TLV) | 868 MHz, SF7 | Bridge → Receiver |
| Serial | UART CLI (identisch zu TCP) | 115200 Bd | PC ↔ µC (USB) |
| CAN | ISO 15765-4, 500 kBit/s | — | Simulator → OBD-Dongle |
Symlinks & Shared Headers
Drei Konfigurationsdateien sind als Symlinks realisiert, damit alle Module
stets denselben Stand haben. Es gibt einen einzigen Änderungspunkt:
die echten Dateien liegen in src/obd_bridge/ bzw. src/wifi_config.h.
| Symlink-Pfad | Ziel | Inhalt |
|---|---|---|
| obd_lora_receiver/wifi_config.h | ../wifi_config.h | SSID, Passwort, AP-Credentials, TCP-Port |
| obd_simulator/wifi_config.h | ../wifi_config.h | SSID, Passwort, AP-Credentials, TCP-Port |
| obd_bridge/wifi_config.h | ../wifi_config.h | SSID, Passwort, AP-Credentials, TCP-Port |
| obd_lora_receiver/pid_registry.h | ../obd_bridge/pid_registry.h | PID-Enum, Tabelle, Prioritäten, Scaling |
| obd_lora_receiver/pid_registry.cpp | ../obd_bridge/pid_registry.cpp | PID-Implementierung (43 Einträge) |
| obd_lora_receiver/http_server.h | ../obd_bridge/http_server.h | HTTP-Server-Implementierung |
tar bleiben Symlinks automatisch erhalten.
ZIP-Tools lösen Symlinks in der Regel auf — daher immer tar -czf projekt.tgz ... verwenden.
wifi_config.h — Struktur
// wifi_config.h — gemeinsam für Bridge, Receiver, Simulator #define WIFI_SSID "MeinHeimnetz" #define WIFI_PASS "meinPasswort" #define AP_SSID "obdBT-Bridge" #define AP_PASS "obd12345" #define TCP_PORT 1234 #define HTTP_PORT 80
PID-Registry
Die Datei pid_registry.h/.cpp definiert alle 43 OBD-PIDs zentral.
Bridge und Receiver teilen sich dieselbe Tabelle über Symlinks — ein Sync-Fehler
zwischen den µCs ist damit strukturell ausgeschlossen.
Jeder Eintrag enthält: OBD-Modus und PID-Byte, Anzeigename, Einheit, Scaling-Faktor (×1 / ×10 / ×100), Priorität (HIGH / MED / LOW) und Gruppe (Motor, Kraftstoff, Abgas, Zündung, Fehler, Info). Der Blind-Scan beim Start testet automatisch, welche PIDs das angeschlossene Fahrzeug unterstützt.
MOD-01 · obd_bridge
Die Bridge ist das Kernmodul. Sie verbindet sich per BLE mit dem OBD-Dongle, liest alle verfügbaren PIDs in einem Prioritätspoller aus und stellt die Daten gleichzeitig auf OLED, TCP und LoRa bereit. Das Polling läuft dauerhaft und unabhängig von aktiven TCP-Verbindungen.
BLE-Client
Die Bridge scannt eigenständig nach BLE-OBD-Dongles. Beim Start durchsucht sie alle in Reichweite befindlichen Geräte nach bekannten Service-UUIDs:
| UUID | Profil |
|---|---|
| 0x18F0 | Standard BLE OBD (häufigster Typ) |
| 0xFFE0 | Alternativer OBD-Service |
| 0xFFF0 | ELM327-kompatibel Typ 2 |
| 0xAE30 | Herstellerspezifisch Typ A |
| 0xAE3A | Herstellerspezifisch Typ B |
Bei Verbindungsverlust startet automatisch ein erneuter Scan. Ein Hardware-Watchdog
(60 s Timeout, esp_task_wdt) verhindert, dass die Bridge dauerhaft hängt.
TCP & HTTP
Die Bridge öffnet nach dem WLAN-Connect einen TCP-Server auf Port 1234.
Mehrere gleichzeitige Clients sind möglich. Das Protokoll ist Klartext (UTF-8):
Zeilenpräfixe BSC (Basis-PIDs) und EXT (erweiterte PIDs)
kennzeichnen die Datenzeilen.
Auf Port 80 läuft ein minimaler HTTP-Server, der eine JSON-Statusseite und eine
einfache HTML-Übersicht liefert. Konfiguriert über http_server.h.
WiFi-Verhalten: Die Bridge versucht sich zuerst ins konfigurierte Heimnetz (STA) einzubuchen.
Schlägt das nach dem Timeout fehl, öffnet sie automatisch einen eigenen Access-Point
(192.168.4.1).
LoRa TX
Die Bridge sendet ausschließlich Extended-Pakete (Typ 0x03, TLV-Format)
mit allen aktiven PIDs. Das Sendeintervall wird dynamisch aus der tatsächlichen
Airtime berechnet (Semtech-Formel), sodass der 1%-Duty-Cycle für DE/EU
(868 MHz, SRD-Band) zu keiner Zeit überschritten wird.
Zusätzlich wird beim Start ein Config-Paket (Typ 0x02) gesendet,
das dem Receiver die aktuellen LoRa-Parameter mitteilt (SF, Region, Sendeleistung).
OLED-Display
Das SSD1306-Display (128×64 Pixel, I²C) zeigt bis zu 5 konfigurierbare Seiten.
Ein Button steuert kurzen Druck (Seite blättern) und langen Druck (aktuelle Seite fixieren).
Die Helligkeit ist per CLI-Befehl dim 0..255 regelbar;
dim 0 versetzt das Display in den Sleep-Modus (SSD1306 Display-Off).
Jede Seite zeigt unten links ein Status-Cluster
(B/R · ST/AP/-- · OBD/SIM/---) und rechts unten den Page-Indicator (Punkte).
Fehlerzustände blinken als !.
Die Punkte unten rechts zeigen die aktuelle Seite (ausgefüllt).
Das Status-Cluster unten links: B=Bridge / R=Receiver ·
ST=WLAN-Client / AP=Access Point / --=kein WLAN ·
OBD=Fahrzeugdaten / SIM=Simulation / ---=keine Daten.
! blinkt bei Fehler.
CLI-Befehle (Bridge)
| Befehl | Wirkung |
|---|---|
| System | |
| help / ? | Alle Befehle auflisten |
| info Alias: status, s | BLE-, WLAN-, LoRa-, OBD-Status, Akku, Statistik |
| version Alias: ver | Build-Zeitstempel und Sketch-Mtime ausgeben |
| debug | Debug-Ausgaben auf Serial/TCP ein-/ausschalten |
| demo | Demo-Modus ein-/ausschalten (simulierte PIDs) |
| reboot Alias: restart | µC neu starten |
| BLE | |
| scan | Neuen BLE-Scan starten, Gerät wählen |
| disco Alias: disconnect | BLE-Verbindung trennen, neuer Scan |
| OBD / PIDs | |
| pid list | Alle PIDs mit Gruppe, Priorität, Aktivstatus anzeigen |
| pid status | Gruppenübersicht + LoRa-Paketgröße + Duty-Cycle |
| pid scan | Capability-Scan (0100–0160) neu starten |
| pid enable <grp/id/all> | PID(s) aktivieren · Gruppen: motor kraftstoff abgas zuendung fehler info all |
| pid disable <grp/id/all> | PID(s) deaktivieren |
| pid blind on/off | Blindscan: PIDs ohne Cap-Bit-Bestätigung abfragen |
| pid interval | LoRa-Sendeintervall aus aktueller Paketgröße neu berechnen |
| pid profile fast | Nur Kern-PIDs: RPM, Speed, Temp, MAP, Last, TPS, IAT, Spannung |
| pid profile full | Alle unterstützten PIDs aktivieren |
| pid profile custom | Intervall neu berechnen ohne Profilwechsel |
| DTC Fehlercodes | |
| dtc Alias: readdtc, dtc read | Fehlercodes auslesen (Mode 03) |
| cleardtc Alias: dtc clear | Fehlerspeicher löschen (Mode 04) |
| Polling | |
| poll <ms> | OBD-Polling-Intervall setzen (100–5000 ms) |
| poll status | Aktuelles Intervall anzeigen |
| Live-Stream (TCP) | |
| live Alias: l | Live-Ausgabe auf TCP umschalten |
| live on / off | Live-Ausgabe explizit ein-/ausschalten |
| Display (OLED) | |
| next / prev | Display-Page weiterschalten |
| page N | Page 1–5 direkt anspringen |
| pagetime N | Auto-Cycle: alle N Sekunden weiterschalten (0 = aus) |
| pagelay R S | Page-0 Schriftgröße für RPM (R) und Speed (S), je 1–3 |
| dim N | OLED-Helligkeit (0 = aus, 255 = max) |
| Beeper (lokal) | |
| beep on / off | Piezo-Beeper Dauerton ein/aus (GPIO 48) |
| beep ack | Doppelpiep (Bestätigung) |
| LoRa CTRL → Receiver | |
| ctrl beep on / off / ack | Receiver-Beeper per LoRa steuern |
| ctrl beep pulse D N | N Pieps à D×10 ms an Receiver senden |
| ctrl gpio PIN | Receiver-GPIO lesen (Antwort per LoRa) |
| ctrl gpio PIN 0/1 | Receiver-GPIO setzen |
| ctrl ping | Receiver anpingen |
| GPIO (lokal) | |
| gpio PIN | Lokalen GPIO-Pin lesen |
| gpio PIN 0/1 | Lokalen GPIO-Pin setzen |
| LoRa | |
| lora status | Frequenz, SF, Power, Intervall, TX-Zähler anzeigen |
| lora sf7 … sf12 | Spreading Factor setzen (beide Seiten müssen gleich sein) |
| lora de / eu / int | Region: DE/EU = 868 MHz 14 dBm · INT = 915 MHz 20 dBm |
| lora pw<N> | Sendeleistung 2–22 dBm (z. B. lora pw14) |
MOD-02 · obd_lora_receiver
Der Receiver empfängt die LoRa-Pakete der Bridge, dekodiert den TLV-Payload und stellt die Werte auf seinem eigenen OLED sowie per TCP auf Port 1234 bereit. Er benötigt keine direkte Verbindung zum Fahrzeug oder zum Dongle.
LoRa RX & Paket-Dekodierung
Der Receiver empfängt Extended-Pakete (Typ 0x03) und Config-Pakete
(Typ 0x02). Extended-Pakete werden per TLV dekodiert — jeder Tag
entspricht einem PID-Index aus der gemeinsamen pid_registry.
Eine Sequenznummer ermöglicht die Zählung verlorener Pakete.
Das OLED zeigt 5 Seiten; Seite 4 scrollt durch alle empfangenen Extended-PIDs.
Der Paket-Modus ist per CLI umschaltbar: pkt auto (Standard),
pkt basic oder pkt extended.
WiFi-Verhalten: STA ins Heimnetz → bei Fehler automatisch STA in den AP der Bridge
(192.168.4.1).
CLI-Befehle (Receiver)
| Befehl | Wirkung |
|---|---|
| System | |
| status Alias: s, info | LoRa-RSSI, SNR, Paketzähler, WLAN-Status, Akku |
| version Alias: ver | Build-Zeitstempel ausgeben |
| wifi | WLAN-Verbindungsinfo anzeigen |
| lora | LoRa-Parameter anzeigen (SF, Frequenz, RSSI) |
| reboot Alias: r | µC neu starten |
| PIDs | |
| pid list Alias: pid | Alle empfangenen PID-Werte mit Einheit anzeigen |
| Paketfilter | |
| pkt auto | Basic- und Extended-Pakete anzeigen (Standard) |
| pkt basic | Nur Basic-Pakete (Typ 0x01) anzeigen |
| pkt extended | Nur Extended-Pakete (Typ 0x03) anzeigen |
| Live-Stream (TCP) | |
| live Alias: l | Live-Ausgabe auf TCP umschalten |
| live on / off | Live-Ausgabe explizit ein-/ausschalten |
| Display (OLED) | |
| next / prev | Display-Page weiterschalten |
| page N | Page 1–5 direkt anspringen |
| pagetime N | Auto-Cycle: alle N Sekunden weiterschalten (0 = aus) |
| pagelay R S | Page-0 Schriftgröße für RPM (R) und Speed (S), je 1–3 |
| dim N | OLED-Helligkeit (0 = aus, 255 = max) |
| Beeper (lokal) | |
| beep on / off | Lokalen Piezo-Beeper dauerhaft ein/aus |
| beep ack | Lokaler Doppelpiep (Bestätigung) |
| beep rx on / off | Quittungs-Piep bei jedem empfangenen LoRa-Paket |
| LoRa CTRL → Bridge | |
| ctrl beep on / off / ack | Bridge-Beeper per LoRa-Rückkanal steuern |
| ctrl beep pulse D N | N Pieps à D×10 ms an Bridge senden |
| ctrl gpio PIN | Bridge-GPIO lesen (Antwort kommt per LoRa zurück) |
| ctrl gpio PIN 0/1 | Bridge-GPIO setzen |
| ctrl ping | Bridge anpingen |
| GPIO (lokal) | |
| gpio PIN | Lokalen GPIO-Pin lesen |
| gpio PIN 0/1 | Lokalen GPIO-Pin setzen |
MOD-03 · obd_gui2.py
Das Python-Dashboard verbindet sich per TCP mit der Bridge oder dem Receiver und zeigt die Fahrzeugdaten als animierte Rundinstrumente (Gauges) und Balkenanzeigen an. Es läuft auf jedem Desktop-System mit Python 3 und tkinter.
Starten
# Direkt mit Bridge verbinden (RSSI-Balken ausgeblendet) python3 obd_gui2.py --bridge <IP-der-Bridge> # Mit Receiver verbinden (LoRa-RSSI-Balken aktiv) python3 obd_gui2.py <IP-des-Receivers> # Style beim Start wählen (1=F1, 2=Sci-Fi, 3=Aviation, 4=Luxury) python3 obd_gui2.py --bridge <IP> --style 2
Styles & Tastenkürzel
| Taste | Style | Beschreibung |
|---|---|---|
| 1 | F1 | Dunkles Carbon-Design, rote Akzente |
| 2 | Sci-Fi | Dunkles Blau/Cyan-Design |
| 3 | Aviation | Grau/Amber, Cockpit-Style |
| 4 | Luxury | Hell, Holzton, klassisch |
| L | — | CSV-Logging ein-/ausschalten (Sentinel-gefiltert) |
| Q / Esc | — | Programm beenden |
Im --bridge-Modus wird der LoRa-RSSI-Balken ausgeblendet, da die Verbindung
direkt zur Bridge läuft. CSV-Logging schreibt alle empfangenen Werte mit Zeitstempel
in eine Datei; Sentinel-Werte (fehlerhafte Antworten des Dongles) werden
automatisch herausgefiltert.
MOD-04 · obd_simulator
Der Simulator emuliert ein Fahrzeug-ECU via CAN-Bus nach ISO 15765-4. Er antwortet auf OBD-II-Anfragen des Dongles, sodass die komplette Toolchain ohne laufendes Fahrzeug getestet werden kann.
Ab dem Boot sendet der Simulator automatisch BSC- und EXT-Live-Pakete mit realistisch simulierten Fahrdynamikwerten (Drehzahl, Geschwindigkeit, Temperatur usw.). Über ein Web-Interface können alle Werte manuell gesetzt werden.
Unterstützte OBD-Modi
| Mode | Funktion | PIDs / Details |
|---|---|---|
| 01 | Live-Daten | 19 PIDs (RPM, Speed, Temp, Last, MAP, TPS, O2, …) |
| 03 | DTC-Fehlercodes lesen | Konfigurierbare DTCs |
| 04 | DTC-Fehlercodes löschen | Setzt MIL-Status zurück |
| 09 | Fahrzeuginfo | VIN, Kalibrier-ID, ECU-Name |
CLI-Befehle (Simulator)
| Befehl | Wirkung |
|---|---|
| status | CAN-Status, WLAN, aktuelle Simulationswerte |
| live on / off | Automatische Live-Pakete ein-/ausschalten |
| verbose on / off | Detailliertes CAN-Logging ein-/ausschalten |
| set rpm N | Drehzahl manuell setzen (0–8000) |
| set speed N | Geschwindigkeit manuell setzen (0–250 km/h) |
| set temp N | Kühlmitteltemperatur manuell setzen |
| reboot | µC neu starten |
Hardware-Verdrahtung
MOD-01 · Bridge (Heltec WiFi LoRa 32 V3)
Der Heltec LoRa V3 hat SX1262, OLED und LoRa-Antenne bereits onboard. Externe Verdrahtung beschränkt sich auf den Beeper.
| GPIO | Funktion | Richtung | Hinweis |
|---|---|---|---|
| 0 | BTN (BOOT-Taste) | IN | Onboard, INPUT_PULLUP — kurz: Seite blättern, lang: fixieren |
| 1 | BAT_ADC | IN | Akkuspannung (ADC1_CH0), intern |
| 9 | LORA_SCK | OUT | SX1262 SPI, intern |
| 10 | LORA_MOSI | OUT | SX1262 SPI, intern |
| 11 | LORA_MISO | IN | SX1262 SPI, intern |
| 12 | LORA_RST | OUT | SX1262 Reset, intern |
| 13 | LORA_BUSY | IN | SX1262 Busy, intern |
| 14 | LORA_IRQ (DIO1) | IN | SX1262 Interrupt, intern |
| 8 | LORA_CS | OUT | SX1262 Chip-Select, intern |
| 17 | OLED_SDA | I²C | SSD1306, intern |
| 18 | OLED_SCL | I²C | SSD1306, intern |
| 21 | OLED_RST | OUT | SSD1306 Reset, intern |
| 36 | VEXT | OUT | Externe 3,3V-Versorgung (OLED), LOW = an |
| 48 | STATIC_BEEP | OUT | Extern: Piezo-Beeper direkt an GPIO 48 und GND |
MOD-02 · LoRa-Receiver (Heltec WiFi LoRa 32 V3)
Identische Hardware wie MOD-01, identische Pin-Belegung. Kein Beeper. Kein externer Anschluss nötig.
MOD-04 · Simulator (ESP32 WROOM-32 + MCP2515)
Der MCP2515 CAN-Controller wird per SPI angebunden. Der TJA1050 CAN-Transceiver ist auf dem MCP2515-Modul (HW-184) bereits integriert.
| ESP32 GPIO | MCP2515 Pin | Funktion |
|---|---|---|
| 18 | SCK | SPI Clock |
| 19 | SO (MISO) | SPI Master In |
| 23 | SI (MOSI) | SPI Master Out |
| 5 | CS | Chip-Select (CAN_CS_PIN) |
| 4 | INT | Interrupt (CAN_INT_PIN) |
| 3,3V | VCC | Versorgung MCP2515-Modul |
| GND | GND | Masse |
OBD-II-Stecker beim Test ohne Fahrzeug
Der BLE-OBD-Dongle wird direkt am Simulator-Board betrieben. Es werden nur die Pins belegt die für CAN-OBD (ISO 15765-4) benötigt werden.
Für den Testaufbau empfiehlt sich ein 3D-gedruckter OBD-II-Stecker (Buchse): Thingiverse Thing 6350647 von TA1GI, lizenziert unter CC BY-SA 4.0. Die Kabel werden direkt an die gewünschten Pins angelötet, Zugentlastung über die Trägerplatte.
| OBD-II Pin | Belegung | Quelle |
|---|---|---|
| 4 | Chassis GND | GND des Simulator-Netzteils |
| 16 | +12V (Batterie) | 12V Netzteil oder Labornetzteil |
| 6 | CAN-High | CANH des TJA1050 (auf MCP2515-Modul) |
| 14 | CAN-Low | CANL des TJA1050 (auf MCP2515-Modul) |
| alle anderen | — | nicht belegt |
CAN-Bus Diagnose
Bei CAN ERROR vom Dongle oder ausbleibenden OBD-Antworten systematisch vorgehen:
Schritt 1 — Simulator-Hardware prüfen (Loopback-Test)
Im Simulator per Serial oder Telnet:
can test loopback
Erwartete Ausgabe: [CAN-TEST] Loopback: OK — MCP2515, SPI und TX/RX-Puffer funktionieren.
Schlägt dieser Test fehl, liegt das Problem am Simulator-Board selbst (SPI-Verdrahtung, Stromversorgung, Quarz).
Schritt 2 — Spannungen messen (Multimeter)
CAN-Bus im Ruhezustand (kein aktiver Traffic) hat definierte Ruhepegel:
| Messung | Sollwert | Abweichung bedeutet |
|---|---|---|
| CAN-H gegen GND | ~2.5 V | 0 V = Leitung unterbrochen oder kurzgeschlossen gegen GND |
| CAN-L gegen GND | ~2.5 V | 5 V = Leitung kurzgeschlossen gegen VCC |
| CAN-H gegen CAN-L | ~0 V | >0.5 V = Bus aktiv oder Terminierung fehlt |
Schritt 3 — Häufige Fehler
| Symptom | Ursache | Lösung |
|---|---|---|
| CAN ERROR, Loopback OK | CAN-H/L zwischen Dongle und MCP2515 vertauscht | Stecker prüfen, Spannung an beiden Enden messen |
| CAN ERROR, Loopback OK | Abschlusswiderstand fehlt | 120Ω-Jumper am MCP2515-Modul prüfen |
| CAN ERROR, Loopback OK | GND nicht durchverbunden | GND Dongle ↔ MCP2515 ↔ Netzteil prüfen |
| CAN ERROR, Loopback FEHLER | MCP2515 nicht erreichbar | SPI-Pins, CS-Pin, 3.3V/5V, Quarz 8MHz prüfen |
| Dongle verbindet nicht per BLE | Kein CAN-Bus → Dongle bleibt in Fehler-LED | Erst CAN-Verbindung herstellen, dann BLE-Verbindung kommt automatisch |
| ATSP6 schlägt fehl | Falsches CAN-Protokoll | Simulator läuft auf ISO 15765-4 500kbit/s (ATSP6) — Dongle-Einstellung prüfen |
Taster-Belegung
| Modul | Taster | GPIO | Kurzdruck | Langer Druck (>1s) |
|---|---|---|---|---|
| Bridge | BOOT (onboard) | 0 | Display-Seite blättern | Aktuelle Seite dauerhaft fixieren |
| Receiver | BOOT (onboard) | 0 | Display-Seite blättern | Aktuelle Seite dauerhaft fixieren |
| Simulator | — | — | kein Taster implementiert | — |
Freie GPIOs (Bridge & Receiver)
Folgende GPIOs des Heltec LoRa V3 sind im aktuellen Code nicht belegt und für Erweiterungen verfügbar:
| GPIO | Hinweis |
|---|---|
| 2, 3 | Frei, ADC-fähig |
| 4, 5, 6, 7 | Frei, digitale I/O |
| 15, 16 | Frei — Hinweis: Boot-Strapping-Pins, mit Vorsicht verwenden |
| 19, 20 | Frei (USB D-/D+, nur verwenden wenn kein USB-CDC genutzt wird) |
| 26, 33, 34, 35 | Frei, digitale I/O |
| 37, 38 | Frei, ADC-fähig |
| 45, 46, 47 | Frei — Hinweis: Strapping-Pins, beim Boot beachten |
Flash-Workflow
Das Projekt enthält zwei Shell-Skripte, die den Flash-Prozess automatisieren:
obd_identify.sh erkennt die angeschlossenen Module und
obd_flash.sh kompiliert und flasht.
obd_identify.sh
Das Skript liest die USB-Gerätebeschreibungen aus und vergleicht sie mit bekannten Heltec- und ESP32-Signaturen. Es gibt für jedes erkannte Modul den fertigen Flash-Befehl aus — inklusive korrektem Port.
# Alle angeschlossenen Module anzeigen ./obd_identify.sh # Beispielausgabe: [PORT] /dev/ttyUSB0 → Heltec LoRa V3 (Bridge) [PORT] /dev/ttyUSB1 → Heltec LoRa V3 (Receiver / Bridge?) [CMD] obd_flash.sh bridge --port /dev/ttyUSB0
obd_flash.sh
Kompiliert das jeweilige Modul mit arduino-cli und flasht es.
Der Build-Zeitstempel wird als Compiler-Define ins Binary eingebettet
(BUILD_TIME, SKETCH_MTIME) und ist per version-Befehl abrufbar.
# Bridge flashen (Port automatisch erkannt) ./obd_flash.sh bridge # Bridge mit explizitem Port ./obd_flash.sh bridge --port bridge=/dev/ttyUSB0 # Receiver flashen ./obd_flash.sh receiver # Simulator flashen ./obd_flash.sh simulator # Remote-Flash: Kompilierung auf PC1, Flash über SSH auf PC2 ./obd_flash.sh bridge --ssh user@pc2
PID-Tabelle (alle 43)
Die vollständige Tabelle aus pid_registry.h/.cpp.
Scale: ×1 = Ganzzahl, ×10 = eine Dezimalstelle, ×100 = zwei Dezimalstellen (TLV-Scaling).
| IDX | OBD-ID | Name | Einheit | Scale | Gruppe | Prio |
|---|---|---|---|---|---|---|
| 00 | 010C | RPM | 1/min | ×1 | Motor | HIGH |
| 01 | 010D | Speed | km/h | ×1 | Motor | HIGH |
| 02 | 0105 | Kuehlmittel | °C | ×10 | Motor | HIGH |
| 03 | 010F | Ansaugluft | °C | ×10 | Motor | MED |
| 04 | 010B | MAP | kPa | ×10 | Motor | HIGH |
| 05 | 0104 | Motorlast | % | ×100 | Motor | HIGH |
| 06 | 0111 | TPS | % | ×100 | Motor | MED |
| 08 | 012F | Tankfuellstand | % | ×100 | Kraftstoff | LOW |
| 09 | 015E | Verbrauch | L/h | ×100 | Kraftstoff | MED |
| 10 | 0110 | MAF | g/s | ×100 | Kraftstoff | MED |
| 11 | 0133 | Baro | kPa | ×10 | Kraftstoff | LOW |
| 12 | 0143 | AbsTPS | % | ×100 | Kraftstoff | MED |
| 13 | 0145 | RelTPS | % | ×100 | Kraftstoff | MED |
| 14 | 0149 | Gaspedal | % | ×100 | Kraftstoff | MED |
| 15 | 014C | Schubventil | % | ×100 | Kraftstoff | MED |
| 18 | 0114 | O2_B1S1_V | V | ×100 | Abgas | MED |
| 19 | 0115 | O2_B1S2_V | V | ×100 | Abgas | MED |
| 20 | 0116 | O2_B1S3_V | V | ×100 | Abgas | LOW |
| 21 | 0117 | O2_B1S4_V | V | ×100 | Abgas | LOW |
| 22 | 0124 | O2_B1S1_WB | λ | ×100 | Abgas | MED |
| 23 | 0125 | O2_B1S2_WB | λ | ×100 | Abgas | MED |
| 24 | 0144 | Lambda | λ | ×100 | Abgas | MED |
| 25 | 0146 | Umgebungsluft | °C | ×10 | Abgas | LOW |
| 26 | 014B | Kruemmerdruck | kPa | ×10 | Abgas | MED |
| 27 | 010E | Zuendwinkel | KW | ×10 | Zündung | MED |
| 28 | 0142 | ECU_Spannung | V | ×100 | Zündung | MED |
| 29 | 015C | Oeltemp | °C | ×10 | Zündung | LOW |
| 30 | 015D | Einspritzdruck | kPa | ×100 | Zündung | MED |
| 31 | 0167 | Kuehlmittel2 | °C | ×10 | Zündung | LOW |
| 33 | 0101 | MIL_Status | — | ×1 | Fehler | HIGH |
| 34 | 0121 | Dist_MIL_an | km | ×1 | Fehler | LOW |
| 35 | 0130 | Warmlaufzyklen | — | ×1 | Fehler | LOW |
| 36 | 0131 | Dist_Reset | km | ×1 | Fehler | LOW |
| 37 | 014D | Zeit_MIL_an | min | ×1 | Fehler | LOW |
| 38 | 014E | Zeit_Reset | min | ×1 | Fehler | LOW |
| 39 | 0151 | Kraftstofftyp | — | ×1 | Info | LOW |
| 40 | 0902 | VIN | — | — | Info | LOW |
| 41 | 0904 | KalibID | — | — | Info | LOW |
| 42 | 090A | ECU_Name | — | — | Info | LOW |
TCP-Protokoll
Alle Daten werden als UTF-8-Klartext auf Port 1234 übertragen.
Der Stream enthält abwechselnd BSC- und EXT-Zeilen, getrennt durch \n.
# BSC-Zeile (Basis-PIDs, hohe Priorität) BSC RPM:2847 SPD:92 T:91 Ld:38.4 MAP:102 V:13.82 TX#1247 # EXT-Zeile (erweiterte PIDs) EXT Ansaugluft:34 TPS:22.35 Gaspedal:28.62 Zuendwinkel:12.0 Lambda:1.000 Oeltemp:82 # Status-/Info-Zeile (kein festes Format) [INFO] BLE connected: OBD-DONGLE-BT RSSI:-58
CLI-Eingaben werden auf derselben TCP-Verbindung gesendet und mit \n abgeschlossen.
IAC-Bytes (Telnet-Steuersequenzen) werden automatisch herausgefiltert —
die Verbindung ist damit vollständig telnet-kompatibel.
LoRa-Paketformat
Alle LoRa-Pakete beginnen mit einem 1-Byte-Typ-Feld. Aktuell verwendet werden
Typ 0x02 (Config) und Typ 0x03 (Extended / TLV).
Extended-Paket (Typ 0x03)
Byte 0: 0x03 Pakettyp Extended Byte 1: SeqNr Sequenznummer (uint8, rollover) Byte 2: N Anzahl TLV-Einträge # TLV-Eintrag (wiederholt N-mal): Byte k+0: Tag PID-Index (aus pid_registry, 0–42) Byte k+1: Len Länge Value (1 oder 2 Bytes) Byte k+2: Value[Len] Skalierter Wert (int16, Big-Endian)
Der tatsächliche Wert ergibt sich durch Division mit dem Scale-Faktor aus der
PID-Registry: Wert = Value / Scale.
Config-Paket (Typ 0x02)
Byte 0: 0x02 Pakettyp Config Byte 1: SF Spreading Factor (7–12) Byte 2: Region 0x01 = EU868, 0x02 = US915 Byte 3: Power Sendeleistung in dBm
Verzeichnisstruktur & Deployment
Das Archiv OBD.tgz entpackt sich direkt in das Webserver-Verzeichnis.
Nach dem Entpacken nach / liegt die Website unter /OBD/,
die Quelldateien unter /OBD/src/.
Verzeichnisstruktur nach dem Entpacken
/OBD/ ├── index.shtml # Einstiegspunkt (SSI-Includes) ├── includes/ # Website-Bausteine (nav, hero, story, …) ├── images/ # Fotos, Screenshots, friese.png, friese.jpg ├── favicon.ico # Favicon (Friese, 16×16 + 32×32) ├── favicon.png # Favicon als PNG (32×32) ├── doku.html # Diese Dokumentation ├── .htaccess # Apache SSI + Kompression ├── LICENSE # GPL v3 (englisch, rechtsverbindlich) ├── LICENSE.de # GPL v3 (deutsch, nicht rechtsverbindlich) ├── OBD.tgz # Komplettarchiv (hier ablegen für Download-Link) └── src/ ├── wifi_config.h # Gemeinsame Konfiguration (SSID, AP, TCP-Port) ├── obd_flash.sh # Flash-Script (erkennt src/-Struktur automatisch) ├── obd_identify.sh # Modul-Erkennung an USB-Ports ├── obd_gui2.py # Python-Dashboard ├── obd_bridge/ # Bridge-Firmware + echte Header-Dateien ├── obd_lora_receiver/ # Receiver-Firmware + Symlinks auf obd_bridge/ └── obd_simulator/ # Simulator-Firmware + Symlink auf wifi_config.h
Deployment auf Apache
# 1. Archiv entpacken cd / tar -xzf OBD.tgz # 2. Rechte setzen bash /OBD/src/fix_permissions.sh # 3. Apache mod_include aktivieren a2enmod include systemctl restart apache2 # 4. VHost: AllowOverride All (damit .htaccess greift) # 5. OBD.tgz in /OBD/ ablegen (für den Download-Link auf der Seite) cp OBD.tgz /OBD/ # Aufruf: https://deinedomain.de/OBD/
Rechte setzen (fix_permissions.sh)
Das Script src/fix_permissions.sh setzt alle Rechte kollisionsfrei für den Webserver:
Verzeichnisse 755, Dateien 644, Shell- und Python-Scripts 755.
cd / sudo bash /OBD/src/fix_permissions.sh
Nginx (Alternative)
# nginx.conf — SSI aktivieren
location /OBD/ {
ssi on;
ssi_silent_errors on;
}
Lokaler Test mit Docker
docker run -p 8080:80 \
-v $(pwd)/OBD:/usr/local/apache2/htdocs/OBD \
httpd:alpine
# Aufruf: http://localhost:8080/OBD/index.shtml
Website-Features
Die Website enthält mehrere interaktive Elemente, die clientseitig per JavaScript realisiert sind — kein externes Framework, kein CDN.
Animierte Rundinstrumente (Hero-Bereich)
Im Hero-Bereich laufen 6 Canvas-Rundinstrumente mit simulierten OBD-Werten: Drehzahl, Speed, Kühlmitteltemperatur, Motorlast, MAP und ECU-Spannung. Jedes Instrument zeigt eine vollständige Rundinstrumenten-Skala mit Skalenstrichen, Beschriftung, farbigem Warnbereich und einem feinen beweglichen Zeiger. Der Zeiger wechselt bei Grenzwertüberschreitung auf Orange bzw. Rot. Die Simulation läuft dauerhaft im Hintergrund — ohne Server, ohne WebSocket.
Lightbox
Alle Bilder auf der Seite — Fotos, Screenshots und der Friese — sind klickbar
und öffnen eine Lightbox in voller Größe. Klick auf den Hintergrund oder
Escape schließt sie wieder. Implementiert als einzelner
<div id="lb-overlay"> in head.html,
gesteuert per lbOpen(src, event).
Favicon
Der Friese dient als Favicon (favicon.ico mit 16×16 und 32×32 px,
zusätzlich favicon.png). Beide Dateien liegen direkt im
/OBD/-Verzeichnis. Die <link>-Tags stehen in
includes/head.html mit relativem Pfad ohne führendes ./
— damit funktioniert die Auflösung korrekt durch SSI.
SSI-Struktur
index.shtml besteht ausschließlich aus
<!--#include virtual="./includes/..."-->-Direktiven.
Jeder Seitenbereich ist eine eigene Datei in includes/ —
Änderungen an einzelnen Bereichen erfordern kein Anfassen von index.shtml.
Lizenz
Alle Quelltexte dieses Projekts stehen unter der GNU General Public License v3 (GPL v3). Das bedeutet: freie Nutzung, Veränderung und Weitergabe — aber Derivate müssen ebenfalls unter GPL v3 veröffentlicht werden. Closed-Source-Derivate sind nicht zulässig.
Die originalen Copyright-Hinweise (Copyright (C) 2026 Ralf Burger)
müssen in allen Kopien und abgeleiteten Werken erhalten bleiben.
LICENSE (englisch, rechtsverbindlich)
und LICENSE.de (deutsche Übersetzung, nicht rechtsverbindlich) im Projektroot.
Online: gnu.org/licenses/gpl-3.0
ralf@RalfBurger.com · Systemintegration Ralf Burger · Roenskenstr. 37 · 46562 Voerde