LoRa und LoRaWAN

Beitrag vom 26.07.2021 

Probleme beim Einsatz von LORA Radio Node V1 (868 MHz) und OTAA – keine Join möglich

Einen Sensor für LoRaWan muss man nicht unbedingt selbst über eine Leiterplatte anfertigen. Es gibt fertige chinesische Baugruppen, die bestens dafür geeignet sind und auch gleich viele notwendige Anschlüsse mitbringen. Das Modul mit der Bezeichnung LORA Radio Node V1 ist ein sofort nutzbares Modul zu erschwinglichen Kosten von unter 20 Euro. Neben dem ATMEGA328P ist ein Lora Radio Modul RFM95 und ein Halter für eine Lithium Batterie vom Typ 14500 verbaut. Es gibt viele Anleitungen im Netz wie man solch einen Knoten für LoRaWan programmiert und einbindet. Bemerkenswert ist der geringe Stromverbrauch in den Sendepausen von unter 200µA. Die Lithium Batterie dürfte bei einem Sendeintervall von 30 Minuten und guter Funkabdeckung mehr als ein Jahr halten.

LORA Radio Node V1

Schnell hatte ich mir zwei solche Module liefern lassen und konnte diese auch nach kurzer Zeit über die Arduino IDE programmieren. Man gibt einfach an, dass man einen Arduino Mini mit 3,3 Volt und 8 MHz hat.

Für den Verbindungsaufbau wollte ich gleich den neueren TheThingsStack (also V3) mit der sicheren Aktivierungmethode (Join Verfahren) OTAA (Over-The-Air-Activation) verwenden. Was auf dem Steckbrett mit einem Arduino Mini 3,3 Volt 8Mhz und dem RFM95 sofort funktioniert, wollte nicht mit dem fertigen LORA Radio Node V1 gelingen. Ununterbrochen versuchte sich das LORA Radio Node per Join einzubuchen, konnte aber diesen Vorgang nach Stunden nicht abschließen. Zuerst dachte ich, dass irgendeine Verbindung zwischen ATMEGA und RFM95 fehlt. Nein, alle Verbindungen waren vorhanden. Dann hatte ich das Radio Modul RFM95 in Verdacht. Ist es wirklich wie auf der Lora Radio Node Leiterplatte angegeben eine 868 MHz Version? Nach allen Zweifeln lötete ich das RFM95 Modul herunter. Alles korrekt, auf der Unterseite war tadellos zu erkennen, dass das RFM95 ein Modul für die EU / Deutschland mit 868 MHz ist. Was soll dann noch das Problem sein? Möglicherweise der Bootloader im ATMEGA323P? Also habe ich den Atmel ISP Steckverbinder 2×3 Pins eingelötet und meinen Atmel STK500 als Programmer angeschlossen. Ergebnis –> keine Änderung. Das Modul stellte auch mit dem neuen Bootloader und der neu geflashten Software nach Stunden absolut keine Verbindung zum TTS her. Die Antwort vom Server kommt einfach nicht an oder kann nicht verstanden werden. Zwischenzeitlich habe ich zig unterschiedliche Konfigurationen getestet, das Internet nach hilfreichen Hinweisen durchsucht und auch das Board mit einer sauberen 3,3 Volt DC spannungsversorgt. Was kann es noch sein? Stimmt eventuell der Takt des Controllers nicht? Schnell habe ich eine kleine Software geschrieben die jede Sekunde einen Portpin schaltet. Komisch war, dass die Impulse gar nicht im erwarteten 1 Sekunden Zyklus angekommen sind. Der Takt des Original Quarz muss viel höher sein als erwartet. Nach einer Messung mit dem Oszi kann man sehr gut sehen, dass schon nach 997 ms die Impulse ausgegeben werden. Das kann doch nicht wahr sein, die eingelötete Quarz hat eine so große Abweichung, dass möglicherweise das LoRaWan Timing nicht eingehalten wird.

der Takt vom Quarz ist viel zu schnell
LORA Radio Node V1 mit gutem Quarz

Der kleine SMD Quarz konnte schnell entfernt werden und aus Mangel an Bauteilen habe ich einen normalgroßen 8 MHz Quarz mit längeren Beinchen eingelötet. Und siehe da, schon nach dem zweiten Join hatte sich des Modul angemeldet und war einsatzbereit. Die Freude nach fast 14 Tagen Fehlersuche war enorm groß.

