Wie eine Karte mit 3 GB auf einen ESP32 passt

Vektorkarte von Lorsch auf Zoomstufe 16 in den sieben Farben des E-Paper-Displays
Lorsch auf Zoomstufe 16, direkt aus einer Mapsforge-Datei gerendert. Straßen, Gebäude als gerastertes Muster, Felder und Wiesen in den sieben Farben des Displays. Kartendaten © OpenStreetMap-Mitwirkende.

Kartenkacheln haben einen Haken: Man muss vorher wissen, wo man hin will. Für jede Tour Kacheln laden, umrechnen und übertragen, das funktioniert, ist aber genau die Art Vorbereitung, die man vor einer spontanen Wanderung nicht machen will. Die Lösung gibt es schon lange: Mapsforge, das Vektorkartenformat, das viele Offline-Kartenapps auf dem Handy nutzen. Eine Datei für eine ganze Region auf der SD-Karte, und das Gerät zeichnet die Karte selbst.

mapsforge-rs

Im ersten Beitrag hatte ich erwähnt, dass der Versuch, die ganze Firmware nach Rust zu portieren, nicht in main gelandet ist. Rust kam trotzdem, als eigene Bibliothek: mapsforge-rs liest Mapsforge-Dateien und liefert Polygone, Linien und Punkte. Ein kleiner Rasterizer in C zeichnet sie direkt in den Framebuffer. Die Firmware ruft das über eine C-Schnittstelle auf und verankert die Vektorkarte am selben Pixel wie die Kachelkarte. Marker, Track und Wegpunkte funktionieren deshalb ohne Änderung weiter. Liegt keine MAPS/map.map auf der Karte, nimmt India Navi wie bisher die Kacheln.

Für den ESP32-S3 heißt das: Rust für Xtensa. Die Toolchain kommt über espup, gebaut wird mit -Zbuild-std für core und alloc. Ein Build-Skript für PlatformIO baut die Bibliothek mit cargo und linkt sie dazu. Seit dem 25. Oktober ist mapsforge-rs ein Submodul, ein rekursiver Clone baut also direkt mit Vektorkarte, und die CI installiert die Toolchain selbst.

70 Millisekunden pro Seek

Die erste Version hat auf dem Gerät 9 Sekunden für ein Bild gebraucht. Der Grund war nicht die Grafik, sondern das Dateisystem. Eine Mapsforge-Datei ist nicht linear zu lesen, sie springt ständig zu den Daten der benötigten Kachel. In einer Datei mit 3 GB hat FatFs bei jedem Sprung die Cluster-Kette von vorne abgelaufen, 70 ms pro Seek. Mit Fast Seek von FatFs wird daraus ein Tabellenzugriff. Die Cluster-Tabelle aufzubauen dauert einmalig 5 Sekunden und bleibt erhalten, solange die Datei gleich bleibt. Danach dauert ein Bild etwa 1 Sekunde. Der Heap der Bibliothek liegt im PSRAM, gelesen wird über einen kleinen DMA-Puffer im internen RAM. Die Lektion aus dem Speicher-Audit wirkt also nach.

Vektorkarte von Traunstein auf Zoomstufe 16 als Annäherung an das E-Paper-Panel mit Wald, Wiese, See und Fluss
Traunstein auf Zoomstufe 16 als Annäherung an das echte Panel, mit gedämpften Farben und leichter Unschärfe. Wald, Wiese, See, Fluss und Feuchtgebiet bekommen jeweils eine eigene Mischung der sieben Farben.

Farben, Muster und Namen

Viel Arbeit steckt nicht im Code, sondern in der Frage, wie eine Karte mit sieben Farben gut aussieht. Gemischte Flächen wie Gebäude oder Wiesen werden mit einem Muster aus Vorder- und Hintergrundfarbe gefüllt. Damit das schnell geht, habe ich den Linux-Simulator der Firmware erweitert: Er rendert die Vektorkarte auf dem PC und speichert auf Wunsch einen Screenshot. Ein Skript baut, rendert und speichert ein PNG, so lässt sich ein Kartenstil in Sekunden prüfen statt in Minuten mit Flashen. Inzwischen stehen auch Ortsnamen in der 8 × 8 Schrift auf der Karte.

