Nachdem das Reengineering und die nahtlose Einbindung der Kompressorkühlbox im letzten Teil meines MultivanPi-Projekts so hervorragend funktioniert hat, war die Motivation groß. Wenn sich eine Kühlbox auslesen und fernsteuern lässt, dann muss das auch mit dem Herzstück der Stromversorgung klappen: Der PowerQueen LiFePO4 Batterie.
Das Ziel war klar definiert: Der aktuelle Ladezustand (SoC) und die Zelltemperatur der Batterie sollen prominent im Dashboard auftauchen, um die bestehenden, hochpräzisen Victron-Daten sinnvoll zu ergänzen. Was anfangs nach einer einfachen Bluetooth-Verbindung aussah, entpuppte sich als spannende Detektivarbeit durch rohe Datenströme, springende Werte und ein raffiniertes Temperatur-Management.
PowerQueen nutzt, wie viele andere Hersteller auch, ein Batterie-Management-System (BMS) von JBD. Eine offizielle Schnittstelle (API) für Entwickler gibt es nicht. Das BMS kommuniziert mit der originalen Smartphone-App über eine sogenannte UART-over-BLE Verbindung – im Grunde ein unsichtbares serielles Kabel über Bluetooth.
Um zu verstehen, was die Batterie sendet, habe ich einen „Scanner“ in Python geschrieben. Dieser schickt einen spezifischen Ping-Befehl (00 00 04 01 13 55 aa 17) an die Batterie und listet die 101 Bytes lange Antwort tabellarisch auf. Durch den direkten Vergleich der Byte-Werte mit der Anzeige auf dem Smartphone ließen sich die Koordinaten für die von mir gewünschten Daten exakt einkreisen.
Spannung: Byte 2-3
Strom: Byte 4-5
Ladezustand (SoC): Byte 82-83
Temperaturen: Byte 42, 45 und 47
Tipp für Nachbauer: Viele Batterie-Apps berechnen die geschätzte restliche Lade- oder Entladezeit intern auf dem Smartphone, basierend auf der übertragenen Restkapazität und dem Momentanstrom. Das BMS selbst überträgt keine fertige Zeitschätzung.
Die Tücken von Bluetooth: Fragmentierung
Der erste Versuch mit dem Skript lieferte zwar Daten, aber die Werte sprangen teilweise wild umher. Aus 22 Grad wurden plötzlich 68 Grad, der Ladezustand stimmte nicht mehr. Die Ursache hierfür ist ein klassisches Bluetooth-Phänomen: die Fragmentierung.
Das BMS sendet die über 100 Bytes nicht am Stück, sondern zerhackt sie in kleine 20-Byte-Häppchen. Geht auch nur ein einziges dieser Pakete in der Luft verloren, verschieben sich alle nachfolgenden Bytes. Python liest dann plötzlich den Stromwert dort aus, wo eigentlich die Temperatur stehen sollte.
Die Lösung war ein „Header-Lock“. Das Skript sammelt nun die ankommenden Daten in einem Puffer und sucht exakt nach der 7-Byte-Signatur der Batterie (00 00 65 01 93 55 aa). Erst wenn dieser Fingerabdruck gefunden und das Paket vollständig ist, werden die Daten ausgewertet. Danach wird der Puffer sauber abgeschnitten. Seitdem stehen die Werte felsenfest.
Das Temperatur-Rätsel und der Kälteschutz
Eine weitere Hürde war die Temperaturanzeige. Das Python-Skript lieferte oft den Wert 0 für einen der Sensoren, während die App konstant 8 Grad anzeigte. Auch hier zeigte das Reengineering interessante Einblicke in die Arbeitsweise des BMS.
LiFePO4-Batterien haben eine physikalische Besonderheit: Sie dürfen bei Kälte (unter 0 Grad Celsius) nicht geladen werden, da sonst Lithium-Plating entsteht, was die Batterie sofort irreparabel zerstört. Daher interessiert sich das BMS nicht für eine durchschnittliche Wohlfühltemperatur, sondern ausschließlich für die kälteste Stelle im Akkupack.
Ich habe herausgefunden, dass die Smartphone-App zwei simple, aber extrem wichtige Regeln anwendet, die ich in meinem Python-Backend exakt nachgebaut habe:
Um Strom und den A/D-Wandler zu schonen, misst das BMS nicht in jeder Sekunde. In den Pausen sendet es einfach eine 0. Diese Nullwerte müssen vom Skript ignoriert werden.
Sind gültige Werte vorhanden (z.B. 8 Grad und 10 Grad), wird niemals der Durchschnitt gebildet. Das Skript muss immer den niedrigsten Wert auslesen und speichern.
Variablen in die config.json auslagern
Wer eigene Skripte schreibt, tappt schnell in die Falle, gerätespezifische Daten wie MAC-Adressen fest in den Code zu schreiben. Das rächt sich spätestens dann, wenn ein Gerät ausgetauscht wird oder jemand anderes das Projekt auf seiner eigenen Hardware nachbauen möchte.
Um das System so anpassungsfähig wie möglich zu halten, habe ich die gerätespezifischen PowerQueen-Daten komplett in die zentrale config.json ausgelagert, wo auch schon die Variablen für die Victron-Geräte, für die Kühlbox und z.B. der Radstand und die Spurweite hinterlegt ist. Das Python-Skript holt sich beim Start dynamisch die MAC-Adresse, die spezifische UART-UUID und den Ping-Befehl für das Batterie-Management-System.
Für Nachbauer bedeutet das: Es muss nicht im eigentlichen Programmcode gesucht und editiert werden. Einfach die eigene MAC-Adresse in der JSON-Datei eintragen, und das Skript läuft sofort ohne tiefere Programmierkenntnisse.
Als der Code lief und in den zentralen Hintergrunddienst (victron_service.py) des MultivanPi integriert wurde, trat das nächste Problem auf: Die Victron-Komponenten verloren schlagartig die Verbindung. Der Raspberry Pi htte einen „Bluetooth-Stau“.
Victron-Geräte werden passiv abgehört (Scanner-Modus), während die Kühlbox und die PowerQueen aktiv angesprochen werden (Client-Modus). Wenn das Python-Skript versucht, beides in derselben Millisekunde zu starten, verschluckt sich der BlueZ-Treiber des Raspberry Pi und der Scanner stürzt lautlos ab.
Die Staging-Lösung: Ich habe einen gestaffelten Boot-Prozess eingeführt. Zuerst verbindet sich die Kühlbox, 15 Sekunden später baut die PowerQueen ihre Verbindung auf, und erst nach weiteren 10 Sekunden Wartezeit darf der Victron-Scanner seine Arbeit aufnehmen. So stören sich die unterschiedlichen Bluetooth-Prozesse nicht.
Das Frontend: Eine Welle aus CSS
Nachdem das Backend die Datenpunkte pq_soc und pq_temp zuverlässig über die lokale API bereitstellt, habe ich die LiFePO4 in die index.html eingebaut. Der bisherige Refresh-Button war sowieso als Platzhalter gedacht und musste nun dem neuen, eigenständigen „LiFePO4“-Button weichen.
Das ist der Teil der originalen App, die ich nachgebaut habe.
Anstatt einfach nur nackte Zahlen anzuzeigen, habe ich das Design an die Smartphone-App angelehnt. Der Ladezustand wird durch eine flüssig animierte Welle dargestellt. Das Besondere daran: Es kommen keinerlei externe Bilder oder Videos zum Einsatz. Die gesamte Animation, das langsame schaukeln der Welle und die dynamische Anpassung der Füllhöhe (je nach Akkustand) sind rein in CSS geschrieben. Fällt die Ladung unter 40 Prozent, verfärbt sich die Welle zudem automatisch gelb, unter 20 Prozent wird sie rot.
Damit ist die PowerQueen LiFePO4 vollständig, stabil und optisch ansprechend in das Dashboard des MultivanPi integriert. Ein weiterer großer Schritt in meiner Philosophie: Alle wichtigen Daten zentral im Blick haben, ohne eine App am Smartphone zu öffnen.
5 März 2026
MultivanPi Teil 8: Die PowerQueen LiFePO4 Batterie in das Dashboard einbinden
Nachdem das Reengineering und die nahtlose Einbindung der Kompressorkühlbox im letzten Teil meines MultivanPi-Projekts so hervorragend funktioniert hat, war die Motivation groß. Wenn sich eine Kühlbox auslesen und fernsteuern lässt, dann muss das auch mit dem Herzstück der Stromversorgung klappen: Der PowerQueen LiFePO4 Batterie.
Das Ziel war klar definiert: Der aktuelle Ladezustand (SoC) und die Zelltemperatur der Batterie sollen prominent im Dashboard auftauchen, um die bestehenden, hochpräzisen Victron-Daten sinnvoll zu ergänzen. Was anfangs nach einer einfachen Bluetooth-Verbindung aussah, entpuppte sich als spannende Detektivarbeit durch rohe Datenströme, springende Werte und ein raffiniertes Temperatur-Management.
Inhalt
Das Protokoll entschlüsseln: Der ‚Scanner‘
PowerQueen nutzt, wie viele andere Hersteller auch, ein Batterie-Management-System (BMS) von JBD. Eine offizielle Schnittstelle (API) für Entwickler gibt es nicht. Das BMS kommuniziert mit der originalen Smartphone-App über eine sogenannte UART-over-BLE Verbindung – im Grunde ein unsichtbares serielles Kabel über Bluetooth.
Um zu verstehen, was die Batterie sendet, habe ich einen „Scanner“ in Python geschrieben. Dieser schickt einen spezifischen Ping-Befehl (
00 00 04 01 13 55 aa 17) an die Batterie und listet die 101 Bytes lange Antwort tabellarisch auf. Durch den direkten Vergleich der Byte-Werte mit der Anzeige auf dem Smartphone ließen sich die Koordinaten für die von mir gewünschten Daten exakt einkreisen.Die Tücken von Bluetooth: Fragmentierung
Der erste Versuch mit dem Skript lieferte zwar Daten, aber die Werte sprangen teilweise wild umher. Aus 22 Grad wurden plötzlich 68 Grad, der Ladezustand stimmte nicht mehr. Die Ursache hierfür ist ein klassisches Bluetooth-Phänomen: die Fragmentierung.
Das BMS sendet die über 100 Bytes nicht am Stück, sondern zerhackt sie in kleine 20-Byte-Häppchen. Geht auch nur ein einziges dieser Pakete in der Luft verloren, verschieben sich alle nachfolgenden Bytes. Python liest dann plötzlich den Stromwert dort aus, wo eigentlich die Temperatur stehen sollte.
Die Lösung war ein „Header-Lock“. Das Skript sammelt nun die ankommenden Daten in einem Puffer und sucht exakt nach der 7-Byte-Signatur der Batterie (
00 00 65 01 93 55 aa). Erst wenn dieser Fingerabdruck gefunden und das Paket vollständig ist, werden die Daten ausgewertet. Danach wird der Puffer sauber abgeschnitten. Seitdem stehen die Werte felsenfest.Das Temperatur-Rätsel und der Kälteschutz
Eine weitere Hürde war die Temperaturanzeige. Das Python-Skript lieferte oft den Wert 0 für einen der Sensoren, während die App konstant 8 Grad anzeigte. Auch hier zeigte das Reengineering interessante Einblicke in die Arbeitsweise des BMS.
LiFePO4-Batterien haben eine physikalische Besonderheit: Sie dürfen bei Kälte (unter 0 Grad Celsius) nicht geladen werden, da sonst Lithium-Plating entsteht, was die Batterie sofort irreparabel zerstört. Daher interessiert sich das BMS nicht für eine durchschnittliche Wohlfühltemperatur, sondern ausschließlich für die kälteste Stelle im Akkupack.
Ich habe herausgefunden, dass die Smartphone-App zwei simple, aber extrem wichtige Regeln anwendet, die ich in meinem Python-Backend exakt nachgebaut habe:
Variablen in die config.json auslagern
Wer eigene Skripte schreibt, tappt schnell in die Falle, gerätespezifische Daten wie MAC-Adressen fest in den Code zu schreiben. Das rächt sich spätestens dann, wenn ein Gerät ausgetauscht wird oder jemand anderes das Projekt auf seiner eigenen Hardware nachbauen möchte.
Um das System so anpassungsfähig wie möglich zu halten, habe ich die gerätespezifischen PowerQueen-Daten komplett in die zentrale
config.jsonausgelagert, wo auch schon die Variablen für die Victron-Geräte, für die Kühlbox und z.B. der Radstand und die Spurweite hinterlegt ist. Das Python-Skript holt sich beim Start dynamisch die MAC-Adresse, die spezifische UART-UUID und den Ping-Befehl für das Batterie-Management-System.Für Nachbauer bedeutet das: Es muss nicht im eigentlichen Programmcode gesucht und editiert werden. Einfach die eigene MAC-Adresse in der JSON-Datei eintragen, und das Skript läuft sofort ohne tiefere Programmierkenntnisse.
Kollision auf dem Raspberry Pi vermeiden
Als der Code lief und in den zentralen Hintergrunddienst (
victron_service.py) des MultivanPi integriert wurde, trat das nächste Problem auf: Die Victron-Komponenten verloren schlagartig die Verbindung. Der Raspberry Pi htte einen „Bluetooth-Stau“.Victron-Geräte werden passiv abgehört (Scanner-Modus), während die Kühlbox und die PowerQueen aktiv angesprochen werden (Client-Modus). Wenn das Python-Skript versucht, beides in derselben Millisekunde zu starten, verschluckt sich der BlueZ-Treiber des Raspberry Pi und der Scanner stürzt lautlos ab.
Das Frontend: Eine Welle aus CSS
Nachdem das Backend die Datenpunkte
pq_socundpq_tempzuverlässig über die lokale API bereitstellt, habe ich die LiFePO4 in dieindex.htmleingebaut. Der bisherige Refresh-Button war sowieso als Platzhalter gedacht und musste nun dem neuen, eigenständigen „LiFePO4“-Button weichen.Anstatt einfach nur nackte Zahlen anzuzeigen, habe ich das Design an die Smartphone-App angelehnt. Der Ladezustand wird durch eine flüssig animierte Welle dargestellt. Das Besondere daran: Es kommen keinerlei externe Bilder oder Videos zum Einsatz. Die gesamte Animation, das langsame schaukeln der Welle und die dynamische Anpassung der Füllhöhe (je nach Akkustand) sind rein in CSS geschrieben. Fällt die Ladung unter 40 Prozent, verfärbt sich die Welle zudem automatisch gelb, unter 20 Prozent wird sie rot.
Damit ist die PowerQueen LiFePO4 vollständig, stabil und optisch ansprechend in das Dashboard des MultivanPi integriert. Ein weiterer großer Schritt in meiner Philosophie: Alle wichtigen Daten zentral im Blick haben, ohne eine App am Smartphone zu öffnen.