Zum Thema Clock Error habe ich einen interessanten Beitrag gefunden.

Clockerror = https://www.thethingsnetwork.org/forum/t/using-lmic-setclockerror-on-mcci-lmic-howto/39776

 

Das Board mit dem abgelöteten RFM95 habe ich weggeworfen, da auch des RFM95 Modul das Ablöten nicht überlebt hatte. Nun habe ich mir weitere LORA Radio Node V1 bestellt und werde an jedem die Umbauarbeiten mit einem neuen Quarz ausführen. Alle meine selbst getesteten LORA Radio Node V1 hatte enorme Taktabweichungen. Wenn man mit diesen eine Uhr programmiert hatte, würde diese am Tag mehr als 6 Minuten falsch laufen.

fertiges LORA Radio Node V1 mit BME280 Sensor

Einen sehr wichtigen Teil der Kommunikation als LoRaWAN Knoten übernimmt die Softwarebibliothek MCCI LoRaWAN LMIC library. Besonders ist darauf zu achten, dass die notwendigen Schlüssel im richtigen Format in die Software eingetragen werden. Mal ist es das little-endian Format mal ist es das big-endian Format. Bitte gut aufpassen!  Große Teile der Software habe ich komplett selbst geschrieben um einen möglichst geringen Speicherplatzbedarf zu realisieren. So ist die Abfrage des als Umweltsensor verwendeten BME280 komplett ohne jegliche Bibliothek realisiert und frei editierbar. Der BME280 benötigt wirklich schon einige Kompensationen um gute und plausible Werte von Temperatur, Luftdruck und Feuchtigkeit bereitzustellen. Im Datenblatt zum Bosch Sensor BME280 ist aber alles genau beschrieben. An diese Vorgabe / Referenz habe ich mich exakt gehalten.

Hier nun der Code, viel Spaß beim Nutzen und weiterentwickeln.

/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 * Copyright (c) 2018 Terry Moore, MCCI
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in
 * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
 * 
 * Jens 25.07.2021 
 * Clockerror = https://www.thethingsnetwork.org/forum/t/using-lmic-setclockerror-on-mcci-lmic-howto/39776
 *******************************************************************************/

#include 
#include <hal/hal.h>
#include 
#include 
#include <avr/sleep.h>
#include <avr/wdt.h>

#define debug                                                   // auskommentieren fuer Debugausgabe

int a = 0;
int trimm[24];                                                  // bme280 trimmwerte
int r_temp[3];                                                  // bme280 rohwert temperatur
int r_pres[3];                                                  // bme280 rohwert luftdruck
int r_humi[2];                                                  // bme280 rohwert luftfeuchte
int32_t adc_T;
int32_t adc_P;
int32_t adc_H;
uint16_t mwT;                                                   // real temperatur * 10 (252 = 25,2 °C)
uint16_t mwP;                                                   // real pressure * 10 (9991 = 99,1 hPa)
uint16_t mwH;                                                   // real huminity * 10 (405 = 40,5 %) 
uint8_t st = 0;                                                 // 5 startmessungen a 30 Sekunden

// register temperatur
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
// register pressure
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
// register humidity
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
uint8_t dig_H6;

//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// This EUI must be in little-endian format, so least-significant-byte
static const u1_t PROGMEM APPEUI[8]={0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA};
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={0xAB, 0x72, 0x35, 0x21, 0x63, 0x85, 0x23, 0x28};
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
static const u1_t PROGMEM APPKEY[16] = {0x15, 0x60, 0x63, 0xF1, 0x0D, 0x9E, 0xFB, 0x0F, 0x96, 0x26, 0xC9, 0xBC, 0x51, 0xE0, 0xA4, 0xE7};
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

//static uint8_t mydata[] = "Hello, world!";
static uint8_t mydata[] = "000000";
static osjob_t sendjob;
const unsigned TX_INTERVAL = 1800;

const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 5, LMIC_UNUSED_PIN},
};

#ifdef debug
void printHex2(unsigned v) {
    v &= 0xff;
    if (v < 16)
        Serial.print('0');
    Serial.print(v, HEX);
}
#endif