In der App erklärt seit Version 1.4.0 der Filterbildschirm im Modus „Vektor“, wie Flächen, Wege und POIs auf dem Gerät aussehen. Wer unterwegs auf ein Muster schaut, kann dort nachsehen, was es bedeutet.

Und bis der GPS-Fix da ist?

Kartenbildschirm von India Navi ohne GPS-Fix: Wanderer, Logo, Satellit und der Text Searching for GPS
So sieht die Karte aus, bis die Position bekannt ist. Screenshot aus dem Linux-Simulator.

Ohne Fix blieb der Kartenbildschirm bisher einfach weiß. Jetzt zeigt er den Wanderer aus dem Splash Screen, dazu einen Satelliten, der ihm Signale schickt, und „Searching for GPS…“. Das Bild erzeugt ein Skript aus der Vorlage: Jeder Pixel bekommt eine Klasse, jede Klasse eine der sieben Farben oder ein 4 × 4 Bayer-Muster, der Farbverlauf im Schriftzug wird zu roten, orangen und gelben Bändern. Lauflängenkodiert braucht das Bild etwa 12 KB im Flash und wird beim Zeichnen dekodiert, ohne RAM-Puffer. Sobald der erste Fix kommt, löst die GPS-Task einen Refresh aus und die Karte ersetzt das Bild.

Was als Nächstes kommt? Light Sleep, sobald die Peripherie dafür bereit ist, und echte Touren mit der Vektorkarte. Das Navi ist jetzt nach Jahren zum ersten Mal an dem Punkt, an dem ich es einfach mitnehmen will. Wer mitbauen will: Firmware und App liegen auf GitHub.

Wie das Wandernavi schneller zeichnet und länger durchhält

Balkendiagramm der Renderzeiten vorher und nachher, kompletter Render von 269 auf 105 Millisekunden
Gemessen auf dem ESP32-S3 mit einem Track aus 7302 Punkten. Die Zahlen stammen aus den Commits vom 10. Oktober.

Bei einem E-Paper, das 17 Sekunden für einen Refresh braucht, ist Rendergeschwindigkeit doch egal? Nicht ganz. Jede Millisekunde, in der die CPU auf 240 MHz rechnet, kostet Akku. Und ein Wandernavi, das nach einem halben Tag leer ist, ist ein Briefbeschwerer. Also habe ich am 10. Oktober einen Bildaufbau auseinandergenommen und nachgeschaut, wo die Zeit bleibt.

Wo die Zeit bleibt

Der größte Brocken war die Mathematik. Jeder Bildaufbau hat für jeden Wegpunkt des Tracks die Kachel berechnet, mit pow, log, tan und cos in doppelter Genauigkeit. Das macht der ESP32-S3 in Software, und bei einem langen Track hat das rund eine Sekunde pro Render gekostet. Jetzt wird die Position eines Wegpunkts einmal beim Laden als 32-Bit-Festkommawert auf der Weltkarte gespeichert. Kachel und Pixel einer Zoomstufe sind dann nur noch Bitshifts. Die Position des Geräts nutzt dieselbe Rechnung, damit Marker und Track auf denselben Pixeln landen.

Der zweite Brocken war das Zeichnen Pixel für Pixel. Den Framebuffer zu leeren hat jeden der 268 800 Pixel einzeln gesetzt und 157 ms gedauert, mit memset sind es 3 ms. Kartenkacheln werden jetzt auf das Display zugeschnitten, bevor sie gezeichnet werden, und zeilenweise kopiert, zwei Pixel pro Byte. Pixel für Pixel lag jeder Schreibzugriff in einer anderen Cache-Line des PSRAM. Das Ergebnis ist Byte für Byte identisch, das habe ich auf dem Gerät geprüft.

