Software

Zur GitHub Repo

Der ganze Quellcode OpenSource.

Das Herz der Software ist ein RaspberryPi 4 mit 4 GB RAM mit einem 7 Zoll Touchdisplay. Auf meiner 32GB großen SD-Karte installierte ich Raspbian Lite.

Ich begann mit der Überlegung, welche Programmiersprache zur Entwicklung am besten geeignet wäre. Da ich bereits gute Erfahrungen mit Flutter/Dart hatte, bot sich dies für mich am meisten an. Zudem ist Flutter für Smartphones optimiert, was es optimal für die Verwendung mit einem Touchscreen machte.

Allerdings gab es keinen offiziellen Support von Flutter für den RaspberryPi. Es war zwar eine Linux-Unterstützung in der Entwicklung, diese war aber noch nicht besonders weit. Jedoch stieß ich schnell auf flutter-pi, ein Flutter Engine Embedder. Dieser ermöglicht es Flutter Apps direkt aus dem Terminal Modus im Vollbild zu starten, das heißt die Raspbian Oberfläche mit X11 muss nicht gestartet werden. Das war für mich perfekt, da die Software möglichst schnell starten sollte und ich die Raspbian Oberfläche nicht benötigte.

Die Oberfläche selbst hat sich während der Entwicklung nie groß verändert. Auch wenn ich selbst zugeben muss, dass sie recht ähnlich zu dieser von Tesla ist, war dies nicht meine Absicht :) Da ich bereits eigene Design-Vorlagen zu einem Light- und DarkMode hatte, war es auch kein besonders großer Aufwand mehr dieses Feature zu entwickeln. Besonders bei Nachtfahrten ist es sehr angenehm den DarkMode zu aktivieren, da das Display keine eingebaute Dimm-Funktion besitzt.

Das Dashbaord im LightMode. Weil der Controller nicht verbunden war, fehlen ein paar Daten.

Und so im DarkMode. Unten links das ControlCenter zum Sperren des Motors, Hupen oder Licht abblenden.

Um das Kärelle zu einer vollständigen Auto Software zu machen, durfte natürlich auch nicht eine Map fehlen. Diese musste offline verfügbar sein und am besten auch Waldwege abdecken. Zu Beginn suchte ich nach Packages, die eine offline Unterstützung beinhalteten. Dabei war flutter_map eines der ersten welches ich fand.

Um die offline Map-Daten im richtigen Format zu erhalten, war es als erstes nötig öffentliche Deutschland Map-Daten in einem verarbeitbaren Format zu finden. Diese downloadete ich von OSM QA tiles, welche im .mbtiles Format gespeichert waren, allerdings als Vektorgrafiken und nicht als PNGs.

Der nächste Schritt war dann diese Daten in TileMill, eine Map-Daten Software zu importieren. In dieser passte ich dann die Farben an, für Light und DarkMode und exportierte die Map wieder im .mbtiles Format, diesmal allerdings als PNGs gespeichert. Zuletzt war es dann nötig diese Datei mit einem Python Skript zu entpacken und auf den RaspberryPi zu laden.

Im Ganzen also ein recht großer Aufwand. Ich hatte verschiedene Probleme mit dem Installationen, musste mich durch verschiedene Dokumentationen quälen, bis ich letztendlich die fertigen Daten hatte. Zudem funktionierte das flutter_map Package nicht richtig mit dem Touchscreen. Deshalb schwenkte ich zuletzt noch auf das map Package um. Dieses war zwar etwas schwieriger zu handhaben, funktionierte jedoch einwandfrei mit dem Touchdisplay.

Die Verwendung von Flutter auf dem RaspberryPi führte aber auch zu neuen Problemen. Da es keine native Unterstützung von Flutter auf dem RaspberryPi gab, musste ich die Schnittstellen zwischen Flutter und Linux teilweise selbst programmieren. Dazu verwendete ich dart:ffi. Dieses Package ermöglicht es, Funktionen aus Dynamischen Libraries mit Flutter aufzurufen.

Die Motor-Daten auszulesen war der erste Part, indem ich dart:ffi benötigte. Die Daten werden über den Motor-Controller ausgegeben. Im Elektronik Teil gibt es noch mehr zum Desaster des Motor-Controllers...

Mit dem Motor-Controller sollte es möglich sein, Daten wie RPM, Akkuspannung, Motorfehler auszulesen. Diese Daten waren essentiell für die Software, da nur so eine Geschwindigkeits oder Akkuanzeige realisierbar war.


Der GoldenMotors Motor-Controller verfügte über eine UART Schnittstelle, über welche es laut Produktbeschreibung möglich sein sollte diese Daten auszulesen. Allerdings wurde dafür keinerlei Dokumentation oder ähnliches bereitgestellt. Glücklicherweise fand ich auf GitHub ein inoffizielles Protokoll, welches die Funktionsweise erklärte. Je nachdem welche Daten man auslesen möchte schickt man ein Datenpaket an den Controller, welcher dann die gewünschten Daten wieder zurückgibt. Der wichtigste Teil des Datenpakets ist der CMD Byte (Command Byte). Dieser definiert unter anderem welche Daten wieder zurück gesendet werden sollen.

Der Aufbau zum Testen von UART mit Controller, Motor und Raspberry Pi auf meinem Schreibtisch. Im Vordergrund der provisorische Gas-Podi.