void onEvent (ev_t ev) {
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println(F("F1"));
            break;
        case EV_BEACON_FOUND:
            Serial.println(F("F2"));
            break;
        case EV_BEACON_MISSED:
            Serial.println(F("F3"));
            break;
        case EV_BEACON_TRACKED:
            Serial.println(F("F4"));
            break;
        case EV_JOINING:
            Serial.println(F("J-ING"));
            break;
        case EV_JOINED:
            Serial.println(F("J-NED"));
#ifdef debug
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print(F("netid: "));
              Serial.println(netid, DEC);
              Serial.print(F("devaddr: "));
              Serial.println(devaddr, HEX);
              Serial.print(F("AppSKey: "));
              for (size_t i=0; i<sizeof(artKey); ++i) {
                if (i != 0)
                  Serial.print("-");
                printHex2(artKey[i]);
              }
              Serial.println("");
              Serial.print(F("NwkSKey: "));
              for (size_t i=0; i<sizeof(nwkKey); ++i) {
                      if (i != 0)
                              Serial.print("-");
                      printHex2(nwkKey[i]);
              }
              Serial.println();
            }
#endif
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
	    // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_RFU1:
        ||     Serial.println(F("EV_RFU1"));
        ||     break;
        */
        case EV_JOIN_FAILED:
            Serial.println(F("F5"));
            break;
        case EV_REJOIN_FAILED:
            Serial.println(F("F6"));
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("F7"));
            if (LMIC.txrxFlags & TXRX_ACK)
              Serial.println(F("R ack"));
            if (LMIC.dataLen) {
              Serial.print(F("R"));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" b of payl"));
            }

            delay(500);
            for(uint16_t t=0;t<(TX_INTERVAL/8);t++) { // intervall siehe oben startSleeping(); } delay(500); os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(1), do_send); break; case EV_LOST_TSYNC: Serial.println(F("F8")); break; case EV_RESET: Serial.println(F("F9")); break; case EV_RXCOMPLETE: // data received in ping slot Serial.println(F("F10")); break; case EV_LINK_DEAD: Serial.println(F("F11")); break; case EV_LINK_ALIVE: Serial.println(F("F12")); break; /* || This event is defined but not used in the code. No || point in wasting codespace on it. || || case EV_SCAN_FOUND: || Serial.println(F("EV_SCAN_FOUND")); || break; */ case EV_TXSTART: Serial.println(F("F13")); break; case EV_TXCANCELED: Serial.println(F("F14")); break; case EV_RXSTART: /* do not print anything -- it wrecks timing */ break; case EV_JOIN_TXCOMPLETE: Serial.println(F("F15")); break; default: Serial.print(F("F16: ")); Serial.println((unsigned) ev); break; } } void do_send(osjob_t* j){ if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("F17")); } else { bme_mess(); bme_trim(); while(st == 0) { // bei start zwei messungen erforderlich st = st + 1; bme_mess(); bme_trim(); delay(1000); } mydata[0] = (mwT & 0xFF00)>>8;
        mydata[1] = (mwT & 0x00FF);
        mydata[2] = (mwP & 0xFF00)>>8;
        mydata[3] = (mwP & 0x00FF);
        mydata[4] = (mwH & 0xFF00)>>8;
        mydata[5] = (mwH & 0x00FF);
        LMIC_setTxData2(1, mydata, 6, 0);        

        Serial.println(F("P queued"));
    }
}