Dazu kleinere Dinge. Der Track wird pro Render nur noch einmal durchlaufen statt fünfmal, jeder Durchlauf durch die Liste im PSRAM kostete 6 ms, auch wenn er nichts tat. Das Höhenprofil mit 7302 Punkten wird einmal in einen Canvas gezeichnet und danach nur noch kopiert. Ein kompletter Render mit GPS-Fix und Track ging so von 269 ms auf 172 ms. Den Rest bis 105 ms holt der Release-Build mit -O2 und ohne Heap-Poisoning. Einen Track laden dauert jetzt 1,1 statt 1,8 Sekunden.

Weniger Takt, weniger Funk

Mit aktivem Power Management läuft die CPU nur noch mit 240 MHz, wenn eine Task sie braucht, sonst mit 80 MHz. Tiefer geht es nicht, weil Bluetooth und der UART des GPS am APB-Takt von 80 MHz hängen. Auf dem Kartenbildschirm mit Fix läuft die CPU 10 % der Zeit auf vollem Takt. Light Sleep bleibt vorerst aus, dafür sind UART, Bluetooth und die Versorgungspins noch nicht bereit.

Bluetooth war der nächste Verbraucher. Ein Handy, das 5 Minuten weder liest noch schreibt, wird jetzt getrennt. Danach sendet India Navi nur noch für 5 Sekunden Advertising, und zwar immer dann, wenn das Display ohnehin neu zeichnet. Dazwischen ist der Bluetooth-Stack komplett gestoppt. Die App weiß das und wartet beim Wiederverbinden auf das nächste Display-Update.

Auch wenn der Akku leer ist, passiert jetzt etwas Sinnvolles. Nach drei Messungen in Folge bei 5 % ohne Ladegerät zeigt das Display „Battery empty“, das GPS-Modul geht in Standby, damit es seine Uhr behält, und das Gerät in Deep Sleep. Das Bild bleibt auf dem E-Paper stehen, ohne Strom zu brauchen. Kommt ein Ladegerät dazu, startet die Karte von selbst, sobald der Akku sich erholt hat.

Aufräumen, bevor es zu spät ist

Am gleichen Wochenende habe ich die Task-Architektur umgebaut. Vorher deklarierte eine tasks.h die Schnittstelle jedes Moduls, main.c enthielt die Power-Task und den Button, und Event-Queue, SD-Mutex und Akkuwerte waren globale Variablen, die jeder direkt benutzte. Jetzt hat jedes Modul einen eigenen Header in include/navi/ und startet seine Task selbst. Die Event-Queue und der SD-Mutex sind privat, die GUI fragt den Status der Module ab, statt dass die Tasks GUI-Objekte beschreiben. Von außen sieht man davon nichts, aber der Umbau hat einen Fehler ans Licht gebracht: Ein Tastendruck, während das Ausschaltbild noch gezeichnet wurde, hat das Gerät direkt wieder eingeschaltet.

Apropos Taste: Ein kurzer Druck auf der Karte hat das Gerät manchmal ausgeschaltet statt den Zoom zu wechseln. Jedes Prellen belegte einen Platz in der Event-Queue der Größe 6, das Loslassen ging verloren und der Timer für den langen Druck lief ab. Jetzt meldet der Interrupt nur noch eine Änderung, und die Haupttask liest den Pegel nach 30 ms. Manchmal ist Embedded eben doch noch Elektronik.

Warum ein Pairing-Code 17 Sekunden zu spät kam

WLAN ist toll, um ein paar hundert Kartenkacheln auf die SD-Karte zu schieben. Für alles andere ist es zu umständlich. Niemand will unterwegs einen QR-Code scannen, nur um die Trackfarbe zu ändern. Seit dem 6. Oktober ist India Navi deshalb ein Bluetooth-LE-Peripheral, solange es eingeschaltet ist. Die Firmware nutzt NimBLE auf dem ESP32-S3, die App react-native-ble-plx.

Diagramm: Wander Navi und India Navi sprechen über Bluetooth LE für Einstellungen, Zeit, Position und Updates, über den WLAN-Access-Point für Kacheln, Tracks und Firmware
Zwei Kanäle, zwei Aufgaben: Bluetooth für die kleinen Dinge, die oft passieren, WLAN für die großen Datenmengen.

Über Bluetooth schickt die App die Uhrzeit und die Position des Handys. Beides geht weiter an das GPS-Modul (PMTK740/PMTK741), damit es die Satelliten schneller findet. Bis zum ersten eigenen Fix zeigt die Karte die Handyposition, ins Log kommt sie nie. Außerdem lässt sich der Access Point ein- und ausschalten, der Track und das Höhenprofil ein- und ausblenden und das Update-Intervall des Displays zwischen 30 Sekunden und 10 Minuten wählen. Das WLAN-Passwort geht nie über Bluetooth, das liest das Handy weiterhin vom Display.

Wenn die Theorie auf das E-Paper trifft

Die erste Version war in Teilen von einem Agenten geschrieben, ohne ESP-IDF-Toolchain. Im Commit steht das auch offen drin: Host-Tests grün, der ESP-Code nur gegen Stub-Header geprüft. Auf der Hardware ging dann erst einmal gar nichts. Drei Fehler, alle lehrreich:

  1. Bluetooth startete nicht, weil der Controller nur noch 2 KB internes RAM fand. Der Framebuffer mit 134 KB liegt jetzt im PSRAM und wird Byte für Byte ohne DMA verschickt.
  2. Der Pairing-Code erschien zu spät. Das Handy hat 30 Sekunden zum Pairen, ein Refresh des 7-Farben-Displays dauert rund 17 Sekunden. Jetzt wird der Code gewählt, sobald ein Handy verbindet, und für den nächsten Versuch behalten.
  3. Ein gepaartes Handy kam nicht wieder rein. Ein Kommando im Connect-Event hat den Host blockiert, während der Controller auf die Antwort zur Schlüsselanfrage wartete.

Ein vierter Fehler kam etwas später. Android schickt nach dem Wiederverbinden eine Indication, und NimBLE ohne GATT-Client antwortet mit „Request Not Supported“. Android hielt das für die Antwort auf die MTU-Anfrage der App, und die App gab 0,2 Sekunden nach dem Verbinden auf. Die Central-Rolle bringt den GATT-Client mit. Das Image wird dadurch 16 KB größer, der freie Heap bleibt gleich.

Firmware-Update aus der Hosentasche

Das Firmware-Update funktioniert jetzt über WLAN und über Bluetooth. Ein getaggter Release auf GitHub hängt die Firmware automatisch an. Die App bietet die neueste Version aus den GitHub Releases direkt zum Update an. Damit ein kaputtes Image das Gerät nicht unbrauchbar macht, ist Rollback aktiv: Eine neue Firmware gilt erst als bestätigt, wenn sie 60 Sekunden gelaufen ist. Stürzt sie vorher ab, startet der Bootloader die alte Version.

Tracks aufzeichnen

Das alte log.gpx, das einfach alles mitgeschrieben hat, ist Geschichte. Aufzeichnungen startet und stoppt man jetzt in der App. Jede Aufzeichnung landet als eigene Datei in TRACKS/ auf der SD-Karte, benannt nach der Startzeit, mit einem neuen Segment nach jedem Neustart. Die App listet und löscht die Aufzeichnungen über Bluetooth und lädt sie über WLAN herunter.

Statusleiste von India Navi mit Akku, Nordpfeil, Access-Point- und Bluetooth-Symbol
Die Statusleiste zeigt Akku, Nordpfeil, Access Point und die Bluetooth-Rune. Mit zwei Punkten daneben ist ein Handy verbunden.

Zwei Erkenntnisse nehme ich mit. Erstens: Ein Agent kann ein Protokoll sauber entwerfen und testen, die Fehler am echten Gerät findet er ohne Gerät nicht. Die Arbeitsteilung war deshalb klar: Er schreibt den Code, ich probiere ihn am Gerät aus. Zweitens: Bei einem Display, das 17 Sekunden für ein Bild braucht, muss man bei jedem Timeout im Protokoll diese 17 Sekunden einplanen.

