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.