void setup() {
    Serial.begin(9600);
    delay(2000);
    Serial.println(F("Start"));
    os_init();
    LMIC_reset();
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

void bme_trim()  {
  uint8_t z = 0;
  Wire.beginTransmission(0x76);                                 // adresse um ein schieben !!!
  Wire.write(0x88);                                             // register adresse
  Wire.endTransmission();
  Wire.requestFrom(0x76, 24);                                   // 24 cal werte 0x88 - 0x9F lesen 
  while(Wire.available()) {  
    trimm[z] = Wire.read();
    z = z + 1;
  }
  Wire.endTransmission();
  
  dig_T1 = trimm[0] + (trimm[1]<<8);
  dig_T2 = trimm[2] + (trimm[3]<<8);
  dig_T3 = trimm[4] + (trimm[5]<<8);
  dig_P1 = trimm[6] + (trimm[7]<<8);
  dig_P2 = trimm[8] + (trimm[9]<<8);
  dig_P3 = trimm[10] + (trimm[11]<<8);
  dig_P4 = trimm[12] + (trimm[13]<<8);
  dig_P5 = trimm[14] + (trimm[15]<<8);
  dig_P6 = trimm[16] + (trimm[17]<<8);
  dig_P7 = trimm[18] + (trimm[19]<<8);
  dig_P8 = trimm[20] + (trimm[21]<<8);
  dig_P9 = trimm[22] + (trimm[23]<<8);

  Wire.beginTransmission(0x76);                                 // wert 0xA1 lesen 
  Wire.write(0xA1);
  Wire.endTransmission();
  Wire.requestFrom(0x76, 1);
  while(Wire.available()) {  
    dig_H1 = Wire.read();
  }
  Wire.endTransmission();

  z = 0;
  Wire.beginTransmission(0x76);                                 // werte 0xE1 - 0xE7 lesen 
  Wire.write(0xE1);
  Wire.endTransmission();
  Wire.requestFrom(0x76, 7);                                    // 7 cal werte 
  while(Wire.available()) {  
    trimm[z] = Wire.read();
    z = z + 1;
  }
  Wire.endTransmission();

  dig_H2 = trimm[0] + (trimm[1]<<8);
  dig_H3 = trimm[2];
  dig_H4 = (trimm[3]<<4) + (trimm[4] & 0x0F); dig_H5 = ((trimm[4] & 0xF0)>>4) + (trimm[5]<<4);
  dig_H6 = trimm[6];

  z = 0;                                                        // temperatur & druck roh lesen
  Wire.beginTransmission(0x76);                                 // adresse um ein schieben !!!
  Wire.write(0xF7);                                             // register adresse temperatur
  Wire.endTransmission();
  Wire.requestFrom(0x76, 3);
  while(Wire.available()) {  
    r_pres[z] = Wire.read();                                    // rohdruckwert
    z = z + 1;
  }
  
  z = 0;
  Wire.requestFrom(0x76, 3);
  while(Wire.available()) {  
    r_temp[z] = Wire.read();                                    // rohtemperatur
    z = z + 1;
  }
  
  z = 0;                                                        // humidity roh lesen
  Wire.beginTransmission(0x76);
  Wire.write(0xFD);                                             // register adresse temperatur
  Wire.endTransmission();
  Wire.requestFrom(0x76, 2);
  while(Wire.available()) {  
    r_humi[z] = Wire.read();                                    // rohfeuchte
    z = z + 1;
  }

  adc_T = ((int32_t)r_temp[0]<<12) + ((int32_t)r_temp[1]<<4) + (r_temp[2]>>4);
  int32_t var1, var2, t_fine;
  var1 = ((((adc_T>>3)-((int32_t)dig_T1<<1)))*((int32_t)dig_T2))>>11;
  var2 = (((((adc_T>>4)-((int32_t)dig_T1))*((adc_T>>4)-((int32_t)dig_T1)))>>12)*((int32_t)dig_T3))>>14;
  t_fine = var1 + var2;
  mwT = ((t_fine * 5 + 128) >> 8)/10;
  Serial.print(F("T = "));
  Serial.println(mwT);

  adc_P = ((int32_t)r_pres[0]<<12) + ((int32_t)r_pres[1]<<4) + (r_pres[2]>>4);
  int64_t var3, var4, p;
  int32_t pp;
  var3 = ((int64_t)t_fine) - 128000;
  var4 = var3 * var3 * (int64_t)dig_P6;
  var4 = var4 + ((var2*(int64_t)dig_P5)<<17);
  var4 = var4 + (((int64_t)dig_P4)<<35); var3 = ((var3 * var3 * (int64_t)dig_P3)>>8) + ((var3 * (int64_t)dig_P2)<<12);
  var3 = (((((int64_t)1)<<47)+var3))*((int64_t)dig_P1)>>33;
  if (var3 == 0) {
    p = 0;
    goto p_ende;
  }
  p = 1048576 - adc_P;
  p = (((p<<31)-var4)*3125)/var3; var3 = (((int64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
  var4 = (((int64_t)dig_P8) * p) >> 19;
  p = ((p + var3 + var4) >> 8) + (((int64_t)dig_P7)<<4);
  p_ende:
  mwP = p / 2560;
  Serial.print(F("P = "));
  Serial.println(mwP);

  adc_H = ((int32_t)r_humi[0]<<8) + r_humi[1];
  int64_t v_x1_u32r;
  int32_t hh;
  v_x1_u32r = (t_fine - ((int32_t)76800));
  v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
  v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
  v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
  hh = (v_x1_u32r>>12)/10.24;
  mwH = hh / 10;
  Serial.print(F("H = "));
  Serial.println(mwH);
}

void bme_mess()  {                                              // messung ausfuehren
  bool rdy = true;
  int8_t stat;
  Wire.beginTransmission(0x76);
  Wire.write(0xF2);                                             // ctrl_hum
  Wire.write(0b00000001);                                       // 1xHOS
  Wire.endTransmission();
  Wire.beginTransmission(0x76);
  Wire.write(0xF4);                                             // ctrl_meas
  Wire.write(0b00100101);                                       // 1xTOS, 2xPOS, MODE = 01 (forced)
  Wire.endTransmission();
  while (rdy)  {
    Wire.beginTransmission(0x76);
    Wire.write(0xF3);                                           // adresse status
    Wire.endTransmission();
    Wire.requestFrom(0x76, 1);
    while(Wire.available()) {  
      stat = Wire.read();
      stat = stat & 0x08;
      if (stat == 0)  {
        rdy = false;
      }
    }
  }
}

ISR (WDT_vect) {
   wdt_disable(); 
}

void startSleeping() {
    // clear various "reset" flags
    MCUSR = 0;                                                  // allow changes, disable reset, enable Watchdog interrupt
    WDTCSR = bit (WDCE) | bit (WDE);                            // set interval (see datasheet p55)
    WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);              // set WDIE, and 8 seconds delay
    //WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);            // same with 1 second
    wdt_reset();                                                // start watchdog timer
    set_sleep_mode (SLEEP_MODE_PWR_DOWN);                       // prepare for powerdown  
    sleep_enable();  
    sleep_cpu ();                                               // power down !
}

Beitrag vom 12.03.2021

Glück gehabt – ein LoRaWAN Gateway ist erreichbar

In meiner Nähe muss jemand in der letzten Zeit ein LoRaWAN Gateway installiert haben. Ich habe mir auch schon ein kleines LoRa Modul bestehend aus ESP8266 und RFM95 aufgebaut und kann damit Datenpakete zu THE THINGS NETWORK (oder neu… The Things Stack) senden. Hier reicht ein simple Account aus, um dann diese Paket in der Cloud weiter verarbeiten zu können. Wenn ich wieder etwas Zeit habe werde ich die gesendeten Paket zu THINGSPEAK weitersenden und hier darstellen. Mit der LoRaWAN Funktechnik kann eine Reichweite von bis zu 10Km und extrem energiesparende Baugruppen erstellen. Das Signal durchdringt auch problemlos Wände und kann auch aus Kellern versendet werden. Eine größere Batterie reicht dann bis zu 10 Jahre.

Mal sehen, vielleicht werde ich wieder einen Controller aus der Serie MSP430 verwenden. Mit kleiner Taktfrequenz reicht eine Knopfzelle um diesen 3 und mehr Jahre zu versorgen.

Vorderseite eines als RFM95 bestellten LoRaWan Modus – eindeutig steht auf dem Chip etwas von RFM96

Vorderseite RFM95 Modul - LORAWAN

Das ist die Rückseite vom gleichen Modul – siehe da, RFM95 und die für Deutschland gedachte Frequenz von 868 MHz. Trotz der optischen Unstimmigkeit funktioniert das Modul einwandfrei.

Rückseite RFM95 Modul - LORAWAN

Hier zwei Softwarebeispiele wobei der ABP-Mode nach wenigen Sekunden eine Verbindung aufbaut. Wenn man den OTAA Modus verwendet, kann der Verbindungsaufbau auch schon mal 30 Minuten und mehr dauern.

Ich habe hier die Bibliothek von „Beelan_LoRaWAN“ verwendet.

Es ist unbedingt erforderlich, die Datei „config.h“ auf die in Deutschland zu verwendenden Frequenzen anzupassen.

/**
   Example of ABP device
   Authors:
          Ivan Moreno
          Eduardo Contreras
    June 2019

   This code is beerware; if you see me (or any other collaborator
   member) at the local, and you've found our code helpful,
   please buy us a round!
   Distributed as-is; no warranty is given.
*/
#include 

#define LED        2                                        // Port Wemos D4 = GPIO 2

//ABP Credentials
const char *devAddr = "26123456";
const char *nwkSKey = "09C4903E584EC02E4CA50123456789BB";
const char *appSKey = "D247B2E00721C3CCD3EB01234567895D";

const unsigned long interval = 30000;                        // 30 s interval to send message
unsigned long previousMillis = 0;                            // will store last time message sent
unsigned int counter = 80;                                   // message counter

char myStr[50];
char outStr[255];
byte recvStatus = 0;

const sRFM_pins RFM_pins = {
  .CS = 16,
  .RST = 2,
  .DIO0 = 15,
  .DIO1 = 2,
  .DIO2 = 2,
  .DIO5 = 2,
};

void setup() {
  pinMode(LED, OUTPUT);                                     // LED pin is output
  digitalWrite(LED, HIGH);                                  // LED off
  // Setup loraid access
  Serial.begin(9600);
  while (!Serial);
  if (!lora.init()) {
    Serial.println("RFM95 not detected");
    delay(1000);
    return;
  }

  // Set LoRaWAN Class change CLASS_A or CLASS_C
  lora.setDeviceClass(CLASS_A);

  // Set Data Rate
  lora.setDataRate(SF7BW125);

  // set channel to random
  lora.setChannel(MULTI);

  // Put ABP Key and DevAddress here
  lora.setNwkSKey(nwkSKey);
  lora.setAppSKey(appSKey);
  lora.setDevAddr(devAddr);
}

void loop() {
  // Check interval overflow
  if (millis() - previousMillis > interval) {
    previousMillis = millis();

    sprintf(myStr, "Counter-%d", counter);

    Serial.print("Sending: ");
    Serial.println(myStr);

    lora.sendUplink(myStr, strlen(myStr), 0, 1);
    counter++;
    if(counter>90) counter=80;
    digitalWrite(LED, LOW);                                   // LED short on
    delay(50);
    digitalWrite(LED, HIGH);                                  // LED off
    delay(950);
  }

  recvStatus = lora.readData(outStr);
  if (recvStatus) {
    Serial.println(outStr);
  }

  // Check Lora RX
  lora.update();
}
/**
Example of OTAA device
Authors:
Ivan Moreno
Eduardo Contreras
June 2019

This code is beerware; if you see me (or any other collaborator
member) at the local, and you've found our code helpful,
please buy us a round!
Distributed as-is; no warranty is given.
*/
#include

#define LED 2 // Port Wemos D4 = GPIO 2

// OTAA credentials
const char *devEui = "1ACB012345678901";
const char *appEui = "70B3012345678901";
const char *appKey = "FC1D25A7309E36C8FC27701234567891";

const unsigned long interval = 20000; // 20 s interval to send message
unsigned long previousMillis = 0; // will store last time message sent
unsigned int counter = 0; // message counter

char myStr[50];
char outStr[255];
byte recvStatus = 0;

const sRFM_pins RFM_pins = {
.CS = 16,
.RST = 2,
.DIO0 = 15,
.DIO1 = 2,
.DIO2 = 2,
.DIO5 = 2,
};

void setup() {
pinMode(LED, OUTPUT); // LED pin is output
digitalWrite(LED, HIGH); // LED off
// Setup loraid access
Serial.begin(9600);
while (!Serial);
if (!lora.init()) {
Serial.println("RFM95 not detected");
delay(5000);
return;
}

// Set LoRaWAN Class change CLASS_A or CLASS_C
lora.setDeviceClass(CLASS_A);

// Set Data Rate
lora.setDataRate(SF9BW125);

// set channel to random
lora.setChannel(MULTI);

// Put OTAA Key and DevAddress here
lora.setDevEUI(devEui);
lora.setAppEUI(appEui);
lora.setAppKey(appKey);

// Join procedure
bool isJoined;
do {
Serial.println("Joining...");
isJoined = lora.join();

//wait for 10s to try again
digitalWrite(LED, LOW); // LED on
delay(50);
digitalWrite(LED, HIGH); // LED on
delay(950);
} while (!isJoined);
Serial.println("Joined to network");
digitalWrite(LED, HIGH); // LED on
}

void loop() {
// Check interval overflow
if (millis() - previousMillis > interval) {
previousMillis = millis();

sprintf(myStr, "Counter-%d", counter);

Serial.print("Sending: ");
Serial.println(myStr);

lora.sendUplink(myStr, strlen(myStr), 0, 1);
counter++;
}

recvStatus = lora.readData(outStr);
if (recvStatus) {
Serial.println(outStr);
}

// Check Lora RX
lora.update();
}