Created
May 16, 2026 11:04
-
-
Save NeoCat/ddaa47dcd33c4ed57349e34d65071bab to your computer and use it in GitHub Desktop.
Send text message to mestastic primary channel from Arduino MKR WAN 1310 LoRa using meshtastic-lite library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // AES from Crypto library (https://github.com/rweather/arduinolibs) | |
| #include <AES.h> | |
| // arduino-LoRa library (https://github.com/sandeepmistry/arduino-LoRa) | |
| // Firmware must be written using MKRWANFWUpdate_standalone example in https://github.com/arduino-libraries/MKRWAN, not v2!) | |
| #include <LoRa.h> | |
| // meshtastic-lite library (https://github.com/jstockdale/meshtastic-lite) | |
| // A fix may be required to send messages longer than 12B (https://github.com/jstockdale/meshtastic-lite/pull/1) | |
| #include <meshtastic.h> | |
| #include <string.h> | |
| static AES128 g_aes; | |
| static uint8_t g_cached_key[16]; | |
| static bool g_key_set = false; | |
| extern "C" void mesh_aes_block_encrypt(const uint8_t *key, int key_bits, | |
| const uint8_t in[16], uint8_t out[16]) | |
| { | |
| (void)key_bits; | |
| if (!g_key_set || memcmp(key, g_cached_key, 16) != 0) { | |
| g_aes.setKey(key, 16); | |
| memcpy(g_cached_key, key, 16); | |
| g_key_set = true; | |
| } | |
| g_aes.encryptBlock(out, in); | |
| } | |
| // ─── Configuration ────────────────────────────────────────────────────────── | |
| static const char TX_TEXT[] = "Test message from Arduino MKR1310 (meshtastic-lite)"; | |
| static const uint32_t TX_INTERVAL_MS = 30000UL; // 30 s between transmissions | |
| // ─── Globals ──────────────────────────────────────────────────────────────── | |
| static MeshSession session; | |
| static uint8_t tx_frame[255]; | |
| static unsigned long last_tx_ms; | |
| // ─── SAMD21 node number from unique serial ────────────────────────────────── | |
| // SAMD21 has a 128-bit serial spread across four non-contiguous NVM words: | |
| // word 0 @ 0x0080A00C, word 3 @ 0x0080A048 | |
| // XOR gives a stable 32-bit value suitable as a Meshtastic node number. | |
| static uint32_t getNodeNum() { | |
| const uint32_t w0 = *reinterpret_cast<const volatile uint32_t *>(0x0080A00Cu); | |
| const uint32_t w3 = *reinterpret_cast<const volatile uint32_t *>(0x0080A048u); | |
| return w0 ^ w3; | |
| } | |
| // ─── Send one Meshtastic text broadcast ───────────────────────────────────── | |
| static void sendMessage() { | |
| size_t len = session.buildTextTx( | |
| 0, MESH_ADDR_BROADCAST, TX_TEXT, false, false, tx_frame); | |
| if (len == 0) { Serial.println("buildTextTx failed"); return; } | |
| if (!LoRa.beginPacket()) { Serial.println("beginPacket failed"); return; } | |
| LoRa.write(tx_frame, len); | |
| LoRa.endPacket(); | |
| Serial.print("TX "); Serial.print(len); Serial.println(" bytes OK"); | |
| } | |
| // ─── Setup ────────────────────────────────────────────────────────────────── | |
| void setup() { | |
| Serial.begin(115200); | |
| while (!Serial && millis() < 3000); // wait up to 3 s for USB console | |
| // Derive node number from SAMD21 hardware unique serial | |
| uint32_t node_num = getNodeNum(); | |
| Serial.print("Node num : 0x"); Serial.println(node_num, HEX); | |
| // Seed packet-ID generator with hardware entropy | |
| srand(node_num ^ (uint32_t)analogRead(A0)); | |
| // Initialise Meshtastic session — JP LongFast, default PSK channel (AES-128) | |
| session.init(REGION_JP, MODEM_LONG_FAST, ROLE_CLIENT, node_num); | |
| session.channels.addDefaultChannel(); | |
| // Derive all radio parameters from the session (frequency, SF, BW, CR, …) | |
| MeshRadioConfig rc = session.radioConfig(); | |
| Serial.print("Frequency : "); Serial.print(rc.frequency_mhz, 3); Serial.println(" MHz"); | |
| Serial.print("SF / BW : "); Serial.print(rc.spreading_factor); | |
| Serial.print(" / "); Serial.print((int)rc.bandwidth_khz); Serial.println(" kHz"); | |
| Serial.print("TX power : "); Serial.print(rc.tx_power_dbm); Serial.println(" dBm"); | |
| // Initialise LoRa radio | |
| if (!LoRa.begin((long)(rc.frequency_mhz * 1.0e6f))) { | |
| Serial.println("LoRa init FAILED — check antenna/module"); | |
| while (1); | |
| } | |
| LoRa.setSpreadingFactor(rc.spreading_factor); // 11 | |
| LoRa.setSignalBandwidth((long)(rc.bandwidth_khz * 1000.0f)); // 250 000 Hz | |
| LoRa.setCodingRate4(rc.coding_rate); // 5 → 4/5 | |
| LoRa.setSyncWord(rc.sync_word); // 0x2B (Meshtastic private) | |
| LoRa.setPreambleLength(rc.preamble_length); // 16 symbols | |
| LoRa.setTxPower(rc.tx_power_dbm); // 13 dBm (JP limit) | |
| LoRa.enableCrc(); | |
| Serial.println("LoRa ready — sending first packet now"); | |
| sendMessage(); | |
| last_tx_ms = millis(); | |
| } | |
| // ─── Loop ─────────────────────────────────────────────────────────────────── | |
| void loop() { | |
| if (millis() - last_tx_ms >= TX_INTERVAL_MS) { | |
| last_tx_ms = millis(); | |
| sendMessage(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment