Heltec WiFi LoRa 32 V4 mit MeshCore-Companion-Firmware und angeschlossenem GPS-Modul auf einer blauen Werkbankmatte. Im Hintergrund sind Flash-Befehle, Bluetooth-Kopplung mit dem Smartphone und ein dezentrales LoRa-Mesh-Netzwerk dargestellt.

Im ersten Teil ging es um die Theorie: was LoRa ist, was MeshCore daraus macht und warum ich hier bei MeshCore gelandet bin. Jetzt wird es praktisch. Bevor ein Repeater aufs Dach kommt, braucht man erst einmal ein Gerät, mit dem man selbst im Mesh mitreden kann. Bei MeshCore heißt das Companion: ein kleiner LoRa-Node, der per Bluetooth am Smartphone oder am PC hängt und für dich funkt. Ich habe dafür einen Heltec WiFi LoRa 32 V4 genommen. Es gibt natürlich noch mehr und auch Geräte, die kein Smartphone brauchen aber ich mag es mit dem Smartphone zusammen.

Was in der Box steckt

Mein erstes Board war ein einzelner Heltec V4 mit GPS. In der kleinen Plastikbox lagen die Platine, eine Federantenne mit u.FL-Stecker (oft IPEX genannt), ein GPS-Modul mit achtadrigem Kabel, zwei JST-Kabel für Akku oder Solarzelle und zwei Stiftleisten, die nicht eingelötet sind. Auf der Box steht „LoRa Band, HP 863-928 MHz“, auf der Platine selbst HTIT-WB32LAF V4.3. Das ist die Revision 4.3 mit dem Frontend KCT8103L, also einem zusätzlichen Leistungsverstärker (PA) und rauscharmen Vorverstärker (LNA) vor dem eigentlichen LoRa-Chip SX1262. Der Rechner auf dem Board ist ein ESP32-S3 mit 16 MB Flash und 2 MB PSRAM, dazu kommen ein kleines OLED, USB-C und zwei Taster.

Ausgepackter Heltec WiFi LoRa 32 V4 auf einer blauen Werkbankmatte: Platine mit angeschlossenem GPS-Modul und Federantenne, zwei JST-Kabel mit roten und schwarzen Adern, eine Tüte mit Stiftleisten und die blaue Plastikbox mit der Aufschrift WiFi LoRa 32 V4
Lieferumfang des Heltec V4: Board, GPS-Modul, Federantenne, zwei JST-Kabel und Stiftleisten. Achtung, auf diesem Foto steckt die Antenne noch an der 2,4-GHz-Buchse. Doof ich bin.

Später habe ich mir noch das Komplettset geholt: Heltec V4 ohne GPS, dafür mit fertigem Gehäuse und 3000-mAh-LiPo. Auf dem läuft heute mein persönlicher Node „Kernel-Error“. Wer einfach nur schnell ins Mesh will, ist mit diesem Set besser bedient als ich mit meinem nackten Board, denn Gehäuse und Akku sind dann schon erledigt. Wie der erste Heltec danach ins Auto gewandert ist, steht weiter unten.

Ab Werk war auf dem Board Meshtastic 2.7.22 installiert. Ein meshtastic --info über USB lief sofort. Die Region stand auf UNSET, so sendet das Board gar nicht, bevor man eine Region gesetzt hat. Das finde ich vernünftig, so schreit das Ding nicht schon in die Luft, ganz ohne config. Ich wollte aber MeshCore, also musste die Firmware runter.

Achtung: Der Heltec V4 hat zwei identische IPEX-Buchsen. LoRa ist die Buchse an der Spitze der Platine, auf der Oberseite direkt neben dem GNSS-Stecker. Die andere sitzt unten neben dem ESP32-S3 und gehört zum 2,4-GHz-Teil. Und schick nie ein Paket raus, ohne dass an der LoRa-Buchse eine Antenne oder ein Abschlusswiderstand hängt. Die Sendeleistung muss irgendwo hin, sonst kommt sie zurück und kann die PA beschädigen. Also erst die Antenne, dann USB. Wenn ihr das dann noch direkt richtig anschließt und nicht, wie ich, einfach mal fummelt….

Heltec V4 von oben am USB-C-Kabel, das OLED zeigt Pin: 182366, die Federantenne steckt an der IPEX-Buchse an der Spitze der Platine direkt neben dem farbigen GNSS-Kabel, rechts das grüne GPS-Modul
So ist es richtig: Die Federantenne steckt an der LoRa-Buchse an der Spitze, direkt neben dem GNSS-Kabel. Auf dem Display steht der BLE-PIN fürs Pairing.

Erst sichern, dann flashen

Bevor ich irgendetwas überschreibe, ziehe ich ein komplettes Abbild des Flashs. Damit komme ich jederzeit zurück auf den Werkszustand. Klar könnte ich einfach wieder die Meshtastic Firmware drüber brennen. Ich bin aber schon ein paar mal mit so etwas auf die Nase gefallen und die drei Minuten für eine Sicherung habe ich dann doch. Der ESP32-S3 meldet sich über seinen nativen USB-Port als /dev/ttyACM0, einen USB-Seriell-Wandler gibt es auf dem Board nicht. Mit esptool schaue ich zuerst, was überhaupt verbaut ist, und lese dann die vollen 16 MB aus. Das hat bei mir knapp 100 Sekunden gedauert.

$ esptool --port /dev/ttyACM0 flash-id
Chip type:          ESP32-S3 (QFN56) (revision v0.2)
Features:           Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 2MB (AP_3v3)
Detected flash size: 16MB

$ esptool --port /dev/ttyACM0 --baud 921600 read-flash 0 ALL backup/heltec-v4-factory-16MB.bin

Die Zeile mit dem PSRAM ist wichtig für die Wahl der richtigen Firmware. MeshCore baut für den V4 mehrere Varianten: heltec_v4, heltec_v4_r8 und jeweils noch _tft-Versionen. Das „R2“ im Produktnamen steht für den ESP32-S3R2 mit 2 MB PSRAM, und das bestätigt esptool. Also heltec_v4. Die r8-Variante benutzt andere Pins für Display-Versorgung und LED, mit dem falschen Image bleibt das Display dunkel. Ratet doch mal, woher ich das so genau sagen kann? Nicht, dass noch das Gefühl aufkommt, dass ich hier Ahnung von dem hätte, was ich mache. Wie immer: Korrigiert mit bitte. Ich will was lernen.

Die Releases auf GitHub sind nach Rolle getrennt: Companion, Repeater und Room Server haben eigene Tags. Für das Handgerät brauche ich den Companion mit Bluetooth, also eine Datei nach dem Muster heltec_v4_companion_radio_ble-<version>-merged.bin. „merged“ heißt, dass Bootloader, Partitionstabelle und Anwendung in einem Image stecken, das ab Adresse 0x0 geschrieben wird. Die SHA-256-Summe habe ich gegen den Digest des GitHub-Assets geprüft, dann den Flash gelöscht, damit keine Meshtastic-Reste liegen bleiben, und das Image geschrieben (vergesst das mit dem Löschen nicht, kommt sonst schnell mal spannendes bei herum):

$ gh release download companion-v1.17.1 -R meshcore-dev/MeshCore -p 'heltec_v4_companion_radio_ble-v1.17.1-d929643-merged.bin'
$ sha256sum heltec_v4_companion_radio_ble-v1.17.1-d929643-merged.bin
$ esptool --port /dev/ttyACM0 erase-flash
$ esptool --port /dev/ttyACM0 --baud 921600 write-flash 0x0 heltec_v4_companion_radio_ble-v1.17.1-d929643-merged.bin

Die 1,3 MB waren in unter fünf Sekunden drauf. Wer keine Lust auf die Kommandozeile hat, kann auch den Web-Flasher von MeshCore im Browser nehmen. Den habe ich selbst nicht benutzt, das Backup vorher solltest du aber in beiden Fällen machen.

Eine Sache aus dem Quellcode der V4-Variante muss man kennen: Dort steht LORA_TX_POWER=10 mit dem Kommentar, dass daraus am Ende 22 dBm werden. Die Sendeleistung in der App ist auf diesem Board die Ausgangsleistung des SX1262, die externe PA legt laut diesem Kommentar etwa 12 dB obendrauf. Eine „22“ in der App bedeutet beim Heltec V4 also nicht 22 dBm an der Antenne. Mein Companion bleibt deshalb auf 10, dem Wert, den MeshCore für den V4 ohnehin mitbringt. Was das für die Grenzwerte heißt, rechne ich in Teil 4 durch. Ja, hehe… Der cliffhanger bleibt auch hier.