Schnell schaffte ich es eine Verbindung mit dem Controller herzustellen. Doch dann begann der schwierige Teil. Die Interpretation der erhaltenen Daten erwies sich als recht schwierig. Teilweise stimmte die Länge der eigentlich zu erhaltenen Daten nicht mit der Länge im Protokoll überein. Zudem verwendete der Ersteller des Protokolls einen anderen Motor-Controller Typ als wir. Verschiedene Interpretationsansätze führten zu keinem Ergebnis. Daraufhin kam ich auf die Idee, einmal jeden möglichen Befehl, also 0x00 bis 0xFF, an den Controller zu senden und die Daten dann bei laufendem und stehendem Motor zu vergleichen. Das Ziel war es, zumindest die Geschwindigkeit und ähnliches auslesen zu können. Diese Idee erwies sich im Nachhinein jedoch als großer Fehler.

Der Controller war nachdem ich die Befehle gesendet hatte nicht mehr funktionabel. Der Motor drehte sich nicht mehr, per PC konnten keine Einstellungen mehr am Controller vorgenommen werden und auch über UART mit dem RaspberryPi war keine Verbindung mehr möglich. Nach langem Hin und Her mit dem Golden Motors Support sendeten wir den Controller letztendlich zurück, mehr dazu wieder im Elektronik Teil. Anstelle eines GoldenMotor Controllers kauften wir uns dann einen KellyController.

Der Aufbau zum Testen des KellyController hinten am Fenster.

Per CAN Bus war es bei diesem möglich Daten live auszulesen. Zudem gab es ein offizielles Protokoll, welches genau erklärte wie die erhaltenen Daten zu verstehen sind. Da der RaspberryPi keine direkte CAN Schnittstelle besitzt, kauften wir noch eine Platine von JOY-IT. Diese liest die CAN Daten ein und gibt sie über die SPI Schnittstelle weiter. Zum Testen ob die Verbindung funktionierte, verwendete ich can-utils. Und siehe da - das Auslesen funktionierte auf den ersten Versuch :D Man konnte live erkennen, wie sich die Zahlen bei höherer Motordrehzahl veränderten. Das war nach dem langen arbeiten an der UART Schnittstelle eine große Freude.

Als nächstes war es dann an der Reihe, das Flutter Package zum Einlesen der Daten zu entwickeln. Auf der Linux Seite verwendete ich SocketCAN. Dabei orientierte ich mich an dem Codeausschnitt der Wikipedia Seite und der offiziellen Dokumentation. Da diese einfach nur die Linux Standard Library “libc.so.6” verwendeten, war die Implementation per dart:ffi letztendlich einfacher als gedacht. Hier möchte ich mich besonders bei Hannes Winkler bedanken, welcher mir besonders bei der Entwicklung dieses Packages geholfen hat und immer hilfsbereit und offen war. Danke! Das Package ist unter linux_can auf GitHub zu finden.

Der I2C Bus war die zweite benötigte Schnittstelle. Diese benötigte ich zum Auslesen der Temperatur Daten. Für dieses Package verwendete ich WiringPi, ebenfalls mit dart:ffi. WiringPi installiert eine Dynamic Library unter ‘/usr/lib/libwiringPi.so’, das Implementieren mit dart:ffi war hier also noch deutlich einfacher. Das Package ist unter wiring_pi_i2c auf GitHub zu finden. Als Temperatursensoren verwendeten wir die MCP9808. Diese geben die Temperatur sogar auf den vierstelligen Kommabereich an, worauf wir aber verzichteten.

Ausschnitt aus den Einstellungen zu dem Temperaturdaten.

Die Temperaturdaten behandelten wir nach Position auf dem Kärelle unterschiedlich. Während der Temperatursensor im Schaltschrank und die Temperatur des Motor Controllers lediglich den Lüfter steuern, werden bei hohen Akkutemperaturen der langsame Modus aktiviert und der Nutzer aktiv gewarnt. Tatsächlich war die Entwicklung dieser Steuerung schwieriger als gedacht. Während zum Beispiel das Auslesen der Motor-Daten rein nach Schema funktionierte, musste bei der Temperatursteuerung auf verschiedenste Bedingungen eingegangen werden. So war es zum Beispiel ein Problem, dass die Temperaturen zwischen Grenzwerten schwanken. Es erschien also eine Fehlermeldung, welche wenige Sekunden später wieder verschwand und dann wieder aufpoppte. Um dies zu Verhindern musste überall zudem ein Wert definiert werden, unter welchem die Fehlermeldung wieder verschwinden sollte. Als Beispiel:

  • Warnung des Nutzers bei einer Akkutemperatur über 40°C

  • Verschwinden der Warnung unter 35°C

Per PWM konnten wir den Lüfter im Schaltschrank ansteuern. Da wir auch Audio über den RaspberryPi laufen lassen wollten, konnten ich nicht die Hardware PWM verwenden. Zuerst versuchte ich selbst ein Software PWM Package in Flutter zu entwickeln, dabei gab es jedoch Performance Probleme. Zuletzt verwendete ich also doch WiringPi, auch hierzu gibt es die dart:ffi Implementation als Flutter package auf GitHub.

Auch die Lichtsteuerung benötigte PWM. Da unseren Frontlichter jeweils über 2760 Lumen hell werden können, war es uns wichtig diese dimmen zu können. Ebenfalls entwickelte ich für diese auch eine Animation, was das Ausschalten und Abblenden sehr cool aussehen lässt.

Neben den Frontlichtern hat das Kärelle auch noch rote Rücklichter, mit zusätzlichen weißen LEDs, welche aktiviert werden wenn der Rückwärtsgang eingeschalten wird.

Ein Highlight ist auch die Bodenbeleuchtung. Die Farbe dieser kann in der Software eingestellt werden. Zudem wird diese an den jeweiligen Benutzer angepasst.

Die Einstellungen für die Lichtsteuerung.


Soweit zur Software.

Hier mehr zu...