forked from fritob/Camper-Monitor
* Demo-Modus-Abschnitt an den tatsächlichen Code angeglichen: Schalter in den Einstellungen (für lokal installierte Builds ohne Xcode) und Umgebungsvariable, beide dokumentiert. * Quelle für den Votronic-Solarregler ergänzt (syssi/esphome-votronic), eigener Abschnitt analog zu Kühlbox/WattCycle. * Persönlichen Pfad aus der Bauanleitung entfernt (open VanControl.xcodeproj statt eines lokalen Nutzerpfads); Beispiel-Bundle-ID im Demo-Modus-Befehl durch Platzhalter ersetzt. Zusätzlich: Symbolliste für Fahrzeugprofile auf Wunsch exakt auf die aus solar-integration zurückgesetzt (car, car.side, suv.side, truck.pickup.side, box.truck, bus), statt sie mit dev_watchs Liste zusammenzuführen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
595 lines
31 KiB
Markdown
595 lines
31 KiB
Markdown
# VanControl Pro
|
||
|
||
iOS-App, die per Bluetooth LE die Energieanlage im Wohnmobil ausliest:
|
||
|
||
| Gerät | Weg | Was ankommt |
|
||
|---|---|---|
|
||
| Victron Ladebooster (Orion-TR Smart / Orion XS) | Instant Readout im Advertisement | Ein-/Ausgangsspannung, beim XS auch Ströme und Ladeleistung, Zustand, Abschaltgrund |
|
||
| Victron Solarladeregler (SmartSolar MPPT) | Instant Readout im Advertisement | PV-Leistung, Batteriespannung, Ladestrom, Tagesertrag, Laststrom, Ladezustand (Bulk/Absorption/Float) |
|
||
| Batterie-BMS (Bulltron/Daly, WattCycle) | GATT-Verbindung, alle 5 s abgefragt | SoC, Spannung, Strom, Restkapazität, alle Einzelzellspannungen, Zelldifferenz, Temperaturen, Zyklen, MOSFET-Status |
|
||
| Kompressor-Kühlbox (Alpicool und Baugleiche) | GATT-Verbindung, lesen **und** stellen | Ist- und Solltemperatur je Zone, Betriebsart, Kompressorstatus, Bordspannung, Batterieanzeige |
|
||
| Neigungsmesser (VanAlign Pro) | GATT-Verbindung | Längs- und Querneigung als Libelle, Kalibrierung aus der App |
|
||
| Votronic-Solarladeregler (über eigene ESP32-Bridge) | GATT-Verbindung, im Takt abgefragt | PV-Leistung, PV-Spannung/-Strom, Batteriespannung, Reglertemperatur, Lade-/Reglerstatus |
|
||
|
||
Ein Victron SmartShunt/BMV wird ebenfalls unterstützt, falls später einer dazukommt.
|
||
|
||
Victron- und Votronic-Solarladeregler sind zwei getrennte Geräterollen, absichtlich
|
||
nicht zusammengelegt: Der Victron-Regler meldet sich passiv und verschlüsselt im
|
||
Advertisement, der Votronic-Regler hängt über eine eigene ESP32-Bridge (siehe
|
||
unten) an einer GATT-Verbindung – grundverschiedene Übertragungswege für
|
||
grundverschiedene Hardware. In Code und Einstellungen tragen beide deshalb
|
||
konsequent `victron`/`votronic` im Namen.
|
||
|
||
Der Neigungsmesser zeigt sich zusätzlich als **Live Activity** auf Sperrbildschirm,
|
||
Dynamic Island und – seit iOS 26 automatisch – im CarPlay-Dashboard, siehe
|
||
[Live Activity](#live-activity).
|
||
|
||
Dazu gehört eine **Apple-Watch-App**: Übersicht, Nivellierung samt
|
||
Ausrichtungs-Assistent mit Vibration und die Steuerung der Kühlbox am
|
||
Handgelenk. Den Neigungsmesser funkt die Uhr selbst an, alles Übrige kommt
|
||
über das iPhone – warum diese Aufteilung, steht unter
|
||
[Apple Watch](#apple-watch).
|
||
|
||
Die Firmware des Neigungsmessers liegt mit im Projekt, siehe
|
||
[firmware/vanalign](firmware/vanalign/README.md). Der Votronic-Solarregler hängt
|
||
an einer zweiten, unabhängigen ESP32-Bridge – Firmware und BLE-Dienst dafür
|
||
liegen als `firmware/vanalign/esp32_ble_solar.yaml` im selben Ordner.
|
||
|
||
## Bauen und installieren
|
||
|
||
```bash
|
||
open VanControl.xcodeproj
|
||
```
|
||
|
||
Dann in Xcode:
|
||
|
||
1. Bei allen vier Targets – `VanControl`, `VanControlWatch`,
|
||
`VanControlComplication`, `VanControlLiveActivityExtension` – unter
|
||
**Signing & Capabilities** dein Apple-Team auswählen. Die Bundle-IDs ggf.
|
||
anpassen, falls sie schon vergeben sind; die der Watch-App muss die des
|
||
iPhones mit angehängtem `.watchkitapp` bleiben, die der Komplikation
|
||
zusätzlich `.levelwidget`.
|
||
2. iPhone per Kabel anschließen, oben als Ziel wählen, ⌘R.
|
||
|
||
Das Schema `VanControl` baut die Watch-App mit und bettet sie ein. Dafür
|
||
muss die watchOS-Plattform in Xcode installiert sein – sonst bricht schon das
|
||
Übersetzen des Symbolkatalogs ab:
|
||
|
||
```bash
|
||
xcodebuild -downloadPlatform watchOS
|
||
```
|
||
|
||
Auf die Uhr kommt die App danach von selbst: Sie steckt in der iPhone-App und
|
||
taucht in der Watch-App des iPhones unter *Verfügbare Apps* auf.
|
||
|
||
Wichtig: **Der Simulator hat kein Bluetooth.** Die App startet dort, findet aber
|
||
nie ein Gerät. Zum Testen muss sie auf ein echtes iPhone.
|
||
|
||
Mit einem kostenlosen Apple-Account läuft die App 7 Tage und muss dann neu
|
||
installiert werden; mit einem bezahlten Developer-Account ein Jahr.
|
||
|
||
## Einrichtung in der App
|
||
|
||
### Victron-Geräte
|
||
|
||
Victron-Geräte senden ihre Messwerte etwa im Sekundentakt verschlüsselt im
|
||
BLE-Advertisement. Es wird nichts verbunden und nichts gestört – VictronConnect
|
||
kann parallel laufen. Dafür brauchst du pro Gerät einmalig den Schlüssel:
|
||
|
||
1. VictronConnect öffnen, Gerät anwählen
|
||
2. Zahnrad (Einstellungen) → ⋮ oben rechts → **Produkt-Info**
|
||
3. **Instant Readout** einschalten
|
||
4. **Verschlüsselungsdaten anzeigen** → der Schlüssel sind 32 Hex-Zeichen
|
||
|
||
In der App: **+** → Gerät aus der Liste wählen (Victron-Geräte werden erkannt
|
||
und die Art vorausgefüllt) → Schlüssel einfügen → Sichern.
|
||
|
||
Der Schlüssel liegt in der iOS-Keychain, nicht in den UserDefaults.
|
||
|
||
#### Wenn „Schlüssel passt nicht" erscheint
|
||
|
||
Victron sendet das erste Byte des Schlüssels unverschlüsselt mit, damit sich ein
|
||
falscher Schlüssel sofort erkennen lässt. Die Detailansicht des Geräts hat dafür
|
||
den Abschnitt **Diagnose**:
|
||
|
||
* *Gerät sendet als erstes Schlüsselbyte* – was das Gerät erwartet
|
||
* *Eingetragener Schlüssel beginnt mit* – was in der App steht
|
||
|
||
Stimmen die beiden nicht überein, gehört der Schlüssel zu einem anderen Gerät.
|
||
Bei mehreren Victron-Geräten ist das die mit Abstand häufigste Ursache: In
|
||
VictronConnect prüfen, dass wirklich dieses Gerät geöffnet war, als der
|
||
Schlüssel angezeigt wurde.
|
||
|
||
Die Diagnose zeigt außerdem Produkt-ID, Datensatztyp und die Rohdaten des
|
||
Advertisements – letztere lassen sich durch langes Antippen kopieren.
|
||
|
||
### Batterie / BMS
|
||
|
||
Kein Schlüssel nötig. Auswählen, Art auf **Batterie / BMS** stellen, Sichern.
|
||
Bulltron-Akkus melden sich meist als `DL-…`, WattCycle je nach Charge unter
|
||
eigenem Namen – findest du nichts, in der Geräteliste auf **Alle** umschalten.
|
||
|
||
Die App probiert drei Protokolle durch und übernimmt, was antwortet:
|
||
|
||
| Dialekt | Verbreitung |
|
||
|---|---|
|
||
| Daly klassisch (`A5`) | Bulltron und viele Daly-BMS |
|
||
| Daly Modbus (`D2`) | neuere Daly-Firmware |
|
||
| JBD / Xiaoxiang (`DD A5`) | viele LiFePO4-Akkus mit eigener App |
|
||
| WattCycle (`1E`/`7E`) | WattCycle-Bluetooth-Serie |
|
||
|
||
WattCycle-Akkus verlangen eine Besonderheit: vor der ersten Abfrage muss der
|
||
Text `HiLink` auf eine eigene Freischalt-Charakteristik (`FFFA`) geschrieben
|
||
werden, sonst bleiben sie auf jede Anfrage stumm. Die App macht das
|
||
automatisch, sobald ein Gerät diese Charakteristik anbietet. Modell,
|
||
Hersteller und Seriennummer liest sie einmalig mit aus und zeigt sie in der
|
||
Detailansicht unter *Gerät*.
|
||
|
||
Protokoll und Feldbelegung stammen aus
|
||
[frabnet/esphome-wattcycle-ble](https://github.com/frabnet/esphome-wattcycle-ble);
|
||
die Prüfsummen-Variante von dort ist gegen den klassischen Modbus-CRC
|
||
nachgerechnet, die Anfragerahmen sind byteweise in `run-tests.sh` abgesichert.
|
||
|
||
Auch die GATT-Charakteristiken werden gesucht statt vorausgesetzt, weil sich
|
||
die BLE-Module zwischen Herstellern und Chargen unterscheiden. Welches
|
||
Protokoll erkannt wurde, steht in der Detailansicht unter **Diagnose** –
|
||
zusammen mit der letzten Rohantwort.
|
||
|
||
Nur **eine** App gleichzeitig kann mit dem BMS verbunden sein. Wenn die
|
||
Hersteller-App offen ist, bekommt VanControl Pro keine Verbindung.
|
||
|
||
### Kühlbox
|
||
|
||
Die IceCube-Boxen von Plug-in Festivals sind umgelabelte Alpicool-Boxen –
|
||
erkennbar daran, dass der Hersteller die App „Alpicool T-Series“ nennt. Dasselbe
|
||
Protokoll sprechen auch BrassMonkey und Ocean Comfort.
|
||
|
||
Beim Einrichten Art auf **Kühlbox** stellen. Die App meldet sich beim Verbinden
|
||
selbst an; steht **APP** im Display der Box, verlangt sie dabei einen
|
||
Tastendruck am Gerät.
|
||
|
||
**Verbunden wird nur, während die Kühlbox geöffnet ist.** Jede Verbindung
|
||
meldet sich an ihrem Display an und stört, wer gerade davorsteht; und ihre Werte
|
||
ändern sich langsam. Auf der Übersicht steht deshalb nicht die gemessene
|
||
Innentemperatur, sondern der **zuletzt gestellte Stand** – Sollwert, Ein/Aus,
|
||
Eco oder Max, dazu wann er gestellt wurde. Der bleibt richtig, auch wenn er von
|
||
gestern ist: Ein Sollwert ändert sich nur, wenn jemand ihn ändert. Eine
|
||
Innentemperatur von gestern sähe dagegen aus wie eine von jetzt.
|
||
|
||
Sobald du die Box öffnest, verbindet die App sich und zeigt alles live; beim
|
||
Verlassen der Ansicht trennt sie wieder. Dasselbe gilt für die Uhr: Auch von
|
||
dort wird die Verbindung nur angefordert, solange die Kühlbox-Ansicht offen ist.
|
||
Ein Stellbefehl geht ebenfalls immer durch – ist die Box gerade nicht verbunden,
|
||
wird er gemerkt und geht raus, sobald sie antwortet.
|
||
|
||
BMS und Neigungsmesser bleiben dagegen dauerhaft verbunden. Sie stören dabei
|
||
nicht, und ihre Werte will man laufend sehen.
|
||
|
||
Neben den Messwerten gibt es hier als einzigem Gerät auch Bedienelemente:
|
||
Ein/Aus, Eco oder Max, Solltemperatur je Zone und die Bedienfeldsperre. Alle
|
||
Schalter zeigen den Stand, den die Box zurückmeldet – nach jedem Stellbefehl
|
||
wird ihr Zustand neu abgefragt, statt die Eingabe anzunehmen. Das Ausschalten
|
||
fragt einmal nach, weil dabei die Kühlung stoppt.
|
||
|
||
Interessant für die Energieübersicht: Die Box misst die Bordspannung und meldet
|
||
sie mit, was einen Quervergleich zu Batterie und Solarregler erlaubt.
|
||
|
||
Protokoll und Feldbelegung stammen aus
|
||
[Gruni22/alpicool_ha_ble](https://github.com/Gruni22/alpicool_ha_ble).
|
||
|
||
### Votronic-Solarladeregler
|
||
|
||
Anders als der Victron-Regler hängt der Votronic-Regler nicht selbst am
|
||
BLE-Advertising – er wird über eine eigene, unabhängige ESP32-Bridge
|
||
ausgelesen (siehe `firmware/vanalign/esp32_ble_solar.yaml`). Die Bridge
|
||
bewirbt ihren eigenen Dienst, wird beim Einrichten also automatisch erkannt
|
||
und die Art vorbelegt, genau wie beim Neigungsmesser.
|
||
|
||
Die Bridge liest den Regler über dessen Displaylink-Port (UART) mit der
|
||
externen ESPHome-Komponente
|
||
[syssi/esphome-votronic](https://github.com/syssi/esphome-votronic) aus und
|
||
stellt PV-Leistung, PV-Spannung/-Strom, Batteriespannung, Reglertemperatur
|
||
sowie Lade- und Reglerstatus über eigene Charakteristiken bereit – reine
|
||
Lesewerte, im Takt abgefragt wie beim Neigungsmesser.
|
||
|
||
### Nivellierung
|
||
|
||
Der Neigungsmesser [VanAlign Pro](https://github.com/) ist ein ESP32 mit
|
||
MPU6050, der Längs- und Querneigung über Bluetooth bereitstellt. Er bewirbt
|
||
seinen Dienst, wird beim Einrichten also sicher erkannt und die Art
|
||
vorbelegt.
|
||
|
||
Die Detailansicht bietet zwei Darstellungen, umschaltbar und über Starts
|
||
hinweg gemerkt:
|
||
|
||
* **Libelle** – die Blase wandert dorthin, wo das Fahrzeug höher steht, wie bei
|
||
einer echten Wasserwaage. Ringe markieren Toleranz und zwei Grad.
|
||
* **Fahrzeug** – Seitenansicht für längs, Heckansicht für quer, beide gekippt.
|
||
Die Heckansicht teilt die Blickrichtung des Fahrers, links im Bild ist also
|
||
links am Fahrzeug. Die Neigung ist dreifach überhöht dargestellt, sonst wäre
|
||
sie bei zwei Grad kaum zu erkennen; die angezeigten Gradzahlen sind echt.
|
||
|
||
In beiden Fällen: grün heißt eben (bis 0,5°), orange bis zwei Grad, darüber
|
||
rot. Dazu steht in Worten, welche Seite höher steht.
|
||
|
||
Die Fahrzeugansicht kennt mehrere Grafikstile (aktuell Vanster und
|
||
California), einstellbar je Fahrzeugprofil. Die Zeichnungen stammen aus dem
|
||
Ursprungsprojekt VanAlign Pro. Sie liegen als Schablonen im Asset-Katalog und
|
||
werden je nach Abweichung eingefärbt; die Helligkeit des Originals wurde
|
||
dafür in Deckkraft übersetzt, damit Fenster und Konturen beim Einfärben
|
||
erhalten bleiben.
|
||
|
||
Im Querformat rücken Libelle bzw. Fahrzeugansicht und die übrigen Angaben
|
||
nebeneinander statt untereinander – gedacht fürs Handy in der Halterung beim
|
||
Rangieren, wo man beiläufig hinschaut statt zu scrollen. Das gilt auch für
|
||
den Ausrichtungs-Assistenten weiter unten.
|
||
|
||
**Vor der ersten Nutzung die Einbaulage bestimmen.** Sitzt der Sensor quer,
|
||
gedreht oder kopfüber, meldet er längs und quer vertauscht oder mit falschem
|
||
Vorzeichen. Der Assistent unter *Sensor → Einbaulage* klärt das durch zwei
|
||
Kippbewegungen: einmal die Front nach unten, einmal die linke Seite. Aus der
|
||
Reaktion ergibt sich die Zuordnung – geraten wird nichts.
|
||
|
||
Dabei wird auch eine **Verdrehung um die Hochachse** mitgemessen, also der
|
||
Fall, dass der Sensor schräg statt längs im Fahrzeug klebt. Ohne diese
|
||
Korrektur verteilt sich eine reine Querneigung auf beide Achsen: Das Fahrzeug
|
||
kippt zur Seite, und die Längsanzeige kippt sichtbar mit – bei 20° Verdrehung
|
||
mit gut einem Drittel des Werts. Der Winkel steht im Ergebnis des Assistenten
|
||
(„um 20° verdreht") und wird von da an herausgerechnet.
|
||
|
||
Beim **Kalibrieren** liesse sich das nicht ermitteln, und zwar grundsätzlich
|
||
nicht: Es misst eine einzige Lage und zieht sie als Nullpunkt ab. Eine Drehung
|
||
um die Hochachse steckt darin nicht – eben sieht in jeder Verdrehung gleich
|
||
aus. Dafür braucht es zwei Kippbewegungen in bekannte Richtungen, und genau die
|
||
macht der Einbaulage-Assistent.
|
||
|
||
### Beides liegt im Sensor, nicht in der App
|
||
|
||
Sowohl die **Kalibrierung** als auch die **Einbaulage** speichert der ESP32
|
||
selbst und gibt sie an jeden aus, der fragt. Das ist der Punkt, sobald mehr als
|
||
ein Gerät im Spiel ist: iPhone, Apple Watch und Android-App zeigen dasselbe,
|
||
und bestimmt werden muss beides nur ein einziges Mal, von welchem Gerät aus
|
||
auch immer.
|
||
|
||
| | wo |
|
||
|---|---|
|
||
| Nullpunkt der Kalibrierung | im ESP (`pitch_offset`, `roll_offset`), wird dort auch schon abgezogen |
|
||
| Einbaulage samt Verdrehung | im ESP, Charakteristik `…3428`, lesbar und schreibbar |
|
||
| Spurweite und Radstand | in der jeweiligen App, im Fahrzeugprofil |
|
||
|
||
Beim Verbinden liest jede App die Einbaulage aus dem Gerät und übernimmt sie.
|
||
Wer sie neu bestimmt, schreibt sie hinauf. Steht dort noch nichts – ältere
|
||
Firmware oder nie bestimmt –, gilt weiter, was die App örtlich gespeichert hat;
|
||
kaputtgehen kann dabei nichts.
|
||
|
||
Angewandt wird sie trotzdem in den Apps und nicht im Sensor. Der Sensor
|
||
verwahrt sie nur: Würde er die Winkel schon umgerechnet melden, rechnete jede
|
||
ältere App die Korrektur ein zweites Mal ein.
|
||
|
||
Die acht Byte der Charakteristik sind in `VanAlignProtocol` beschrieben und in
|
||
`run-tests.sh` byteweise festgenagelt – daran hängen drei Apps und die
|
||
Firmware.
|
||
|
||
**Danach kalibrieren:** Fahrzeug eben stellen, dann *Auf
|
||
aktuelle Lage kalibrieren*. Ohne das zeigt die Anzeige die Lage des Sensors,
|
||
nicht die des Fahrzeugs – je nachdem, wie schief er eingebaut ist.
|
||
|
||
Die Werte werden zweimal je Sekunde abgefragt. Bietet ein Gerät `notify` an,
|
||
werden sie stattdessen abonniert – die mitgelieferte Firmware tut das
|
||
allerdings nicht, siehe deren README.
|
||
|
||
Ob bereits kalibriert wurde, meldet nicht jede Firmware zurück. Fehlt die
|
||
Angabe, gilt sie als unbekannt und die App warnt nicht – eine Warnung, die
|
||
sich nie abstellen lässt, wäre schlimmer als keine. Kalibrieren aus der App
|
||
braucht Firmware 1.0.2.
|
||
|
||
### Ausrichtungs-Assistent
|
||
|
||
Von der Nivellierungs-Ansicht aus erreichbar, gedacht fürs Rangieren. Er
|
||
verfolgt die Neigung über die Zeit und sagt, ob es gerade besser oder
|
||
schlechter wird – und wo es am besten stand: *„Vor 4 Sekunden stand das
|
||
Fahrzeug 0,8° flacher."* Steht der Camper in der Toleranz, meldet das Gerät
|
||
sich mit einer Vibration; das Display bleibt solange wach.
|
||
|
||
Bewusst **ohne** Positionsbestimmung. Aus einem MEMS-Beschleunigungssensor
|
||
lässt sich keine brauchbare Strecke ableiten, weil der Fehler beim zweifachen
|
||
Integrieren quadratisch mit der Zeit wächst und die Beschleunigungen im
|
||
Schritttempo ohnehin im Rauschen untergehen. Gebraucht wird das auch nicht:
|
||
Beim Einparken lautet die Frage nie „wo stehe ich", sondern „wird es besser".
|
||
Das steckt vollständig im zeitlichen Verlauf der Neigung – ohne jede Annahme
|
||
über das Gelände.
|
||
|
||
Sind beim Fahrzeug **Spurweite und Radstand** hinterlegt, rechnet der Assistent
|
||
zusätzlich die nötige Höhe der Auffahrkeile aus – **je Rad**, als Draufsicht auf
|
||
das Fahrzeug. Getrennte Angaben für quer und längs wären irreführend: „rechts
|
||
8 cm" und „vorne 4 cm" beschreiben dasselbe Fahrzeug, und unter beiden liegt
|
||
teils dasselbe Rad. Es steht auf vier Punkten, also gehören vier Zahlen hin.
|
||
|
||
Das ist reine Geometrie und damit exakt: 2,0° Querneigung bei 2,00 m Spurweite
|
||
ergeben 7,0 cm unter beide Räder der tieferstehenden Seite; kommen 1,5°
|
||
Längsneigung bei 3,50 m Radstand dazu, braucht die tiefste Ecke 16,1 cm, ihre
|
||
Nachbarn 9,2 und 7,0 cm, und das höchststehende Rad bleibt liegen. Die Maße
|
||
stehen im Fahrzeugprofil, erreichbar über das ⓘ in der Fahrzeugliste.
|
||
|
||
### Live Activity
|
||
|
||
In der Nivellierungs-Ansicht lässt sich eine Live Activity einschalten, die
|
||
die Neigung auch bei gesperrtem Bildschirm zeigt: Sperrbildschirm, Dynamic
|
||
Island und – seit iOS 26, ohne eigenes CarPlay-Ziel nötig – automatisch im
|
||
CarPlay-Dashboard. Der Schalter ist deaktiviert, solange keine Messwerte
|
||
anliegen, sonst startete die Anzeige gleich mit einem veralteten Stand.
|
||
|
||
Die Aktualisierung ist bewusst auf höchstens einmal je Sekunde gedrosselt.
|
||
Der Neigungsmesser liefert deutlich öfter, und stiesse jede Messung sofort
|
||
eine Aktualisierung an, drosselt iOS das von sich aus zunehmend stärker –
|
||
sichtbar vor allem in CarPlay, dessen Dashboard ohnehin zurückhaltender
|
||
aktualisiert als Sperrbildschirm oder Dynamic Island. Innerhalb der
|
||
Sperrfrist eingehende Messwerte werden nicht verworfen, sondern der
|
||
jeweils neueste für ihr Ende vorgemerkt.
|
||
|
||
Ein kurzer Verbindungsabbruch beendet die Aktivität nicht sofort – erst nach
|
||
zwölf Sekunden ohne Verbindung, und meldet sich das Gerät vorher zurück,
|
||
läuft sie unverändert weiter. War sie wegen einer längeren Trennung wirklich
|
||
beendet, startet sie bei der nächsten Verbindung automatisch neu.
|
||
|
||
## Apple Watch
|
||
|
||
Die Uhr zeigt dasselbe wie das Dashboard, nur auf das eingedampft, wofür man
|
||
den Arm hebt:
|
||
|
||
* **Übersicht** – je Gerät der Hauptwert und ein Punkt für den Zustand der
|
||
Verbindung. Ganz unten steht, wie alt der Stand ist.
|
||
* **Nivellierung** – drei Seiten zum Wischen, weil auf diesem Bildschirm ein
|
||
Wisch besser zu treffen ist als eine Scrollposition:
|
||
|
||
1. *Neigung* – wahlweise als Fahrzeugansicht wie am iPhone (Seiten- und
|
||
Heckansicht, dreifach überhöht) oder als Libelle; umschaltbar und über
|
||
Starts hinweg gemerkt. Dazu, was zu tun ist, und woher die Werte kommen.
|
||
2. *Keile* – die Höhe je Rad als Draufsicht. Fehlen Spurweite und Radstand im
|
||
Fahrzeugprofil, steht das dort statt einer leeren Seite.
|
||
3. *Ausrichten* – der Assistent fürs Rangieren.
|
||
|
||
Kalibriert wird nur am iPhone: Das gehört einmalig auf ebenen Boden, und ein
|
||
Knopf dafür an der Uhr wäre vor allem eine Gelegenheit, die Nullage aus
|
||
Versehen zu verstellen.
|
||
* **Ausrichten** – derselbe Assistent wie am iPhone: Tendenz, Rat und der
|
||
Hinweis auf den flachsten Punkt der letzten anderthalb Minuten. **Steht das
|
||
Fahrzeug in der Toleranz, vibriert die Uhr.** Das ist der eigentliche Gewinn
|
||
gegenüber dem iPhone – beim Rangieren schaut niemand aufs Display, aber die
|
||
Vibration am Handgelenk kommt an.
|
||
* **Kühlbox** – ein/aus, Eco oder Max und die Solltemperatur, über die Krone
|
||
gestellt. Verbunden wird die Box erst beim Öffnen dieser Ansicht, davor steht
|
||
dort der zuletzt gestellte Stand.
|
||
|
||
### Wer mit wem funkt
|
||
|
||
Die Aufteilung folgt dem, was jedes Gerät hergibt.
|
||
|
||
**Den Neigungsmesser funkt die Uhr selbst an.** Er bewirbt seinen Dienst, ist
|
||
also ohne jede Einrichtung auffindbar, und er ist unverschlüsselt – es gibt
|
||
keinen Schlüssel, der auf der Uhr ein zweites Mal lagern müsste. Damit steht
|
||
die Nivellierung am Handgelenk **ohne iPhone**: kein geöffnetes Telefon, keine
|
||
Reichweite dorthin. Genau dafür hebt man beim Rangieren den Arm.
|
||
|
||
Die Einbaulage des Sensors reist einmal vom iPhone herüber und bleibt auf der
|
||
Uhr gespeichert. Ohne sie stünden längs und quer je nach Einbau vertauscht oder
|
||
mit falschem Vorzeichen – eingestellt wird sie weiterhin nur am iPhone, im
|
||
Assistenten dort.
|
||
|
||
**Alles Übrige kommt über das iPhone.** Zwei Gründe, beide hart:
|
||
|
||
* BMS und Kühlbox lassen jeweils nur **eine** Verbindung zu. Eine mitlesende
|
||
Uhr nähme dem iPhone die Verbindung weg, statt sie zu ergänzen.
|
||
* Die Victron-Schlüssel liegen in der Keychain des iPhones. Sie auf die Uhr zu
|
||
kopieren hiesse, sie ein zweites Mal aufzubewahren, ohne dass der zweite Ort
|
||
irgendetwas brächte.
|
||
|
||
Was die Uhr stellt, stellt also in Wahrheit das iPhone – und angezeigt wird
|
||
auch dort nur, was das Gerät zurückmeldet, nicht der Tastendruck.
|
||
|
||
### Was das für den Betrieb heisst
|
||
|
||
Für Batterie, Solar und Kühlbox muss VanControl Pro auf dem iPhone laufen.
|
||
Solange die Uhr meldet, dass jemand hinschaut, hält die App das Funkgerät auch
|
||
im Hintergrund am Leben; die Victron-Werbedaten stehen dabei still, weil iOS im
|
||
Hintergrund kein ungefiltertes Suchen erlaubt.
|
||
|
||
Für die Nivellierung gilt das alles nicht – die läuft an der Uhr allein.
|
||
Gefunkt wird dort nur im Vordergrund: watchOS lässt eine App im Hintergrund
|
||
ohnehin kaum scannen, und beim Ausrichten schaut man auf die Uhr.
|
||
|
||
Gesendet wird in zwei Takten: ein halber Sekundentakt, solange die Watch-App im
|
||
Vordergrund ist, sonst alle zwei Sekunden und nur bei Änderungen. Der schnelle
|
||
Takt läuft über eine Frist, die die Uhr regelmässig erneuert – schläft sie ein,
|
||
hört das iPhone von selbst wieder auf.
|
||
|
||
Was die Uhr zeigt, ist immer Weitergereichtes. Deshalb steht auf jedem
|
||
Bildschirm das Alter des Standes: Eine abgerissene Strecke zum iPhone sähe sonst
|
||
genauso aus wie ein Fahrzeug, an dem sich nichts tut.
|
||
|
||
## Diagnose
|
||
|
||
Im Alltag zeigt die App nur Messwerte und Bedienelemente. Die technischen
|
||
Angaben – erkanntes Protokoll, Bluetooth-Merkmale, gesendete Befehle, Rohdaten
|
||
der letzten Antwort – sind ausgeblendet und lassen sich unter *Einstellungen →
|
||
Diagnose anzeigen* einschalten. Die Einstellungen sitzen im Fahrzeugmenü oben
|
||
links im Dashboard.
|
||
|
||
Meldet ein Gerät einen Fehler oder fehlt ein Victron-Schlüssel, werden die
|
||
Angaben unabhängig davon eingeblendet – dann sind sie genau das, was
|
||
weiterhilft.
|
||
|
||
## Mehrere Fahrzeuge
|
||
|
||
Oben links im Dashboard sitzt der Fahrzeugwechsel. Jedes Profil hat seinen
|
||
eigenen Gerätesatz; die App scannt und verbindet immer nur für das gewählte
|
||
Fahrzeug. Über *Fahrzeuge verwalten…* lassen sich Profile anlegen, umbenennen,
|
||
mit einem Symbol versehen und löschen. Geräte, die vor der Profilverwaltung
|
||
eingerichtet wurden, wandern beim Update automatisch ins erste Profil.
|
||
|
||
## Ohne Fahrzeug ansehen
|
||
|
||
Ein Demo-Modus füllt die App mit erfundenen Fahrzeugen und Messwerten, damit
|
||
sich die Ansichten ohne Bluetooth prüfen lassen. Nur in Debug-Builds, auf zwei
|
||
Wegen einzuschalten:
|
||
|
||
* **Schalter in den Einstellungen** (*Einstellungen → Entwicklung →
|
||
Demo-Modus*) – der Weg für einen lokal installierten Build ohne
|
||
Xcode-Verbindung, etwa auf dem eigenen iPhone. Wirkt erst nach einem
|
||
Neustart der App (im App-Umschalter nach oben wischen, dann neu öffnen),
|
||
weil `DeviceStore` und `BluetoothManager` den Stand nur beim Start lesen.
|
||
* **Umgebungsvariable** `CAMPER_DEMO=1` – in Xcode unter *Product → Scheme →
|
||
Edit Scheme → Run → Arguments*, oder im Simulator direkt:
|
||
|
||
```bash
|
||
xcrun simctl launch --terminate-running-process booted <deine-bundle-id>
|
||
# mit SIMCTL_CHILD_CAMPER_DEMO=1 davor
|
||
```
|
||
|
||
Solange der Demo-Modus läuft, schreibt die App nichts in die echte
|
||
Geräteliste – Änderungen an den erfundenen Fahrzeugen/Geräten verschwinden
|
||
beim Ausschalten wieder.
|
||
|
||
Die Watch-App kennt nur den Umgebungsvariablen-Weg und braucht dann kein
|
||
iPhone: Im Schema `VanControlWatch` dieselbe Variable setzen. Die Neigung
|
||
wandert dort langsam hin und her, sonst hätte der Ausrichtungs-Assistent
|
||
nichts zu zeigen.
|
||
|
||
## Protokolle prüfen
|
||
|
||
```bash
|
||
./run-tests.sh
|
||
```
|
||
|
||
Prüft die Entschlüsselung und die Rahmenverarbeitung ohne Hardware und ohne
|
||
Simulator – unter anderem AES-CTR gegen den Referenzvektor aus NIST SP 800-38A
|
||
und die Prüfsummen beider Daly-Dialekte.
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
firmware/vanalign/ Firmware der beiden ESP32 im Fahrzeug (ESPHome)
|
||
├── esp32_ble.yaml Neigungsmessung und Bluetooth-Schnittstelle
|
||
├── esp32_ble_solar.yaml Zweiter ESP32: Votronic-Solarregler über BLE
|
||
└── experimente/ Nicht für den Betrieb nötig
|
||
|
||
Shared/ In iPhone- und Watch-App übersetzt
|
||
├── Models/
|
||
│ ├── Profile.swift Fahrzeug samt Maßen
|
||
│ ├── AlignmentAssistant.swift Verlauf, Tendenz und Keilhöhe je Rad
|
||
│ ├── SensorOrientation.swift Einbaulage und ihre Erkennung
|
||
│ ├── ConfiguredDevice.swift Eingerichtetes Gerät, Rolle, Transportart
|
||
│ ├── DeviceSnapshot.swift Messwerte in Anzeigeform
|
||
│ ├── LevelState.swift Neigung, Toleranz und Klartext dazu
|
||
│ ├── VictronCodes.swift Klartexte für Zustands-/Fehlercodes
|
||
│ └── VehicleGraphicStyle.swift Grafikstile der Fahrzeugansicht (Vanster/California)
|
||
├── Bluetooth/
|
||
│ ├── VanAlignProtocol.swift Neigungsmesser: Winkel und Kalibrierung
|
||
│ ├── LevelSession.swift Verbindung zum Neigungsmesser (iPhone und Uhr)
|
||
│ ├── VotronicSolarESPProtocol.swift Votronic-Solarregler: Charakteristiken, Statusbits
|
||
│ └── VotronicSolarESPSession.swift GATT-Verbindung zur Solar-Bridge, im Takt abgefragt
|
||
├── VehicleTilt.swift Überhöhung und Farben der Fahrzeugansicht
|
||
├── WheelLiftPlan.swift Keilhöhen als Draufsicht auf die vier Räder
|
||
└── WatchLink/
|
||
└── WatchLink.swift Datensatz und Befehle zwischen iPhone und Uhr
|
||
|
||
SharedActivity/ In Haupt-App und Live-Activity-Extension übersetzt
|
||
├── LevelActivityAttributes.swift Inhalt der Live Activity (ActivityKit)
|
||
└── LevelDirectionFormatting.swift "H 1.8°" statt Vorzeichen, für App und Extension gleich
|
||
|
||
VanControl/
|
||
├── Bluetooth/
|
||
│ ├── BluetoothManager.swift Zentraler Scan, Verbindungen, Verlauf
|
||
│ ├── VictronAdvertisement.swift Advertisement entschlüsseln und auswerten
|
||
│ ├── AESCounterMode.swift AES-128-CTR (CommonCrypto)
|
||
│ ├── BitReader.swift Bitweises Lesen der gepackten Felder
|
||
│ ├── DalyProtocol.swift Daly-Rahmen und Prüfsummen
|
||
│ ├── DalyState.swift Sammelt Daly-Antworten zu einem Gesamtbild
|
||
│ ├── JBDProtocol.swift JBD/Xiaoxiang, Rahmen und Auswertung
|
||
│ ├── WattCycleProtocol.swift WattCycle, Freischaltung und Auswertung
|
||
│ ├── AlpicoolProtocol.swift Kühlboxen: Auswertung und Stellbefehle
|
||
│ └── BMSSession.swift GATT-Verbindung, Protokollerkennung, Abfrage
|
||
├── LiveActivity/
|
||
│ └── LevelActivityManager.swift Startet/aktualisiert/beendet die Live Activity, drosselt auf 1 Hz
|
||
├── Store/
|
||
│ ├── DeviceStore.swift Geräteliste, Persistenz
|
||
│ ├── DemoData.swift Erfundene Werte für den Demo-Modus
|
||
│ └── KeychainStore.swift Victron-Schlüssel
|
||
├── Watch/
|
||
│ └── PhoneWatchLink.swift Sendet den Stand, nimmt Befehle der Uhr an
|
||
└── Views/
|
||
├── DashboardView.swift Kachelübersicht
|
||
├── DeviceCard.swift Eine Kachel
|
||
├── DeviceDetailView.swift Alle Werte, Verlauf, Zellspannungen, Diagnose
|
||
├── ProfilesView.swift Fahrzeuge anlegen und verwalten
|
||
├── FridgeControls.swift Bedienelemente der Kühlbox
|
||
├── LevelView.swift Libelle, Kalibrierung, Live-Activity-Schalter
|
||
├── LevelSetupView.swift Einbaulage und Nullpunkt
|
||
├── AlignmentAssistantView.swift Ausrichtungs-Assistent fürs Rangieren
|
||
├── VehicleTiltView.swift Neigung am Fahrzeug dargestellt
|
||
├── VictronKeyView.swift Verschlüsselungsschlüssel eintragen
|
||
├── SettingsView.swift App-Einstellungen
|
||
├── SensorSetupView.swift Assistent für die Einbaulage
|
||
└── AddDeviceView.swift Scannen und Einrichten
|
||
|
||
VanControlWatch/ watchOS-App
|
||
├── CamperWatchApp.swift Einstieg, meldet dem iPhone das Hinschauen
|
||
├── PhoneLink.swift Gegenstelle zum iPhone
|
||
├── WatchLevelRadio.swift Eigene Bluetooth-Verbindung zum Neigungsmesser
|
||
├── WatchDemo.swift Erfundene Werte für den Simulator
|
||
└── Views/
|
||
├── WatchRootView.swift Übersicht und Altersangabe
|
||
├── WatchBubble.swift Libelle für den kleinen Bildschirm
|
||
├── WatchVehicleView.swift Fahrzeugansicht auf Uhrgrösse
|
||
├── WatchLevelView.swift Die drei Seiten: Neigung, Keile, Ausrichten
|
||
├── WatchAlignView.swift Ausrichten mit Vibration
|
||
├── WatchDeviceDetailView.swift Alle Werte eines Geräts
|
||
└── WatchFridgeControls.swift Kühlbox stellen
|
||
|
||
VanControlComplication/ Zifferblatt-Komplikation (watchOS)
|
||
└── LevelComplication.swift Tippt auf die Nivellierung, ohne eigenen Messwert
|
||
|
||
VanControlLiveActivity/ Live-Activity-Extension (WidgetKit)
|
||
├── VanControlLiveActivityBundle.swift Einstieg der Extension
|
||
└── VanControlLiveActivity.swift Ansicht für Sperrbildschirm, Dynamic Island, CarPlay
|
||
|
||
Config/
|
||
├── VanControl-Info.plist Nur der Hintergrundbetrieb für die Uhr
|
||
└── VanControlComplication-Info.plist Anzeigename der Komplikation
|
||
```
|
||
|
||
## Bekannte Grenzen
|
||
|
||
* **Im Hintergrund nur die verbundenen Geräte.** iOS erlaubt das ungefilterte
|
||
Scannen nach Advertisements nur im Vordergrund; die Victron-Werte stehen im
|
||
Hintergrund also still. Verbundene Geräte – Neigungsmesser, BMS, Kühlbox –
|
||
laufen weiter, aber nur solange die Uhr meldet, dass jemand hinschaut. Ohne
|
||
Uhr pausiert die App im Hintergrund vollständig und nimmt beim Zurückkommen
|
||
wieder auf.
|
||
* **Die Uhr braucht das iPhone – ausser für die Nivellierung.** Batterie, Solar
|
||
und Kühlbox zeigt sie nur, was von dort kommt; ist das iPhone ausser
|
||
Reichweite, steht dort, wie alt der letzte Stand ist. Den Neigungsmesser
|
||
erreicht sie selbst.
|
||
* **Neigungsmesser und Uhr:** Der ESP32 nimmt mehrere Verbindungen an, iPhone
|
||
und Uhr können also gleichzeitig mitlesen. Weist er die zweite ab, hilft es,
|
||
die iPhone-App zu schliessen – dann gehört der Sensor der Uhr.
|
||
* **Auf der Uhr muss beim ersten Start Bluetooth erlaubt werden**, sonst bleibt
|
||
die Nivellierung dort leer.
|
||
* **Die Live Activity aktualisiert höchstens einmal je Sekunde.** Öfter
|
||
drosselt iOS lokale Aktualisierungen ohnehin von sich aus, zunehmend
|
||
stärker – sichtbar vor allem in CarPlay. Siehe [Live Activity](#live-activity).
|
||
* **Das Modbus-Registerlayout des neuen Daly-Protokolls variiert zwischen
|
||
Firmwareständen.** Das klassische `A5`-Protokoll und das JBD-Protokoll sind
|
||
gut dokumentiert; falls dein BMS Modbus spricht und Werte unplausibel
|
||
aussehen, muss das Mapping in `DalyState.apply(registers:)` am realen Gerät
|
||
nachgezogen werden.
|
||
* **Die Feldbelegung der BMS-Datensätze ist nicht an jedem Modell geprüft.**
|
||
Meldet die Diagnose dauerhaft „wird ermittelt“, spricht der Akku ein
|
||
Protokoll, das die App nicht kennt – die dort angezeigte Rohantwort ist dann
|
||
der Ansatzpunkt.
|
||
* Die Feldbelegungen der Victron-Datensätze stammen aus Victrons
|
||
„Extra Manufacturer Data“-Beschreibung. Solarladeregler und DC/DC-Wandler
|
||
sind die am besten belegten Typen.
|
||
* Die Bluetooth-Kennung eines Geräts ist pro iPhone eindeutig. Auf einem
|
||
zweiten Gerät müssen die Geräte neu eingerichtet werden.
|