Alles auf einem Bildschirm hiess scrollen, und auf dieser Grösse ist das
der falsche Handgriff: Einen Wisch trifft man beim Rangieren auch ohne
hinzusehen, eine Scrollposition nicht. Was gerade zählt, hängt ausserdem
vom Arbeitsschritt ab.
Jetzt drei Seiten zum Wischen:
* Neigung – Fahrzeugansicht oder Libelle, was zu tun ist, Herkunft der
Werte
* Keile – die Höhe je Rad. Fehlen Spurweite und Radstand, steht das dort,
statt dass die Seite leer wirkt.
* Ausrichten – der Assistent, vorher hinter einem Knopf
Der Assistent ist dabei enger gesetzt: kleinere Zahl, zwei Zeilen Rat mit
Skalierung, kleinere Libelle, und kein ScrollView mehr. Beim Rangieren
schaut man kurz hin, da darf nichts ausserhalb des Bildes liegen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Dinge in einem Commit, weil sie sich dieselben Dateien teilen:
project.pbxproj und WatchRootView.swift tragen beides.
**Komplikation.** Neues Target CamperMonitorComplication, auf watchOS
sind Komplikationen WidgetKit-Widgets. Es wird in die Watch-App
eingebettet (PlugIns) und bietet die vier Zubehör-Familien an. Ein Tipp
öffnet die App direkt auf der Nivellierung statt in der Geräteliste:
Die Komplikation hängt eine Adresse an, die Watch-App führt dafür jetzt
einen Navigationsweg, den sie von aussen setzen kann. Die Adresse steht
in Shared/WatchDeepLink.swift, weil beide Seiten sie brauchen – ein
Tippfehler auf einer Seite bliebe sonst stumm.
Bewusst ohne Messwert: Eine Komplikation läuft in einem eigenen Prozess
und käme an die Werte nur über eine App-Gruppe, und die verlangt eine
bezahlte Mitgliedschaft. Ein Wert, der beim Blick aufs Zifferblatt
Stunden alt sein kann, wäre ohnehin schlimmer als keiner – man glaubt
ihm. Die Komplikation tut deshalb genau eine Sache, und die zuverlässig.
**Rebranding.** Der sichtbare Name ist überall VanControl Pro:
CFBundleDisplayName beider Apple-Targets, Android-Label, die Texte in
beiden Apps, beide READMEs und der Name der APK im Gitea-Ablauf.
Die Kennungen bleiben, wie sie sind – Bundle-IDs, Android-Paket,
Target-, Ordner- und Schemanamen. Eine geänderte Bundle-ID ist für iOS
eine neue App: Geräte, Fahrzeugprofile und Victron-Schlüssel wären weg,
dazu neue Profile und eine neu zu koppelnde Watch-App. Sichtbar ist
davon nichts, also gibt es dafür auch keinen Grund.
Geprüft: alle drei Apple-Targets bauen, die Erweiterung liegt im
Watch-Bundle unter PlugIns, die Anzeigenamen stimmen im gebauten
Bundle, Tests grün. Die neue Bundle-ID der Erweiterung braucht beim
ersten Lauf aus Xcode ein Profil, das Xcode selbst anlegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Kalibrier-Nullpunkte lagen schon immer im ESP und wurden dort auch
abgezogen – nur verriet die Firmware sie niemandem. Die Einbaulage lag
dagegen in jeder App einzeln, und damit stand sie bei drei Endgeräten
dreimal da, im Zweifel dreimal verschieden. Sie beschreibt aber den
Einbau und nicht das Telefon.
Zwei neue Charakteristiken:
...3426 Nullpunkte, zwei Floats, nur lesen
...3428 Einbaulage, acht Byte, lesen und schreiben
0 Version, 1 = gültig gesetzt, 0 = nie geschrieben
1 Längsachse: 0 = Pitch des Sensors, 1 = Roll
2 längs umgekehrt (0/1)
3 quer umgekehrt (0/1)
4..7 Verdrehung um die Hochachse, float32, Grad
Das Versionsbyte löst die Umstellung: Steht dort 0, hat nie jemand
geschrieben – dann gilt weiter, was die App örtlich gespeichert hat, und
beim nächsten Bestimmen wandert sie hinauf. Mit älterer Firmware ohne die
Charakteristik passiert schlicht nichts.
Angewandt wird die Lage weiterhin in den Apps. Der Sensor verwahrt sie
nur: Meldete er die Winkel bereits umgerechnet, rechnete jede ältere App
die Korrektur ein zweites Mal ein.
Beim Verbinden liest jede App die Lage und übernimmt sie; nach dem
Einbaulage-Assistenten schreibt sie sie hinauf. Die Uhr braucht das
iPhone dafür nicht mehr – sie holt sie sich selbst. Die Android-App
bekommt dabei auch die Verdrehung, die ihrer Fassung bisher ganz fehlte:
Modell, Erkennung und Persistenz nachgezogen.
Geprüft: Firmware übersetzt, im erzeugten Code stehen beide
Charakteristiken mit den richtigen Rechten und der Schreib-Handler;
iPhone- und Watch-Ziel bauen; sieben neue Prüfungen in run-tests.sh
nageln das Byte-Format fest, samt Rundlauf und der Bedeutung von
Version 0. Nicht geprüft ist die Android-Fassung: Auf dieser Maschine
liegt nur Java 8, Gradle verlangt mindestens 11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Uhr hatte Hänger und wurde von watchOS wegen Speicherverbrauch
abgeschossen. Ursache war eine Schleife ohne Bremse: Ist der Sensor
gerade nicht zu haben, folgte auf jeden Verbindungsversuch sofort der
Abbruch, auf den Abbruch sofort die nächste Suche – so schnell, wie das
Funkmodul es zulässt. Jeder Durchlauf schiebt Arbeit auf den
Hauptthread; die Uhr war dann mit Verbindungsaufbau beschäftigt statt
mit Anzeigen.
Drei Änderungen:
* Nach einem Fehlschlag wird gewartet, beginnend bei einer Sekunde und
verdoppelnd bis acht. Kommen wieder Werte, beginnt die Wartezeit von
vorn.
* Fundmeldungen, die nach dem Abschalten der Suche noch nachkommen,
werden verworfen. Sie lösten je einen weiteren Verbindungsversuch aus.
* Messwerte gehen höchstens fünfmal je Sekunde an die Oberfläche. Der
Sensor meldet zehnmal je Sekunde und für zwei Achsen getrennt, das
waren bis zu zwanzig Neuzeichnungen pro Sekunde.
Dazu die Fahrzeugbilder der Watch-App von iPhone-Auflösung (925x600 und
600x600) auf Bildschirmgrösse gebracht. Auf einem 40-mm-Display wurden
sie in voller Grösse entpackt, ohne dass davon etwas zu sehen war.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher standen dort zwei Angaben nebeneinander, etwa "rechts 8 cm" und
"vorne 4 cm". Das liest sich wie zwei Aufgaben, ist aber eine: Unter
"rechts" liegen zwei Räder, unter "vorne" auch, und eines davon ist
dasselbe. Ausführen lässt sich das so nicht.
Gerechnet wird jetzt über alle vier Aufstandspunkte auf einmal. Aus
Spurweite, Radstand und den beiden Winkeln ergeben sich deren Höhen;
angehoben wird auf das höchststehende Rad, das liegen bleibt. Aus dem
Beispiel oben werden damit 12 cm vorne rechts, 8 cm hinten rechts, 4 cm
vorne links und nichts hinten links – dieselbe Geometrie, nur richtig
zusammengesetzt.
Angezeigt wird das als Draufsicht auf das Fahrzeug (WheelLiftPlan), auf
iPhone und Uhr dieselbe Ansicht, auf der Uhr kompakt. LevelingWedge
entfällt.
Abgesichert in run-tests.sh: reine Querneigung, reine Längsneigung,
beides zusammen (16,1 / 9,2 / 7,0 / 0 cm), gespiegelte Vorzeichen und
die Fälle ohne Fahrzeugmasse.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Uhr funkt den Neigungsmesser jetzt selbst an, statt die Werte vom
iPhone weiterreichen zu lassen. Bei diesem einen Gerät geht das, und nur
bei ihm: Es bewirbt seinen Dienst, ist also ohne Einrichtung auffindbar,
und es 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 geöffnetes iPhone, und genau dafür hebt man beim Rangieren den Arm.
Batterie, Solar und Kühlbox bleiben beim Weg über das iPhone: Sie lassen
nur eine Verbindung zu, und die Victron-Schlüssel liegen in dessen
Keychain. Die Einbaulage des Sensors reist einmal mit dem Datensatz
herüber und bleibt auf der Uhr gespeichert – ohne sie stünden längs und
quer je nach Einbau vertauscht.
LevelSession und VanAlignProtocol wandern nach Shared/Bluetooth; beide
sind reines CoreBluetooth und laufen auf watchOS unverändert. Die
Fahrzeugansicht gibt es jetzt auch auf der Uhr, umschaltbar zur Libelle;
Überhöhung und Farbregeln stehen in Shared/VehicleTilt.swift, damit
iPhone und Uhr nicht auseinanderlaufen.
Kalibrieren fällt auf der Uhr weg, samt Befehl. Das gehört einmalig ans
iPhone mit ebenem Fahrzeug; ein Knopf dafür am Handgelenk wäre vor allem
eine Gelegenheit, die Nullage aus Versehen zu verstellen.
Zwei Fehler dabei behoben:
* Beim Start meldete die Uhr dem iPhone nie, dass jemand hinschaut –
onChange(of: scenePhase) feuert beim ersten Erscheinen nicht. Der
schnelle Sendetakt blieb aus, bis die App einmal im Hintergrund war.
* Ein einmal direkt gemessener Wert hatte für immer Vorrang. Blieb der
Sensor stehen, ohne die Verbindung zu trennen, klebte die Anzeige
daran. Jetzt gilt er drei Sekunden als frisch, danach übernimmt der
Stand des iPhones – mit Alter und Grund daneben.
Ausserdem WATCHOS_DEPLOYMENT_TARGET auf 10.0 (Xcode hatte 11.6 gesetzt)
und die Bluetooth-Begründung für das Watch-Target.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neues Target CamperMonitorWatch, eingebettet in die iPhone-App. Die Uhr
zeigt je Gerät den Hauptwert, die Nivellierung mit Libelle und Keilhöhen,
den Ausrichtungs-Assistenten und stellt die Kühlbox.
Die Uhr funkt nicht selbst, obwohl watchOS das könnte. BMS, Kühlbox und
Neigungsmesser lassen jeweils nur eine Verbindung zu – eine mitlesende Uhr
würde dem iPhone die Verbindung wegnehmen, statt sie zu ergänzen. Und die
Victron-Schlüssel liegen in der Keychain des iPhones; sie ein zweites Mal
auf der Uhr aufzubewahren brächte nichts. Das iPhone bleibt also das
Funkgerät und reicht fertige Messwerte über WatchConnectivity weiter.
Gesendet wird in zwei Takten: ein halber Sekundentakt als Nachricht,
solange die Watch-App im Vordergrund ist und das über eine Frist meldet,
sonst alle zwei Sekunden als Anwendungskontext und nur bei Änderungen.
Läuft die Frist ab, hört das iPhone von selbst wieder auf.
Solange die Uhr hinschaut, hält die App das Funkgerät auch im Hintergrund
am Leben (UIBackgroundModes in Config/CamperMonitor-Info.plist) – beim
Rangieren liegt das iPhone sonst mit dunklem Bildschirm in der Halterung
und die Anzeige am Handgelenk wäre genau dann tot. Die verbundenen Geräte
liefern dabei weiter, die Victron-Werbedaten nicht: ungefiltertes Suchen
lässt iOS im Hintergrund nicht zu.
Der plattformneutrale Modellcode wandert nach Shared/ und wird in beide
Targets übersetzt; LevelState ist dafür aus VanAlignProtocol.swift
herausgelöst. Datensatz und Befehle stehen in Shared/WatchLink und sind
in run-tests.sh abgesichert: Rundlauf, Schutz gegen fremde Formatversion,
Vergleich ohne Zeitstempel und die Auflösung der Zeitangaben.
Geprüft sind der Bau beider Targets samt Einbetten und der Testlauf; auf
echter Hardware noch nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>