Got Adafruit Feather 32u4 LoRa Radio to work and here is how

Does the gateway (i.e. a kerlink one for me) needs to be tuned with ttn ?
Cannot it work with the “normal” spn version ? (v4.0.1)

Thanks.

TTN does provide master gateway configuration files on github: https://github.com/TheThingsNetwork/gateway-conf

Man, you save my life, thank you very much.

Has anyone managed to get the Adafruit Feather 32u4 to operate consistently?
I have built the device specified in Creating a TTN node
https://www.thethingsnetwork.org/labs/story/creating-a-ttn-node
it operates without problems – transmitting data to my application every minute or so

I modified the code to run on the Feather 32u4 and the device joins and transmits to my application
However, the EV_TXCOMPLETE (includes waiting for RX windows) message does not appear for a minute or two (using the Creating a TTN node device the messages appears within a second or so)
Messages are then transmitted at random intervals varying from a minute to 10 minute – after a time it stops transmitting at all.

Notes

  1. I am using a The Things Gateway model TTN-001-868-1.0
  2. Pins IO1and 6 are connected
  3. // Pin mapping
    const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
    };
  4. set LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
  5. changed to use Serial1 hardware – helped a bi

where is your 'modified code ?

  • and to format your code in a friendly forum way… read THIS :wink:

commented out references to BMP280 and add pin definitions

//#include "i2c_BMP280.h"
//#define Serial Serial1
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include "LowPower.h"
#include "i2c.h"

#define Serial Serial1
//BMP280 bmp280;

#include <Arduino.h>

int sleepcycles = 7;  // every sleepcycle will last 8 secs, total sleeptime will be sleepcycles * 8 sec
bool joined = false;
bool sleeping = false;
#define LedPin 2     // pin 13 LED is not used, because it is connected to the SPI port

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.

static const u1_t DEVEUI[8]  = { 0x89, 0x49, 0x65, 0x98, 0xEF, 0xAB, 0x12, 0x00 };
static const u1_t APPEUI[8] = { 0xCB, 0x81, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
// The key shown here is the semtech default key.
static const u1_t APPKEY[16] = { 0x0A, 0xC2, 0x09, 0xCD, 0x16, 0x5E, 0x1E, 0x7D, 0xA3, 0xA1, 0xD6, 0x38, 0x23, 0xFD, 0x65, 0x0C };

void os_getArtEui (u1_t* buf) {
  memcpy(buf, APPEUI, 8);
}

// provide DEVEUI (8 bytes, LSBF)
void os_getDevEui (u1_t* buf) {
  memcpy(buf, DEVEUI, 8);
}

// provide APPKEY key (16 bytes)
void os_getDevKey (u1_t* buf) {
  memcpy(buf, APPKEY, 16);
}

static osjob_t sendjob;
static osjob_t initjob;

// Pin mapping is hardware specific.
// Pin mapping Doug Larue PCB
// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
};

void onEvent (ev_t ev) {
  int i,j;
  switch (ev) {
    case EV_SCAN_TIMEOUT:
      Serial.println(F("EV_SCAN_TIMEOUT"));
      break;
    case EV_BEACON_FOUND:
      Serial.println(F("EV_BEACON_FOUND"));
      break;
    case EV_BEACON_MISSED:
      Serial.println(F("EV_BEACON_MISSED"));
      break;
    case EV_BEACON_TRACKED:
      Serial.println(F("EV_BEACON_TRACKED"));
      break;
    case EV_JOINING:
      Serial.println(F("EV_JOINING"));
      break;
    case EV_JOINED:
      Serial.println(F("EV_JOINED"));
      // Disable link check validation (automatically enabled
      // during join, but not supported by TTN at this time).
      LMIC_setLinkCheckMode(0);
      digitalWrite(LedPin,LOW);
      // after Joining a job with the values will be sent.
      joined = true;
      break;
    case EV_RFU1:
      Serial.println(F("EV_RFU1"));
      break;
    case EV_JOIN_FAILED:
      Serial.println(F("EV_JOIN_FAILED"));
      break;
    case EV_REJOIN_FAILED:
      Serial.println(F("EV_REJOIN_FAILED"));
      // Re-init
      os_setCallback(&initjob, initfunc);
      break;
    case EV_TXCOMPLETE:
      sleeping = true;
        if (LMIC.dataLen) {
        // data received in rx slot after tx
        // if any data received, a LED will blink
        // this number of times, with a maximum of 10
        Serial.print(F("Data Received: "));
        Serial.println(LMIC.frame[LMIC.dataBeg],HEX);
        i=(LMIC.frame[LMIC.dataBeg]);
        // i (0..255) can be used as data for any other application
        // like controlling a relay, showing a display message etc.
        if (i>10){
          i=10;     // maximum number of BLINKs
        }
          for(j=0;j<i;j++)
          {
            digitalWrite(LedPin,HIGH);
            delay(200);
            digitalWrite(LedPin,LOW);
            delay(400);
          }
      }
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      delay(50);  // delay to complete Serial Output before Sleeping

      // Schedule next transmission
      // next transmission will take place after next wake-up cycle in main loop
      break;
    case EV_LOST_TSYNC:
      Serial.println(F("EV_LOST_TSYNC"));
      break;
    case EV_RESET:
      Serial.println(F("EV_RESET"));
      break;
    case EV_RXCOMPLETE:
      // data received in ping slot
      Serial.println(F("EV_RXCOMPLETE"));
      break;
    case EV_LINK_DEAD:
      Serial.println(F("EV_LINK_DEAD"));
      break;
    case EV_LINK_ALIVE:
      Serial.println(F("EV_LINK_ALIVE"));
      break;
    default:
      Serial.println(F("Unknown event"));
      break;
  }
}
  float temperature=10,pascal=999;