Pairing mit der App

Nach dem Flashen bootet das Board direkt in MeshCore und zeigt auf dem OLED einen sechsstelligen PIN. Den gibt man beim Bluetooth-Pairing in der MeshCore-App ein. Der PIN wird bei jedem Start neu gewürfelt. Der Companion nimmt immer nur einen BLE-Client gleichzeitig an. Hängt das Handy dran, findet der PC das Gerät nicht, und umgekehrt. Was mich bestimmt 10 min. meines Lebens gekostet hat, das zu realisieren.

Beim ersten Verbinden fragt die App nach einem Funk-Preset. Hier im Rheinland funkt die Community mit „EU/UK (Narrow)“, das sind 869,618 MHz, 62,5 kHz Bandbreite, Spreading Factor 8 und Coding Rate 8. Welches Preset in deiner Gegend gilt, solltest du bei der lokalen Community nachschauen, denn mit anderer Frequenz, Bandbreite oder anderem Spreading Factor hörst du schlicht niemanden. Die Coding Rate steht dagegen im LoRa-Header, ein Empfänger mit Coding Rate 5 versteht also auch Pakete mit Coding Rate 8. Danach bekommt der Node einen Namen. Meiner heißt „Kernel-Error“.

MeshCore-App, Einstellungen des Nodes Kernel-Error: Avatar mit dem Buchstaben K, darunter der verkürzte Schlüssel b2035736...84ce8610, Felder für Name und öffentlichen Schlüssel, Breiten- und Längengrad 0.0, Häkchen Position in Advert teilen nicht gesetzt, Preset EU/UK (Narrow) mit 869.618 MHz und 62.5 kHz, unten ein Info-Hinweis zur Iconfarbe
Die Node-Einstellungen in der App. Unter dem Namen steht die Kurzform des öffentlichen Schlüssels, die Position wird nicht im Advert geteilt.

Erstes Advert, erste Nachrichten

Ein Advert ist die Visitenkarte eines Nodes: Name, öffentlicher Schlüssel, Typ und optional eine Position, signiert mit dem privaten Schlüssel, aber nicht verschlüsselt. Ein Companion verschickt so etwas nur, wenn du es auslöst. Als Flood-Advert geht es über die Repeater in die Fläche, als Zero-Hop-Advert erreicht es nur die direkte Nachbarschaft.

Nach meinem ersten Flood-Advert war die Kontaktliste leer. Das ist normal und hat mich trotzdem kurz irritiert. Ein Advert holt keine Liste aus dem Netz, Kontakte tauchen erst auf, wenn du die Adverts der anderen hörst, und Repeater senden die nur in längeren Abständen. Etwas Geduld hilft also. Bei mir hat es zusätzlich etwas länger gedauert, weil die Antenne an der falschen Buchse steckte, siehe oben. Ja, ich bin einer von denen, die auf dem ersten Foto die Antenne an der falschen Buchse hatten.

Ob eine Nachricht wirklich rausgegangen ist, zeigt dir die App selbst. Unter jeder eigenen Kanalnachricht zeigt sie an, wie viele Repeater sie wiederholt haben. Hört dein Companion einen Repeater deine Nachricht weitersenden, ist sie im Mesh. Diese „heard repeats“ empfiehlt auch Mesh Rheinland zum Testen. Bei mir stand unter dem ersten „geht“ im Public-Kanal „1 Repeater empfangen“, drumherum liefen Testnachrichten aus Velbert, Euskirchen und Lengerich.

MeshCore-App im Kanal Public: Nachrichten anderer Nutzer wie Test aus velbert und Test aus Euskirchen, rechts die eigene blaue Nachricht @DE_NW_SWE01 geht mit dem Vermerk 1 Repeater empfangen und einem Häkchen, unten das Eingabefeld mit 0/130 Zeichen
Der Moment, in dem es funktioniert: „1 Repeater empfangen“ unter der eigenen Nachricht heißt, dass ein Repeater sie weitergesendet hat.

Avatar per Emoji