Endlich eine App für das Wandernavi

App-Icon von Wander Navi mit Nordpfeil auf grünem Grund und der Splash Screen mit Schriftzug
Links das App-Icon, rechts der Splash Screen. Der Nordpfeil kommt direkt aus den Icons der Firmware.

Das größte Problem von India Navi war nie die Firmware. Es war der Weg der Karte auf die SD-Karte. Bisher hieß das: GPX-Datei nehmen, mit dem Converter die Kacheln für Zoomstufe 14 und 16 herunterladen, jede Kachel in das 4-Bit-Format des Displays umrechnen, alles in die richtige Ordnerstruktur kopieren, SD-Karte raus, SD-Karte rein. Das macht man genau einmal mit Begeisterung.

Das Repository IndiaNavi_App gibt es seit Oktober 2022. Es enthielt genau einen Commit: das, was create-expo-app erzeugt. Vier Jahre später wurde daraus eine echte App. Zuerst das Update von Expo SDK 46 auf 57, also React Native 0.86 und React 19. Der alte GPX-Parser brauchte Node-Bibliotheken, die sich in React Native nicht bündeln lassen, also flog er raus. Und die App bekam einen neuen Namen: Wander Navi.

Vom Track zur SD-Karte

Die App öffnet eine GPX-Datei über den Dateidialog oder über „Öffnen mit“ auf Android. Sie berechnet den Bereich um den Track, lädt die Kacheln und rechnet sie in die sieben Farben des Displays um. Die Farbumrechnung ist aus dem alten Rust-Converter portiert. Mit dem Standardfilter ist die Ausgabe Byte für Byte identisch mit der Rust-Version. Dabei ist auch ein alter Fehler aufgefallen: Der Converter hat den Rand um den Track nicht auf allen Seiten berücksichtigt.

Inzwischen geht es auch ohne Track. Die Startansicht ist eine Karte, die man verschieben und zoomen kann, mit Suche nach Orten und Koordinaten. Der Bereich um das Kreuz in der Mitte wird geladen. Als Kartenquelle ist Thunderforest Outdoors voreingestellt, der API-Key hängt die App unsichtbar an jede Anfrage.

Sieben Farben sind nicht viel

Ein E-Paper mit Schwarz, Weiß, Grün, Blau, Rot, Gelb und Orange zeigt keine Pastelltöne. Im Filterbildschirm der App lassen sich deshalb zwei Displayfarben in beliebigem Anteil mischen, in Schritten von 1/16. Gemischt wird mit einem 8 × 8 Bayer-Muster, das über die Kachelgrenzen hinweg weiterläuft. Linien und Schrift würden im Muster zerfallen. Pixel, die deutlich dunkler sind als ihr hellster Nachbar, werden deshalb in der dunkleren Farbe durchgezogen. Original und Umrechnung kann man nebeneinander vergleichen, per Pinch bis auf den einzelnen Pixel. Den Filter kann man als JSON exportieren und wieder importieren.

Übertragung per WLAN

Damit das Kopieren der SD-Karte entfällt, hat die Firmware einen WLAN-Access-Point bekommen. Hängt India Navi am Ladegerät, öffnet es ein Netz IndiaNavi-XXXX mit WPA2 und zeigt Name und Passwort als QR-Code auf dem Display. Die App scannt den Code, verbindet sich und lädt die Dateien über eine kleine HTTP-API hoch. Weil das Netz kein Internet hat, braucht es dafür ein eigenes Expo-Modul, das die App an genau dieses WLAN bindet.

Display von India Navi mit Fortschrittsanzeige Upload 8 von 12 Dateien
Die App kündigt die Anzahl der Dateien an, das Display zeigt den Fortschritt. Screenshot aus dem Linux-Simulator der Firmware.

Kacheln, die schon auf der Karte liegen, werden übersprungen. Hochgeladen wird über zwei Verbindungen mit Wiederholung, track.gpx kommt als letzte Datei. Die Firmware schreibt jede Datei erst in eine .tmp und benennt sie um, wenn alle Bytes angekommen sind. Ohne Ladegerät schaltet sich das WLAN nach 10 Minuten ohne Nutzung ab.

