{"id":1527,"date":"2026-06-02T07:12:40","date_gmt":"2026-06-02T07:12:40","guid":{"rendered":"https:\/\/reppi.de\/?p=1527"},"modified":"2026-06-02T07:12:41","modified_gmt":"2026-06-02T07:12:41","slug":"projekt-lora-beacon-fuer-meshtastic","status":"publish","type":"post","link":"https:\/\/reppi.de\/?p=1527","title":{"rendered":"Projekt: LoRa Beacon f\u00fcr Meshtastic"},"content":{"rendered":"\n<p>Kleiner Meshtastic Beacon, der einmal pro Zeiteinheit einen Wert in das Meshtastic Netz postet. <\/p>\n\n\n\n<!--more-->\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_85 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Inhaltsverzeichnis<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/reppi.de\/?p=1527\/#Worum_geht_es_hier\" >Worum geht es hier?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/reppi.de\/?p=1527\/#Was_brauchen_wir\" >Was brauchen wir?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/reppi.de\/?p=1527\/#Hardware_aufsetzen\" >Hardware aufsetzen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/reppi.de\/?p=1527\/#Der_Code\" >Der Code<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/reppi.de\/?p=1527\/#STEP_2_Persoenlicher_Kanal\" >STEP 2: Pers\u00f6nlicher Kanal<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Worum_geht_es_hier\"><\/span>Worum geht es hier? <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Es geht darum das Prinzip zu Verstehen, wie man in ein bestehendes LoRa Meshtastic Netz einen Beacon installiert, der z.B. Messwerte zyklisch sendet. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Was_brauchen_wir\"><\/span>Was brauchen wir? <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Zuerst das, was nicht passiert: Ich erkl\u00e4re hier nicht, wie man mit Arduino IDE oder VSC Code generiert oder wie man l\u00f6tet. Auch bitte ich zu bedenken, dass der erste &#8222;Proof ob Concept&#8220; keine Dauerl\u00f6sung sein darf &#8211; In &#8222;erster N\u00e4herung&#8220; sendet der Beacon n\u00e4mlich an \u00f6ffentliche Kan\u00e4le und so w\u00fcrde jeder Node in der Umgebung zugespamt werden. Bitte richtet euch auf alle F\u00e4lle einen privaten Kanal ein f\u00fcr sowas &#8211; Wie das geht, erkl\u00e4re ich am Ende nochmal genau. <\/p>\n\n\n\n<p>Wir brauchen einen uC f\u00fcr die Messwertaufnahme und das Housekeeping. Da Ger\u00e4te, die im Freien irgendwo zug\u00e4nglich &#8222;herumh\u00e4ngen&#8220; auch gerne mal Fl\u00fcgel bekommen, also geklaut oder stumpf zerst\u00f6rt werden, soll es nicht all zu teuer zugehen. Stellt euch vor ihr nutzt einen derzeit hei\u00df begehrten Raspberry Pi 2 W oder gar einen Pi 5 &#8211; Da freut der &#8222;Finder&#8220; gleich doppelt. Darum benutze ich folgendes: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ESP32 C3 Super Mini mit kleinem OLED Display. Ca. 6\u20ac<\/li>\n\n\n\n<li>HT-RA62 LoRa Modul &#8211; Auch ca. 6\u20ac<\/li>\n\n\n\n<li>Ein PETG Geh\u00e4use mit Fach f\u00fcr Powerbank plus ein entsprechendes USB Kabel, Wasserdicht gestaltet und Antennenbuchse nach au\u00dfen gef\u00fchrt. <\/li>\n\n\n\n<li>Eine billige Powerbank unter 10\u20ac mit >5000mAh. <\/li>\n<\/ul>\n\n\n\n<p>Wir landen also um die 20\u20ac und korrekt: Da nur die Temperatur zu \u00fcbermitteln ging einfacher. Es k\u00f6nnen so beliebige Werte an mehrere Empf\u00e4nger &#8222;gepostet&#8220; werden und daf\u00fcr ist der Preis okay. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Hardware_aufsetzen\"><\/span>Hardware aufsetzen<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"910\" height=\"1024\" src=\"https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_-910x1024.jpeg\" alt=\"\" class=\"wp-image-1529\" srcset=\"https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_-910x1024.jpeg 910w, https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_-266x300.jpeg 266w, https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_-768x865.jpeg 768w, https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_-1364x1536.jpeg 1364w, https:\/\/reppi.de\/wp-content\/uploads\/2026\/06\/Bildschirmfoto_2-6-2026_81124_www.amazon.de_.jpeg 1384w\" sizes=\"(max-width: 910px) 100vw, 910px\" \/><\/figure>\n\n\n\n<p>Ich nutze hier bequemerweise einfach die ARDUINO IDE und habe das Board ESP32 C3 &#8222;SuperMini&#8220; korrekt enabled. &#8222;Hello World&#8220; l\u00e4uft auf dem OLED und die LEDs kann ich ansprechen. Das bedeutet: Der ESP32 hat verloren und ist unter Kontrolle. (Immer MEIN TIP: Geht Schrittweise vor!) <\/p>\n\n\n\n<p>Dann ben\u00f6tigt ihr die RadioLib, die wir mit &#8222;radiolib.h&#8220; einbinden. <\/p>\n\n\n\n<p>Die Hardware: Verbaut zwischen 3,3V Speistung und GND einen PUFFER-ELKO. Sonst kann es sein, dass euch beim Senden die Spannungsversorgung versagt. Je nach Platz sind 10..100uF Okay. <\/p>\n\n\n\n<p>Der ESP32 kommt bei mir tats\u00e4chlich ohne WLAN M\u00f6glichkeit. Das ganze Anpassungsnetzwerk fehlt. So oder so: Soll der ESP das LoRa-Modul versorgen, lasst WLAN vorsorglich aus. LoRa und WLAN ist mitunter etwas hart f\u00fcr den kleinen LDO. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Der_Code\"><\/span>Der Code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Der Code als SKELETTON: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;RadioLib.h>\n#include &lt;U8g2lib.h>\n\n\n\/\/ --- KONSTANTEN  ---\nconst int PIN_LORA_NSS = 10;\nconst int PIN_LORA_DIO1 = 9;\nconst int PIN_LORA_RST = 8;\nconst int PIN_LORA_BUSY = 7;\n\nconst int PIN_I2C_SDA = 4;\nconst int PIN_I2C_SCL = 5;\n\nconst float LORA_FREQ = 868.0;\nconst float LORA_BW = 250.0;\nconst int LORA_SF = 11;\nconst int LORA_CR = 4;\nconst int LORA_SYNC_WORD = 0x2B;\nconst int LORA_TX_POWER = 17;\n\n\n\n\/\/ OLED Setup (z.B. SH1106 oder SSD1306)\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, \/* reset=*\/ U8X8_PIN_NONE, PIN_I2C_SCL, PIN_I2C_SDA);\n\nSX1262 radio = new Module(PIN_LORA_NSS, PIN_LORA_DIO1, PIN_LORA_RST, PIN_LORA_BUSY);\n\nvoid updateDisplay(String status) {\n  u8g2.firstPage();\n  do {\n    u8g2.setFont(u8g2_font_ncenB08_tr);\n    u8g2.drawStr(0, 20, \"Status:\");\n    u8g2.drawStr(0, 40, status.c_str());\n  } while (u8g2.nextPage());\n}\n\nvoid setup() {\n  u8g2.begin();\n  updateDisplay(\"Initialisierung...\");\n  \n  SPI.begin(6, 2, 3, PIN_LORA_NSS);\n  if (radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, LORA_SYNC_WORD) == RADIOLIB_ERR_NONE) {\n    radio.setOutputPower(LORA_TX_POWER);\n    updateDisplay(\"Bereit.\");\n  } else {\n    updateDisplay(\"LoRa Fehler!\");\n  }\n}\n\nvoid loop() {\n  String message = \"Messwert: 22.5\";\n  \n  updateDisplay(\"Sende...\");\n  \n  \/\/ CAD Pr\u00fcfung (LBT)\n  if (radio.startCAD() == RADIOLIB_ERR_NONE &amp;&amp; radio.scanCAD() != RADIOLIB_PREAMBLE_DETECTED) {\n    radio.transmit(message);\n    updateDisplay(\"Gesendet!\");\n  } else {\n    updateDisplay(\"Kanal belegt\");\n  }\n  \n  delay(60000);\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"STEP_2_Persoenlicher_Kanal\"><\/span>STEP 2: Pers\u00f6nlicher Kanal<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Wie oben geschrieben spamt ihr nat\u00fcrlich jeden Node zu, der sich in der N\u00e4he befindet und einen \u00f6ffentlichen Kanal hat. <br>Das ist nicht die feind Art. Aber kompliziert ist der Umgang mit dem notwendigen protobuf wiederum leider auch&#8230; Darum: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;RadioLib.h>\n#include &lt;U8g2lib.h>\n\n\/\/ --- KONSTANTEN ---\nconst uint8_t CHANNEL_HASH&#91;4] = {0xAA, 0xBB, 0xCC, 0xDD}; \/\/ HIER DEINEN HASH EINTRAGEN!\nconst int PIN_LORA_NSS = 10;\nconst int PIN_LORA_DIO1 = 9;\nconst int PIN_LORA_RST = 8;\nconst int PIN_LORA_BUSY = 7;\n\nSX1262 radio = new Module(PIN_LORA_NSS, PIN_LORA_DIO1, PIN_LORA_RST, PIN_LORA_BUSY);\n\nvoid sendPrivatePacket(String message) {\n  uint8_t buffer&#91;64];\n  \n  \/\/ 1. Header: Channel Hash (Privater Kanal)\n  memcpy(buffer, CHANNEL_HASH, 4);\n  \n  \/\/ 2. Meshtastic Protobuf Tags\n  buffer&#91;4] = 0x08; buffer&#91;5] = 0x01; \/\/ hop_limit = 1\n  buffer&#91;6] = 0x52; buffer&#91;7] = 0x01; \/\/ portnum = TEXT_MESSAGE\n  buffer&#91;8] = 0x5A; buffer&#91;9] = (uint8_t)message.length();\n  \n  \/\/ 3. Payload\n  for(int i = 0; i &lt; message.length(); i++) {\n    buffer&#91;10 + i] = message&#91;i];\n  }\n  \n  \/\/ 4. CAD &amp; Senden\n  if (radio.startCAD() == RADIOLIB_ERR_NONE &amp;&amp; radio.scanCAD() != RADIOLIB_PREAMBLE_DETECTED) {\n    radio.transmit(buffer, 10 + message.length());\n  }\n}<\/code><\/pre>\n\n\n\n<p>und Verschl\u00fcsselung k\u00e4me so ins Spiel:  Ja&#8230; dann beachtet bitte, dass das AES vom HELTEC \u00fcblicherweise BASE64 ist und ihr das korrekt auf 32 Bit \u00fcbertragen m\u00fcsst. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include \"mbedtls\/aes.h\"\n\n\nuint8_t aesKey&#91;32] = { \/* Deine Key-Bytes hier *\/ }; \n\nvoid sendEncryptedPacket(String message) {\n  uint8_t buffer&#91;128];\n  uint8_t encryptedPayload&#91;message.length()];\n  \n  \/\/ 1. AES-256-CTR Verschl\u00fcsselung\n  \/\/ Ben\u00f6tigt: IV\/Nonce (bestehend aus Node-ID + Counter)\n  uint8_t iv&#91;16] = {0}; \/\/ Hier muss dein Z\u00e4hler\/Nonce rein!\n  \n  mbedtls_aes_context aes;\n  mbedtls_aes_init(&amp;aes);\n  mbedtls_aes_setkey_enc(&amp;aes, aesKey, 256);\n  size_t nc_off = 0;\n  uint8_t stream_block&#91;16] = {0};\n  \n  mbedtls_aes_crypt_ctr(&amp;aes, message.length(), &amp;nc_off, iv, stream_block, \n                        (uint8_t*)message.c_str(), encryptedPayload);\n  mbedtls_aes_free(&amp;aes);\n\n  \/\/ 2. Paket zusammenbauen\n  memcpy(buffer, CHANNEL_HASH, 4);\n  buffer&#91;4] = 0x08; buffer&#91;5] = 0x01; \/\/ hop_limit = 1\n  buffer&#91;6] = 0x1A;                   \/\/ Tag f\u00fcr \"encrypted\" statt \"decoded\"\n  \/\/ ... restliche Protobuf-Tags f\u00fcr das verschl\u00fcsselte Feld ...\n  \n  \/\/ Kopiere das verschl\u00fcsselte Payload statt dem Klartext\n  memcpy(buffer + 10, encryptedPayload, message.length());\n  \n  radio.transmit(buffer, 10 + message.length());\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Kleiner Meshtastic Beacon, der einmal pro Zeiteinheit einen Wert in das Meshtastic Netz postet.<\/p>\n","protected":false},"author":1,"featured_media":1530,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[11],"tags":[],"class_list":["post-1527","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technik"],"_links":{"self":[{"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/posts\/1527"}],"collection":[{"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/reppi.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1527"}],"version-history":[{"count":2,"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/posts\/1527\/revisions"}],"predecessor-version":[{"id":1531,"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/posts\/1527\/revisions\/1531"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/reppi.de\/index.php?rest_route=\/wp\/v2\/media\/1530"}],"wp:attachment":[{"href":"https:\/\/reppi.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1527"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/reppi.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1527"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/reppi.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1527"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}