Ein Profilbild kann man nicht hochladen. Die App erzeugt die Farbe des Icons aus dem Namen und zeigt den ersten Buchstaben. Steht am Anfang des Namens ein Emoji, nimmt die App das statt des Buchstabens. Ich habe mich zuerst „😈 Kernel-Error“ genannt, das kommt dem FreeBSD-Daemon am nächsten, einen echten Beastie gibt es als Emoji leider nicht. Falls jemand ein Unicode-Zeichen für Beastie kennt, sagt gerne Bescheid, ich schaue dann nach, ob die App es mitmacht. Der Name darf maximal 31 Byte lang sein, ein Emoji frisst davon meist vier. Nach dem Umbenennen ein Flood-Advert schicken, damit die anderen den neuen Namen lernen. Am Schlüssel ändert sich nichts. Das kleine OLED kann übrigens keine Emojis darstellen und zeigt nur den Text.

MeshCore-App, Kontaktliste mit 32 Einträgen, oben in der Kopfzeile 100 Prozent Akku und der Name Kernel-Error mit Teufels-Emoji, darunter Repeater wie DE-NW-Wpt.-Vohwinkel LZ, DE-NW-BM-GlessenerHoehe, DE-NW-Wuppertal-West und der Raum Hi @ meshrheinland.de, jeweils mit verkürztem Schlüssel und Uhrzeit
Nach ein paar Stunden füllt sich die Kontaktliste. Oben steht der Node mit Emoji im Namen.

Kontakte und warum der Schlüssel zählt

Wie in Teil 1 beschrieben, ist die Identität eines Nodes sein Schlüsselpaar und der Name nur ein Etikett. Die App zeigt deshalb neben jedem Kontakt eine Kurzform des öffentlichen Schlüssels, die ersten und letzten acht Hex-Zeichen, bei mir <b2035736...84ce8610>. Wenn du sicher sein willst, dass du wirklich mit der Person schreibst, die du meinst, vergleich diesen Fingerprint über einen anderen Weg: persönlich, am Telefon oder über eine Webseite, der du vertraust. Erst dann ist der Kontakt verifiziert.

Standardmäßig wird jedes gehörte Advert zum Kontakt. Das wird schnell viel: Die Kontakttabelle ist auf dem Heltec-BLE-Build auf 350 Einträge begrenzt, und im Rheinland gibt es viele Repeater. Ist die Tabelle voll und „älteste überschreiben“ aus, landen neue Kontakte nicht mehr in der Liste. Die Firmware reicht das Advert dann zwar noch an die App weiter und meldet, dass der Speicher voll ist, speichert den Kontakt aber nicht. Ich habe deshalb auf selektives Hinzufügen umgestellt: Chats, Repeater und Room Server ja, Sensoren nein, maximal drei Hops entfernt, älteste überschreiben an. Favoriten werden beim Überschreiben nicht angefasst, die eigenen wichtigen Kontakte sollte man also als Favorit markieren.

Die App kann die komplette Node-Konfiguration als JSON exportieren und wieder importieren. Achtung: In diesem Export steckt der private Schlüssel, und dazu die zuletzt bekannte Position, auch wenn du sie nicht im Advert teilst. Behandle die Datei wie einen privaten SSH-Schlüssel. Wer ihn hat, kann sich als dein Node ausgeben und deine Direktnachrichten lesen. Hier deshalb nur ein bereinigter Auszug mit den Teilen, die wirklich etwas einstellen:

"radio_settings": {
  "frequency": 869618,
  "bandwidth": 62500,
  "spreading_factor": 8,
  "coding_rate": 8,
  "tx_power": 10
},
"other_settings": {
  "manual_add_contacts": 1,
  "advert_location_policy": 0
},
"auto_add_settings": {
  "auto_add_chat": true,
  "auto_add_repeater": true,
  "auto_add_room_server": true,
  "auto_add_sensor": false,
  "overwrite_oldest": true,
  "auto_add_max_hops": 4
}

manual_add_contacts: 1 schaltet das selektive Hinzufügen ein, erst dann wirken die auto_add_*-Schalter. auto_add_max_hops: 4 bedeutet bis zu drei Hops, 0 hieße ohne Grenze. advert_location_policy: 0 hält die Position aus dem Advert raus. Ein Companion, der meistens zu Hause liegt, würde sonst die Wohnadresse ins Netz senden. Wenn du so etwas übernehmen willst, setz die Werte in der App oder kopier nur diese Blöcke. Einen fremden Export mitsamt Schlüssel zu importieren, klont eine fremde Identität, und zwei Nodes mit demselben Schlüssel bringen das Routing für beide durcheinander.

Die Einstellungen, auf die es ankommt

Zwei Einstellungen stehen nicht im Export und müssen von Hand gesetzt werden: die Größe der Pfad-Hashes und der Default-Scope. Den Pfad-Hash habe ich auf 2 Byte gestellt, damit sich Repeater auf dem Weg seltener verwechseln lassen. Der Default-Scope steht auf #de, weil die Repeater hier ungescopte Floods ignorieren. Beides erkläre ich ausführlich in Teil 6. Die Funkwerte entsprechen dem Preset, zur Sendeleistung von 10 steht oben schon alles. Ein Board ohne externe PA würde man dagegen mit seiner echten Ausgangsleistung einstellen.

MeshCore-App, Einstellungen des Companions: öffentlicher Schlüssel, Breiten- und Längengrad 0.0, Position in Advert teilen deaktiviert, Preset EU/UK (Narrow), Frequenz 869.618 MHz, Bandbreite 62.5 kHz, Spreading-Faktor 8, Codierungsrate 8, Sendeleistung 10 dBm, Repeat-Modus deaktiviert, Default Path Hash Size 2-Byte (max. 32 Hops), Default Region Scope #de
Die Companion-Einstellungen von Kernel-Error: Sendeleistung 10, Pfad-Hash 2 Byte, Default-Scope #de, Position im Advert aus und Repeat-Modus aus.

Der Schalter „Repeat-Modus aktivieren“ sieht verlockend aus, macht aber nicht, was man erwartet. Laut Quellcode der Companion-Firmware ist er nur auf 433,000, 869,495 oder 918,000 MHz erlaubt. Auf 869,618 MHz lehnt die Firmware ihn ab. Wer ihn einschaltet, zieht also auf eine andere Frequenz um und verlässt das Netz der Community. Gedacht ist das für Off-Grid-Gruppen ohne Repeater. Bei meinen Companions bleibt er aus, das Weiterleiten erledigen hier die Repeater.

Kanäle

Neben Direktnachrichten an Kontakte gibt es Kanäle. Jeder MeshCore-Companion kennt den Kanal „Public“. Dessen Schlüssel 8b3387e9c5cdea6ac9e5edbaa115cd72 ist überall gleich und öffentlich, die Verschlüsselung schützt dort also vor niemandem im Mesh. Dazu kommen Hashtag-Kanäle wie #nrw, #bonn oder #rheinbach. Bei denen wird der Schlüssel per SHA-256 aus dem Namen abgeleitet. Wer den Namen kennt, kann mitlesen. Privat sind nur Kanäle mit einem selbst erzeugten, geheim geteilten Schlüssel.

Jeder Kanal kann zusätzlich einen eigenen Scope tragen, meinen Public-Kanal habe ich auf de gestellt. Welcher Scope für welchen Kanal sinnvoll ist und wie du den tatsächlich wirksamen Scope prüfst, steht in Teil 6. Ein Nebeneffekt, den man sofort sieht: Mit Scope begrenzt die App die maximale Nachrichtenlänge im Kanal auf 120 statt 130 Zeichen. Die Firmware selbst kürzt dabei nichts, der Transport-Code steckt im Paketkopf und nicht in den Nutzdaten.

Mein Kontakt als QR-Code

Über „Kontakt teilen“ erzeugt die App einen QR-Code. Darin steckt eine Adresse der Form meshcore://contact/add?name=...&public_key=...&type=1, also nur Name, öffentlicher Schlüssel und Typ (1 steht für Chat). Position, Zeitstempel und Signatur fehlen. Der Code beweist deshalb nicht von selbst, dass er von mir stammt. Das Vertrauen kommt hier aus dem Kanal: Du hast ihn aus meinem Blog. Wenn du mich hinzufügen willst, scann den Code und vergleich den Fingerprint mit <b2035736...84ce8610>. Der volle Schlüssel lautet:

b203573688347b345a560e0a8156ecc6c0dc7d14c4f53046064bfabe84ce8610
Schwarz-weißer QR-Code mit einer MeshCore-Kontaktadresse, die den Namen Kernel-Error und den öffentlichen Schlüssel enthält
Mein Companion „Kernel-Error“ als MeshCore-Kontakt. Der Code enthält nur Namen und öffentlichen Schlüssel.