Eine App, die aussieht wie das Gerät

Am 5. Oktober bekam die App ein Redesign nach dem Vorbild des Displays: nur die sieben Farben des E-Papers, flache Flächen mit schwarzen Rändern und harten Schatten. Die Icons erzeugt ein Skript direkt aus den Quellen der Firmware. Seitdem sieht die App aus wie ein Stück India Navi auf dem Handy. Mit einem GitHub Workflow, der bei jedem Push ein APK baut, ging es dann in kurzen Schritten bis zur Version 1.4.0 am 25. Oktober.

India Navi lag zwei Jahre in der Schublade

Der letzte Commit im India Navi Repository vor der langen Pause trägt das Datum 31. Mai 2024. Power Management für Light Sleep, eine neue Batterieanzeige, GPS-Punkte auf die SD-Karte loggen. Danach war Schluss. Das Studium, die Gruppenleitung, das Leben. Das Gerät lag in der Schublade, die Firmware auf GitHub und beide haben geduldig gewartet.

Balkendiagramm der Commits pro Quartal in Firmware und App von 2021 bis 2026, mit gut zwei Jahren Pause
Commits pro Quartal auf main in IndiaNavi_Firmware und IndiaNavi_App. Nach Mai 2024 war gut zwei Jahre Ruhe, im Oktober 2026 kamen 123 Commits zusammen.

Für alle, die India Navi nicht mehr auf dem Schirm haben: India Navi ist ein Offline-Navi zum Wandern. Kein Turn-by-Turn, kein „in 200 Metern links abbiegen“. Es zeigt die eigene Position auf einer Karte von der SD-Karte, dazu einen GPX-Track mit Höhenprofil, und aktualisiert das Bild jede Minute. Herzstück ist ein ESP32-S3 mit 16 MB Flash und 8 MB PSRAM, dazu das 7-Farben E-Paper von Waveshare mit 5,65 Zoll und 600 × 448 Pixeln und ein Quectel L96 als GNSS-Modul. Nicht verirren statt navigieren, das war von Anfang an das Ziel. Wer die Vorgeschichte nachlesen will: das Update vom Juni 2021 und die Firmware 2.0r2.

Der Splash Screen von India Navi: Schriftzug und ein Wanderer mit Rucksack und Stock
Der Wanderer mit Rucksack und Stock ist seit Jahren das Gesicht des Projekts. Die Vorlage liegt jetzt auch in der Dokumentation des Repositories.

Erst einmal wieder bauen können

Wer ein Embedded-Projekt zwei Jahre liegen lässt, kennt das: Nichts baut mehr. Die PlatformIO-Plattform hatte sich weiterbewegt, ESP-IDF auch. Der erste Schritt Ende September war deshalb das Update auf ESP-IDF 6.1 (espressif32 7.1.3) samt GCC 15. Klingt nach Versionsnummer hochdrehen, war es aber nicht. FatFs alloziert seine Puffer jetzt nur noch, wenn der Zeiger im FIL-Objekt NULL ist. Ohne Nullinitialisierung ist das Gerät beim Booten abgestürzt. Dazu ein Pufferüberlauf in ctime_r(), das 26 Byte braucht, und eine Handvoll geänderter API-Aufrufe für SD-Karte, Wakeup-Ursachen und HTTP-Client.

Ein Speicher-Audit, das wehgetan hat

Direkt danach habe ich den Code einmal komplett auf Speicherfehler durchsucht. Die Liste ist lang und liegt als MemoryAudit.md im Repository. Ein paar Highlights: Der NMEA-Parser hat seine Plugins als Zeiger auf den Stack des Aufrufers gespeichert. Ein PMTK001-Ack hat einen Funktionszeiger außerhalb des Arrays aufgerufen. Fehlte eine Kartenkachel, blieb der SD-Mutex gesperrt und das Gerät stand still. Der GPX-Parser hat unvollständige Dateien für immer neu geparst. Und das Höhenprofil hatte einen Heap-Überlauf. Dazu Lecks im OTA-Puffer alle 30 Sekunden und bei jedem Aus- und Einschalten.