void do_send(osjob_t* j) {
  byte buffer[2]={11,22};

    uint16_t t_value, p_value, s_value;
/*    bmp280.awaitMeasurement();
    bmp280.getTemperature(temperature);
    bmp280.getPressure(pascal);
    bmp280.triggerMeasurement();*/
    pascal=pascal/100;
    Serial.print(" Pressure: ");
    Serial.print(pascal);
    Serial.print(" Pa; T: ");
    Serial.print(temperature);
    Serial.println(" C");
    temperature+=1;
    // getting sensor values
  
    temperature = constrain(temperature,-24,40);  //temp in range -24 to 40 (64 steps)
    pascal=constrain(pascal,970,1034);    //pressure in range 970 to 1034 (64 steps)*/
        t_value=int16_t((temperature*(100/6.25)+2400/6.25)); //0.0625 degree steps with offset
                                                      // no negative values
        Serial.print(F("decoded TEMP: "));
        Serial.print(t_value,HEX);
        p_value=int16_t((pascal-970)/1); //1 mbar steps, offset 970.
        Serial.print(F(" decoded Pascal: "));
        Serial.print(p_value,HEX);
        s_value=(p_value<<10) + t_value;  // putting the bits in the right place
        Serial.print(F(" decoded sent: "));
        Serial.println(s_value,HEX);
        buffer[0]=s_value&0xFF; //lower byte
        buffer[1]=s_value>>8;   //higher byte
    // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    Serial.println(F("OP_TXRXPEND, not sending"));
  } else {
    // Prepare upstream data transmission at the next possible time.
    LMIC_setTxData2(1, (uint8_t*) buffer, 2 , 0);
    Serial.println(F("Sending: "));
  }
}

// initial job
static void initfunc (osjob_t* j) {
    // reset MAC state
    LMIC_reset();
    // start joining
    LMIC_startJoining();
    // init done - onEvent() callback will be invoked...
}

void setup()
  {
    delay(1000);
    while (!Serial);
  delay(250);
  Serial.begin(9600);  Serial1.begin(9600);
  delay(250);
  Serial.println(F("Starting now"));
    //Serial.print("Probe BMP280: ");
   // if (bmp280.initialize()) Serial.println("Sensor found");
   // else
   // {
    //    Serial.println("Sensor missing");
    //    //while (1) {}
   // }

    // onetime-measure:
   /// bmp280.setEnabled(0);
   // bmp280.triggerMeasurement();
    
  // if LED is connected to pin 10, it has to be defined before any SPI initialization else
  // it will be used as SS (Slave Select) and controlled by the SPI module
      pinMode(LedPin, OUTPUT);
//LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
  os_init();
  // Reset the MAC state. Session and pending data transfers will be discarded.
  os_setCallback(&initjob, initfunc);
  LMIC_reset();
LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
}