Viel gebe ich damit nicht preis. Name und Schlüssel stehen ohnehin in jedem Advert, das mein Node sendet. Was dazukommt, sind vielleicht ein paar ungefragte Direktnachrichten. Die nehme ich in Kauf.

Ein Gehäuse aus dem Drucker

Mein erstes Board lag anfangs nackt auf dem Tisch. Für unterwegs habe ich mir das Modell „WiFi LoRa 32 V4 (Heltec V4)“ von Adi_CEvil auf MakerWorld in Rot gedruckt. Danke dafür. Heltec und ein 1100-mAh-LiPo passen hinein, geladen wird über USB-C. Beim Filament lieber keine Varianten mit Carbon- oder Metallanteil nehmen, leitfähige Füllstoffe direkt an der Antenne verstimmen sie und schlucken Leistung. Ihr wickelt das Teil ja auch nicht in Alufolie ein, oder?

Rotes 3D-gedrucktes Gehäuse mit Heltec V4 und Akku auf einer Werkbankmatte, im Fenster das OLED mit Kernel-Error, Akkusymbol, MSG: 0 und Pin: 370062, oben rechts schaut die schwarze Federantenne heraus
Fertig für die Hosentasche: Heltec V4 mit LiPo im gedruckten Gehäuse. Das OLED zeigt den Namen ohne Emoji, der kleine Font kennt keine Emojis.

Vom Linux-Rechner aus: meshcore-cli und Meshy

Am PC benutze ich zwei Werkzeuge. Für die Kommandozeile gibt es meshcore-cli, installiert mit pipx, der Befehl heißt dann meshcli. Gekoppelt wird über BlueZ. Vorher am Handy Bluetooth aus, denn es gibt nur den einen BLE-Platz. Nach dem Pairing hält bluetoothctl die Verbindung gern offen, dann findet meshcli das Gerät nicht. Ein disconnect löst das.

$ pipx install meshcore-cli
$ bluetoothctl
[bluetooth]# agent KeyboardOnly
[bluetooth]# pair <BLE-MAC>
  (PIN vom OLED eingeben)
[bluetooth]# trust <BLE-MAC>
[bluetooth]# disconnect <BLE-MAC>
$ meshcli -a <BLE-MAC> ver
Model: Heltec V4.3 OLED

Mit meshcli komme ich an Dinge, die die App nicht zeigt, etwa die Funkstatistik mit get stats_radio oder node_discover, bei dem Repeater in direkter Reichweite mit ihren Empfangswerten antworten. Den Pfad-Hash kann man dort mit set path_hash_mode 1 auf 2 Byte stellen. Ein Tipp aus der Praxis: meshcli 1.6.5 holt die Kontakte mit einem Timeout von 5 Sekunden. Bei über hundert Kontakten über BLE reicht das nicht, dann behauptet das Tool, es gäbe keine. Die Python-Bibliothek dahinter mit längerem Timeout schafft es.

Für den Alltag am Schreibtisch nehme ich Meshy, einen grafischen MeshCore-Client für den Linux-Desktop. Meshy verbindet sich ebenfalls per BLE, zeigt Geräteinfos, Kontakte, Kanäle und eine Karte und kann Adverts senden, Pfade tracen und Nodes in der Nähe suchen.

Meshy unter Linux, Ansicht Device Information: Status Bluetooth Connected, Node Name Kernel-Error, Board Heltec V4.3 OLED, Firmware v1.17.1-d929643 vom 14-Aug-2026, öffentlicher Schlüssel, Location GPS off, Battery 97 % bei 4139 mV, links Aktionen wie Send Advert, Trace Path, Discover Nearby Nodes und Rx Log
Meshy unter Linux: Geräteinfo des Companions, hier noch mit Firmware v1.17.1.
Meshy-Kontaktliste mit 150 Kontakten, oben ErrorCar als Favorit mit Direct-Route, darunter Repeater wie DE-NW-SU-Todenfeld-KE, DE-NW-MH-CITYRPT, DE-NW Graniti Duisdorf und DE-RP-Remagen mit Flood-Route
Kontakte in Meshy. Favoriten stehen oben, daneben steht, ob der Weg bekannt ist (Direct) oder geflutet wird (Flood).
Meshy mit geöffnetem Kanal Public, links die Kanalliste mit Public, #linux, #nrw, #bonn, #rheinland, #wetter, #rheinbach, #todenfeld und #goch, rechts zwei Nachrichten mit den Angaben 4 hops und de sowie 8 hops
Der Public-Kanal in Meshy. Unter jeder Nachricht stehen Hop-Anzahl und, falls vorhanden, der Scope.