Der spannendste Fund war aber kein Bug im klassischen Sinn. Ein GPX-Track mit 154 KB hat den kompletten internen RAM gefressen, 59 Byte waren noch übrig. Jeder Trackpunkt ist eine kleine Allokation, und kleine Allokationen landen im internen RAM. Dann konnte der SD-Kartentreiber seine DMA-Puffer nicht mehr anlegen und die Karte blieb weiß. Seitdem liegen Trackpunkte, Höhenprofil und Dateipuffer im PSRAM. Mit dem gleichen Track bleiben jetzt 30 KB internes RAM frei.

Altlasten abarbeiten

Am 6. Oktober habe ich mir dann die offenen Issues vorgenommen. Manche davon lagen schon peinlich lange herum. Das log.gpx wurde bei jedem Boot geleert und nie beschrieben, weil die GPS-Task auf ein Flag gewartet hat, das niemand gesetzt hat. Die Uhrzeit aus dem GPS war nur in Zeitzonen mit UTC+1 richtig, weil irgendwann jemand (ich) eine Stunde draufaddiert hat. Das Höhenprofil eines Tracks unter Meeresniveau wurde nie gezeichnet, weil der Wertebereich vorzeichenlos war. Und der nächste Wegpunkt war nur dann der nächste, wenn er auf beiden Achsen näher lag. Sechzehn Issues an einem Tag geschlossen, dazu ein Release v3.0.0. Die letzte Version davor war v2.1.1 aus dem Juni 2023.

Wie das in der Zeit möglich war

Ehrlich gesagt: Dieses Tempo hätte ich neben dem Job alleine nicht geschafft. Wer in die Commit-Historie schaut, sieht an vielen Stellen „Claude“ als Autor oder eine Claude-Session im Commit-Text. In meinem letzten Beitrag habe ich gefragt, wie ihr mit euren KI-Agenten redet. India Navi ist mein Versuchsfeld dafür geworden. Das Muster, das sich bewährt hat: Ich beschreibe das Problem mit Kontext und dem Ziel dahinter, der Agent liefert einen Pull Request, ich teste auf echter Hardware und schreibe dann die Fixes, die nur am Gerät auffallen. Nicht alles hat funktioniert. Ein Agent hat parallel angefangen, die ganze Firmware nach Rust zu portieren. Das liegt in eigenen Branches und ist nicht in main gelandet. Rust ist trotzdem ins Projekt gekommen, aber anders, dazu später mehr.

In den nächsten Beiträgen geht es um die App, um Bluetooth, um schnelleres Rendern und um Vektorkarten. In einem Monat ist ganz schön viel passiert.

IndiaNavi Update 4.6.2021

Firmware Update over the Air

Heute habe ich einen großen Teil der Remote Update Funktion in Betrieb genommen. Es ist jetzt also möglich ein Firmwareupdate von einem HTTP Webserver einzuspielen. HTTPS funktioniert noch nicht, die TLS Engine lässt sich noch nicht richtig initialisieren. Ein Update dauert ungefähr 10 Sekunden.

Benutzeroberfläche

Auf der GUI Oberfläche wird jetzt ein Icon angezeigt, wenn das Gerät sich mit einem WiFi-Netzwerk verbunden hat.

Karten Tiles generieren

Der Konverter, der OSM Tiles von unserem Server lädt und in .raw Bilder für das Display umwandelt kann das jetzt parallel tun und läuft damit wesentlich schneller. Die Mapping Funktion hat leider eine Konvertierzeit von ca. 500ms pro Kachel. Hier kann also noch einiges optimiert werden.

Die Bilder zeigen das Display mit den Farben und auf der rechten Seite die Farben, wie sie im PNG zu sehen sind. Transparent wurde auf schwarz gemappt, daher der dunkle Balken auf dem Display.