unsigned long time;
void loop()
{
//Serial1.print("*");
    // start OTAA JOIN
    if (joined==false)
    {

      os_runloop_once();

    }
    else
    {
      do_send(&sendjob);    // Sent sensor values
      while(sleeping == false)
      {
        os_runloop_once();
      }
      sleeping = false;
      for (int i=0;i<sleepcycles;i++)
      {
          LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);    //sleep 8 seconds
      }
    }

      digitalWrite(LedPin,((millis()/100) % 2) && (joined==false)); // only blinking when joining and not sleeping
}```
4 Likes

after deepsleep doesn’t the 32U4 looses USB serial connection ?
(9600 is slow - use for hw serial 57600 and up)

after compiling… how much mem left ?

I don’t see any LMIC_setDrTxpow in your code; what SF are you using? Did you see LMiC's TX_COMPLETE event takes 20-30 seconds to fire - #11 by arjanvanb?

1 Like

Perhaps it will be helpful: updating IBM’s LMIC library version from “1.5.0+arduino1” to “1.5.0+arduino2” seems to shrink the size of the code…

not making much progress on the Adafruit feather 32U4 device
I am now testing using the example code from
\Documents\Arduino\libraries\arduino-lmic-master\examples\ttn-otaa
which on the Dragino Lora Shield and the TTN node joins and transmits and receives data without problems

for the Adafruit feather 32U4 the code was modified to use the correct pins

// Lora OTAA for Adafruit Feather 32u4 

/*
// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
   // .dio = {3, 6, LMIC_UNUSED_PIN},
};*/


/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 *
 * 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 config.h.
 *
 *******************************************************************************/


//#define Serial Serial1
#include <lmic.h>
#include <hal\hal.h>
#include <SPI.h>
//#define Serial Serial1
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={ 0xCB, 0x81, 0x00, 0xD0, 0x7E, 0xD5, 0xB3, 0x70  };
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]={  0x89, 0x49, 0x65, 0x98, 0xEF, 0xAB, 0x12, 0x00};
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
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = { 0x0A, 0xC2, 0x09, 0xCD, 0x16, 0x5E, 0x1E, 0x7D, 0xA3, 0xA1, 0xD6, 0x38, 0x23, 0xFD, 0x65, 0x0C  };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t mydata[] ={01,02};//"Hello, world!";
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
   // .dio = {3, 6, LMIC_UNUSED_PIN},
};

void onEvent (ev_t ev) {
    Serial.print(os_getTime());
    Serial.print(": ");
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println(F("EV_SCAN_TIMEOUT"));
            break;
        case EV_BEACON_FOUND:
            Serial.println(F("EV_BEACON_FOUND"));
            break;
        case EV_BEACON_MISSED:
            Serial.println(F("EV_BEACON_MISSED"));
            break;
        case EV_BEACON_TRACKED:
            Serial.println(F("EV_BEACON_TRACKED"));
            break;
        case EV_JOINING:
            Serial.println(F("EV_JOINING"));
            break;
        case EV_JOINED:
            Serial.println(F("EV_JOINED"));

            // Disable link check validation (automatically enabled
            // during join, but not supported by TTN at this time).
            LMIC_setLinkCheckMode(0);
            break;
        case EV_RFU1:
            Serial.println(F("EV_RFU1"));
            break;
        case EV_JOIN_FAILED:
            Serial.println(F("EV_JOIN_FAILED"));
            break;
        case EV_REJOIN_FAILED:
            Serial.println(F("EV_REJOIN_FAILED"));
            break;
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if (LMIC.txrxFlags & TXRX_ACK)
              Serial.println(F("Received ack"));
            if (LMIC.dataLen) {
              Serial.println(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));
              Serial.print(F("Data Received: "));
              for(int i=0;i< LMIC.dataLen;i++)
                 { Serial.print(" 0x"); Serial.print((LMIC.frame+LMIC.dataBeg)[i],  HEX);}
              Serial.println();
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        case EV_LOST_TSYNC:
            Serial.println(F("EV_LOST_TSYNC"));
            break;
        case EV_RESET:
            Serial.println(F("EV_RESET"));
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            Serial.println(F("EV_RXCOMPLETE"));
            break;
        case EV_LINK_DEAD:
            Serial.println(F("EV_LINK_DEAD"));
            break;
        case EV_LINK_ALIVE:
            Serial.println(F("EV_LINK_ALIVE"));
            break;
         default:
            Serial.println(F("Unknown event"));
            break;
    }
}

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        mydata[0]++;mydata[1]++;
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    delay(1000);
    while(!Serial);
    delay(1000);
    Serial.begin(115200);
    Serial.println(F("Starting"));

    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
Serial.println(LMIC.freq);
}

void loop() {
    os_runloop_once();
}

while working on the TTN node I had changed the default SF7 to SF9 - changing it back to SF7 helped the connecting and also moving the gateway further away helped
I have three of the Adafruit feather 32u4 devices - all give inconsistent results
sometimes join fails, when it works data is sometimes transmitted, sooner or later the device hangs at Packet queued if I place my hand near the device it then wakes up and displays
12127800: EV_TXCOMPLETE (includes waiting for RX windows)
FAILURE
D:\xxx\Documents\Arduino\libraries\arduino-lmic-master\src\lmic\lmic.c:1890

any ideas?

I have been working with a module that’s equal to adafruits feather 32u4.

snip/

LMIC_reset();

// Set up the channels used by the Things Network, which corresponds
// to the defaults of most gateways. Without this, only three base
// channels from the LoRaWAN specification are used, which certainly
// works, so it is good for debugging, but can overload those
// frequencies, so be sure to configure the full frequency range of
// your network here (unless your network autoconfigures them).
// Setting up channels should happen after LMIC_setSession, as that
// configures the minimal channel set.

#if defined(CFG_eu868)
LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
#endif

/snip

2 key tips to make it work with the generic lmic examples:

  1. Don’t use OTAA - use APB
  2. Reset the Frame Counter every time you reset your device (otherwise you see traffic from your device on your gateway (yes, its better to have one around for testing) but it never reaches your device due to the frame counter check)

I posted similar instructions for the feather m0 lora here: Adafruit feather m0 lora 900 end-to-end instructions

the APB example from \IBM_LIMC_Framerwork\ttn-abp gives
Starting
Packet queued
it then hangs - when I place my hand near the device it then gives
13145803: EV_TXCOMPLETE (includes waiting for RX windows)
FAILURE
D:\OneDrive - Environmental Products and Services Ltd\Documents\Arduino\libraries\arduino-lmic-master\src\lmic\lmic.c:1890

  1. Check if DIO1 connection to D6 is wired correctly. Measure from RFM95 DIO1 pin, not the pin at the end of the longer pin header as there seem to be boards where connection between the RFM and that pin is missing.
  2. Could you try with the code at https://github.com/kersing/node-workshop/blob/master/lora32u4.md? I’ve been using it for two workshops without issues. (The only issue constantly reoccurring is people not using LSB for the EUIs and MSB for the APPKEY)
3 Likes

Kersing noted
Check if DIO1 connection to D6 is wired correctly. Measure from RFM95 DIO1 pin, not the pin at the end of the longer pin header as there seem to be boards where connection between the RFM and that pin is missing.
this proved to be the case on all three boards I have (one from the UK two from China) in that the RFM95 DIO1 pin was not connected!
Connecting DIO1 to pin 6 on one of my boards worked transmitting and receiving data

Starting
CFG_eu868
Packet queued
126195: EV_JOINING
888098: EV_JOINED
4901286: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
8783081: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
12664725: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
39774617: EV_TXCOMPLETE (includes waiting for RX windows)
Received
2 bytes of payload
Data Received:  0x11 0x22
Packet queued
43656533: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
47476518: EV_TXCOMPLETE (includes waiting for RX windows)
Received
2 bytes of payload
Data Received:  0xFB 0xFC
Packet queued
51358293: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
55239949: EV_TXCOMPLETE (includes waiting for RX windows)
Packet queued
59060251: EV_TXCOMPLETE (includes waiting for RX windows)
Received
4 bytes of payload
Data Received:  0x34 0x56 0x78 0x90

1 Like

Had the same issues, as after some time I again built up the Feather 32u4 LoRa on a breadboard. Like @kersing already said, it is important to do the wiring from DIO1 to Pin 6 correctly.
I actually made the mistake to make the wire connection to pin PD6 (which is the sixth pin counted down from the connector for the LiPo battery. Instead, use pin 6 as counted in the Arduino IDE, which has label “6” printed on the board and is the fourth pin (on the side of the LiPo connector) counted from the opposite direction of the LiPo connector).

I have three of the 32u4 Lora boards two from china and one from UK
two of the boards worked OK with OTTA once I connected the FRM95 DIO1 to pin 6 the other did not
Looking with a scope DIO1 to pin 6 was going high as expected once the Tx was complete
In the end I connected DIO1 to pin 5 and changed the FRM95 pin mapping to reflect this and it then worked OK joining, transmitting and recieving data using the Things network
it looks like there is a PCB fault in that 6 is not connected correctly on this particular PCB

I assume that the original Adafruit 32u4 Lora boards do not have these problems

You also might want to have a look at the LMIC stack by @Charles Hallard. This stack allows you to use any DIO configuration including no DIO’s attached to the microprocessor at all.

1 Like

But beware that one has last changed Aug 11, 2016, while the original meanwhile has greatly decreased the time for the EV_TXCOMPLETE event to fire, which might be nice to have.

To see the differences with @matthijs’ original:

https://github.com/matthijskooijman/arduino-lmic/compare/master...hallard:master