KiekR auf Android

Auf dem Handy bin ich inzwischen bei KiekR gelandet, einer alternativen Android-App von Marcel Verdult (kiekr.app). Sie ist Closed Source, das sollte man wissen. Mir gefällt sie trotzdem besser als die offizielle App, weil sie mehr vom Funk zeigt: den Scope pro Nachricht, eine Statistik und vor allem „Live-Funk“, eine Rohansicht aller empfangenen Pakete, auch der, die der Node nicht entschlüsseln kann. KiekR ersetzt die offizielle App, parallel geht wegen des einen BLE-Platzes nicht.

KiekR-Chatliste mit Kanälen #nrw, #bochum, #bonn, #eifel, #euskirchen, #goch, #hamradio, #kevelaer und #kleve, Nachrichtenvorschauen unscharf gemacht, oben ein Hinweis Neue Kanäle in der Nähe gehört: #duisburg
Die Chatliste in KiekR. Die App meldet sogar Hashtag-Kanäle, die in der Nähe gehört wurden.

An einem Morgen zu Hause hat der Live-Funk innerhalb weniger Minuten 250 Pakete gezeigt, rund 23 pro Minute. 232 davon waren für meinen Node nicht lesbar. Das ist kein Fehler, das sind Direktnachrichten, Anfragen und Antworten anderer Nodes und Kanäle, deren Schlüssel ich nicht habe. Schön zu sehen war auch, wie weit gescopte Floods laufen: Eine Anfrage mit Scope de kam über 17 und 18 Hops, mit 1-Byte-Pfad-Hashes. Jeder dieser Hops kostet Sendezeit. Warum das relevant ist, steht in Teil 6.

KiekR Live-Funk ungefiltert: 250 Pakete gesamt, Rauschen -89 dBm, Mesh ausgelastet mit 23 pro Minute, 236 Multi-Hop, 232 undekodierbar, darunter TFlood-REQ-Pakete mit 17 und 18 Hops, Scope de und Pfad mit 1 Byte pro Hop, Hinweis verschlüsselt, nicht für dieses Gerät
Live-Funk in KiekR: Die meisten Pakete sind verschlüsselt und nicht für diesen Node. Oben ein gescopter Flood über 17 Hops.
KiekR-Statistik: 336 Adverts gehört, 266 in den letzten 7 Tagen, 319 mit GPS, 336 Kontakte installiert, nach Typ 34 Chats, 292 Repeater, 10 Räume, 0 Sensoren, 0 Andere
336 Kontakte bei einem Limit von 350: Ohne „älteste überschreiben“ wäre die Tabelle bald voll.

KiekR hat außerdem optionale Community-Funktionen, etwa das Hochladen gehörter Repeater-Adverts auf Karten oder einen Observer-Modus, der Rohpakete an Analyse-Seiten weiterreicht. Die sind standardmäßig aus. Bevor du sie auf einem mobilen Node einschaltest, überleg kurz, was sie über deine Wege verraten.

Firmware-Update ohne Datenverlust

Mitten im Projekt erschien MeshCore v1.18.0 (Release-Notes). Unter anderem kann der BLE-Build für den Heltec V4 seitdem auch WLAN. Beim Update will ich weder Identität noch Kontakte verlieren, also gehe ich so vor:

  • Erst wieder ein volles 16-MB-Backup mit read-flash. Darin steckt der private Schlüssel, die Datei bekommt Rechte 600 und bleibt auf dem eigenen Rechner.
  • Die Partitionstabelle aus dem Backup lesen. Bei mir liegt die Anwendung ab 0x10000, Identität, Kontakte, Kanäle und Einstellungen liegen in einer eigenen Datenpartition weiter hinten.
  • Prüfen, ob Bootloader und Partitionstabelle im neuen Merged-Image identisch sind. Nur dann reicht es, das reine App-Image an 0x10000 zu schreiben.
  • Kein erase-flash. Damit bleiben Datenpartition und BLE-Kopplung unangetastet.
$ esptool --port /dev/ttyACM0 --baud 921600 read-flash 0 ALL backup/companion-before-1.18.0-16MB.bin
$ chmod 600 backup/companion-before-1.18.0-16MB.bin
$ esptool --port /dev/ttyACM0 --baud 921600 write-flash 0x10000 heltec_v4_companion_radio_ble-v1.18.0-81dbae5.bin

Das Schreiben hat acht Sekunden gedauert. Danach meldete sich der Node mit v1.18.0-81dbae5, gleichem Schlüssel, gleichem Namen, allen 244 Kontakten, allen Kanälen, 2-Byte-Pfad-Hash und Default-Scope #de. Auch das Pairing mit dem Handy hielt. Nur die Uhr lief nach den Resets ein paar Minuten hinterher. Nach einem Update also kurz die Zeit vom Handy oder PC synchronisieren. Keine Angst, hier fühlt man sich ganz schnell wieder zuhause, wenn man schon mal mit arduino, esp usw. gearbeitet hat.

MeshCore-App, Einstellungsmenü mit App-Datenbank exportieren, Daten löschen, Fehlerbericht, Debug-Protokolle, Kommandozeile, Auf Werkseinstellungen zurücksetzen und Neustart, darunter Geräte-Info mit Kanäle 1/40, Kontakte 163/350, Speicher 3 % belegt, Geräte-Modell Heltec V4.3 OLED, Firmware-Datum 15-Oct-2026, Firmware-Version v1.18.0-81dbae5, Connected via BLE
Geräte-Info in der App nach dem Update auf v1.18.0.

Ein zweiter Node mit GPS fürs Auto

Als das Komplettset da war, habe ich meine Identität „Kernel-Error“ auf das neue Board umgezogen. SUPER WICHTIG: Ein Schlüssel gehört nur einem aktiven Gerät. Also zuerst das alte Board komplett löschen und neu flashen, dann auf dem neuen Board den gesicherten Export importieren. Pfad-Hash und Default-Scope stehen nicht im Export und müssen auf beiden Geräten neu gesetzt werden.

Das alte Board mit dem GPS-Modul hat einen neuen Schlüssel bekommen und wohnt jetzt als „ErrorCar“ im Auto. Die Position sendet es nicht im Advert. Stattdessen darf genau ein Kontakt, mein persönlicher Node, sie per Telemetrie abfragen. Die Antwort ist verschlüsselt, Repeater unterwegs sehen nur Chiffretext, und ohne Anfrage verrät das Auto gar nichts. Achtung: Das klappt nur, wenn neben der Standort-Telemetrie auch die Basis-Telemetrie für diesen Kontakt freigegeben ist, sonst antwortet der Node niemandem. Was ich mit dem Auto-Node sonst noch mache, zeige ich in Teil 5 über MeshMapper. Aber ich werde an dem Punkt nicht der Einzige sein, der den Satz „ich kann remote also die GPS Position abfragen?“ im Kopf hatte. Damit ist auch klar, warum ich an Sensoren denke und öhm einen GPS Tracker?!

Was du jetzt tun kannst

Wenn du selbst einsteigen willst, ist das Heltec-V4-Komplettset mit Gehäuse und 3000-mAh-Akku der bequemste Weg: Antenne an die LoRa-Buchse, Backup ziehen, MeshCore drauf, Preset deiner Region wählen, Advert senden und im Public-Kanal auf das erste „Repeater empfangen“ warten. Wer gleich einen Companion fürs Auto oder ein zweites Board für einen eigenen Repeater haben möchte, bekommt den Heltec V4 mit L76K-GPS im Doppelpack. Eins solltest du dabei wissen: Der V4 ist ein ESP32-S3 mit zusätzlichem Verstärker und braucht deutlich mehr Strom als Boards mit nRF52-Chip, etwa der RAK4631. Für meinen Einstieg war mir das egal. Wer lange am Akku laufen will, schaut sich besser so ein Board an. Und wenn du mich hinzufügst, freue ich mich über ein kurzes „Moin“ per Direktnachricht.

Siehe auch: Teil 1: LoRa und MeshCore, Teil 3: Der Repeater, Teil 4: Antennen und Messen, Teil 5: MeshMapper, Teil 6: Konfiguration, die zählt.

Links zu Amazon und AliExpress sind Affiliate-Links: Kaufst du darüber, bekomme ich eine kleine Provision, für dich ändert sich am Preis nichts. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen.

Wenn ihr Fragen habt oder euer Companion partout keinen Repeater hören will, dann dürft ihr mich gerne fragen.