IBM LoRa MAC in C (LMiC) mbed library port

Dependents:   lora-temperature LoRaWAN-lmic-app_HS LoRaWAN-lmic-app_huynh

LoRa WAN in C for sx1276 shield

Currently version 1.5


LoRaWAN network configuration for end-device

The following three pieces of information uniquely identifies end-device to network to allow over-the-air activation. These are stored in the end-device prior to join procedure.

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t APPEUI[8]  = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x25, 0x00 };

This is copied into LMIC by os_getArtEui() callback function in application.

DevEUI

End-device ID, unique to each end-node.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t DEVEUI[8]  = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x25, 0x00 }; 

This is copied into LMIC by os_getDevEui() callback function in application.

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

static const u1_t DEVKEY[16] = { 0xe4, 0x72, 0x71, 0xc5, 0xf5, 0x30, 0xa9, 0x9f, 0xcf, 0xc4, 0x0e, 0xab, 0xea, 0xd7, 0x19, 0x42 };

This is copied into LMIC by os_getDevKey() callback function in application.

Using over-the air activation, the end-device (LMIC) performs a join procedure every time it starts for first time, or has lost session context information. When join procedure has successfully completed, the end-device will have a network session key (NwkSKey) and an application session key (AppSKey), which are used for encryption and message integrity check.


US915 configuration with http://us01-iot.semtech.com/

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.
CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Revision:
1:d3b7bde3995c
Parent:
0:62d1edcc13d1
Child:
2:974cafbfb159
Child:
3:519c71d29a06
--- a/radio.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ b/radio.cpp	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -14,15 +14,16 @@
  *                              USE_SMTC_RADIO_DRIVER preprocessing directive
  *                              in lmic.h
  *******************************************************************************/
+
 #include "lmic.h"
 
 #if USE_SMTC_RADIO_DRIVER
 #include "sx1276-hal.h"
 
 /*!
- * Syncword for lora networks (nibbles swapped)
+ * Syncword for lora networks
  */
-#define LORA_MAC_SYNCWORD           0x34
+#define LORA_MAC_SYNCWORD                           0x34
 
 /*
  * Callback functions prototypes
@@ -81,7 +82,7 @@
 {
     ostime_t now = os_getTime( );
     // save exact tx time
-    LMIC.txend = now - us2osticks( 43 ); // TXDONE FIXUP
+    LMIC.txend = now - us2osticks( RADIO_WAKEUP_TIME ); // TXDONE FIXUP
     
     // go from stanby to sleep
     Radio.Sleep( );
@@ -93,14 +94,18 @@
 {
     ostime_t now = os_getTime( );
     // save exact rx time
-    LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+    if( getBw( LMIC.rps ) == BW125 )
+    {
+        now -= LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+    }
+    LMIC.rxtime = now;
     // read the PDU and inform the MAC that we received something
     LMIC.dataLen = size;
     // now read the FIFO
     memcpy( LMIC.frame, payload, size );
     // read rx quality parameters
-    LMIC.rxq.snr  = snr; // SNR [dB] * 4
-    LMIC.rxq.rssi = rssi; // RSSI [dBm] (-196...+63)
+    LMIC.snr  = snr; // SNR [dB] * 4
+    LMIC.rssi = rssi; // RSSI [dBm] (-196...+63)
 
     // go from stanby to sleep
     Radio.Sleep( );
@@ -113,7 +118,8 @@
     ostime_t now = os_getTime( );
 
     // indicate error
-
+    LMIC.dataLen = 0;
+    
     // go from stanby to sleep
     Radio.Sleep( );
     // run os job (use preset func ptr)
@@ -230,7 +236,7 @@
         else
         { // LoRa modem
             
-            Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, false, 3e6 );
+            Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, false, 3e6 );
         }
 
         //starttx( ); // buf=LMIC.frame, len=LMIC.dataLen
@@ -249,8 +255,14 @@
         }
         else
         { // LoRa modem
-            
-            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, false );
+            if( ( getSf( LMIC.rps ) <= SF9 ) && ( LMIC.rxsyms < 8 ) )
+            {
+                Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms + 3, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+            }
+            else
+            {
+                Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+            }
         }
 
         // now instruct the radio to receive
@@ -279,10 +291,10 @@
         }
         else
         { // LoRa modem
-            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, true );
+            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, true );
         }
 
-        //startrx( RXMODE_SCAN) ; // buf = LMIC.frame
+        //startrx( RXMODE_SCAN ); // buf = LMIC.frame
         Radio.Rx( 0 );
         break;
     }
@@ -448,7 +460,7 @@
 #define SX1276_MC3_AGCAUTO                 0x04
 
 // preamble for lora networks (nibbles swapped)
-#define LORA_MAC_PREAMBLE           0x34
+#define LORA_MAC_PREAMBLE                  0x34
 
 #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
 #ifdef CFG_sx1276_radio
@@ -539,6 +551,12 @@
 #error Missing CFG_sx1272_radio/CFG_sx1276_radio
 #endif
 
+#define RADIO_DBG
+#if defined(RADIO_DBG)
+DigitalOut txStateIo( PB_8 );
+DigitalOut rxStateIo( PB_9 );
+#endif
+
 static void writeReg (u1_t addr, u1_t data ) {
     hal_pin_nss(0);
     hal_spi(addr | 0x80);
@@ -576,7 +594,7 @@
     writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
 }
 
-static void opmodeLora(void) {
+static void opmodeLora() {
     u1_t u = OPMODE_LORA;
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
@@ -584,7 +602,7 @@
     writeReg(RegOpMode, u);
 }
 
-static void opmodeFSK(void) {
+static void opmodeFSK() {
     u1_t u = 0;
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
@@ -593,79 +611,79 @@
 }
 
 // configure LoRa modem (cfg1, cfg2)
-static void configLoraModem (void) {
+static void configLoraModem () {
     sf_t sf = getSf(LMIC.rps);
 
 #ifdef CFG_sx1276_radio
-    u1_t mc1 = 0, mc2 = 0, mc3 = 0;
+        u1_t mc1 = 0, mc2 = 0, mc3 = 0;
 
-    switch (getBw(LMIC.rps)) {
-    case BW125: mc1 |= SX1276_MC1_BW_125; break;
-    case BW250: mc1 |= SX1276_MC1_BW_250; break;
-    case BW500: mc1 |= SX1276_MC1_BW_500; break;
-    default:
-        ASSERT(0);
-    }
-    switch( getCr(LMIC.rps) ) {
-    case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
-    case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
-    case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
-    case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
-    default:
-        ASSERT(0);
-    }
+        switch (getBw(LMIC.rps)) {
+        case BW125: mc1 |= SX1276_MC1_BW_125; break;
+        case BW250: mc1 |= SX1276_MC1_BW_250; break;
+        case BW500: mc1 |= SX1276_MC1_BW_500; break;
+        default:
+            ASSERT(0);
+        }
+        switch( getCr(LMIC.rps) ) {
+        case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
+        case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
+        case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
+        case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
+        default:
+            ASSERT(0);
+        }
 
-    if (getIh(LMIC.rps)) {
-        mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
-        writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
-    }
-    // set ModemConfig1
-    writeReg(LORARegModemConfig1, mc1);
+        if (getIh(LMIC.rps)) {
+            mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
+            writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+        }
+        // set ModemConfig1
+        writeReg(LORARegModemConfig1, mc1);
 
-    mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
-    if (getNocrc(LMIC.rps) == 0) {
-        mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
-    }
-    writeReg(LORARegModemConfig2, mc2);
-    
-    mc3 = SX1276_MC3_AGCAUTO;
-    if (sf == SF11 || sf == SF12) {
-        mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
-    }
-    writeReg(LORARegModemConfig3, mc3);
+        mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
+        if (getNocrc(LMIC.rps) == 0) {
+            mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
+        }
+        writeReg(LORARegModemConfig2, mc2);
+        
+        mc3 = SX1276_MC3_AGCAUTO;
+        if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+            mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
+        }
+        writeReg(LORARegModemConfig3, mc3);
 #elif CFG_sx1272_radio
-    u1_t mc1 = (getBw(LMIC.rps)<<6);
+        u1_t mc1 = (getBw(LMIC.rps)<<6);
 
-    switch( getCr(LMIC.rps) ) {
-    case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
-    case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
-    case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
-    case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
-    }
-    
-    if (sf == SF11 || sf == SF12) {
-        mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
-    }
-    
-    if (getNocrc(LMIC.rps) == 0) {
-        mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
-    }
-    
-    if (getIh(LMIC.rps)) {
-        mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
-        writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
-    }
-    // set ModemConfig1
-    writeReg(LORARegModemConfig1, mc1);
-    
-    // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
-    writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
+        switch( getCr(LMIC.rps) ) {
+        case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
+        case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
+        case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
+        case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
+        }
+        
+        if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+            mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
+        }
+        
+        if (getNocrc(LMIC.rps) == 0) {
+            mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
+        }
+        
+        if (getIh(LMIC.rps)) {
+            mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
+            writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+        }
+        // set ModemConfig1
+        writeReg(LORARegModemConfig1, mc1);
+        
+        // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
+        writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
 #else
 #error Missing CFG_sx1272_radio/CFG_sx1276_radio
 #endif /* CFG_sx1272_radio */
 }
 
-static void configChannel (void) {
+static void configChannel () {
     // set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
     u8_t frf = ((u8_t)LMIC.freq << 19) / 32000000;
     writeReg(RegFrfMsb, (u1_t)(frf>>16));
@@ -675,26 +693,26 @@
 
 
 
-static void configPower (void) {
+static void configPower () {
 #ifdef CFG_sx1276_radio
     // no boost used for now
     s1_t pw = (s1_t)LMIC.txpow;
-    if(pw > 14) {
-    pw = 14;
-    } else if(pw < -1) {
-    pw = -1;
+    if(pw >= 17) {
+        pw = 15;
+    } else if(pw < 2) {
+        pw = 2;
     }
     // check board type for BOOST pin
-    writeReg(RegPaConfig, (u1_t)(0x00|((pw+1)&0xf)));
+    writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
     writeReg(RegPaDac, readReg(RegPaDac)|0x4);
 
 #elif CFG_sx1272_radio
     // set PA config (2-17 dBm using PA_BOOST)
     s1_t pw = (s1_t)LMIC.txpow;
     if(pw > 17) {
-    pw = 17;
+        pw = 17;
     } else if(pw < 2) {
-    pw = 2;
+        pw = 2;
     }
     writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
 #else
@@ -702,12 +720,16 @@
 #endif /* CFG_sx1272_radio */
 }
 
-static void txfsk (void) {
+static void txfsk () {
     // select FSK modem (from sleep mode)
     writeReg(RegOpMode, 0x10); // FSK, BT=0.5
     ASSERT(readReg(RegOpMode) == 0x10);
     // enter standby mode (required for FIFO loading))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // set bitrate
     writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
     writeReg(FSKRegBitrateLsb, 0x80);
@@ -743,9 +765,12 @@
     
     // now we actually start the transmission
     opmode(OPMODE_TX);
+#if defined(RADIO_DBG)
+    txStateIo = 1;
+#endif
 }
 
-static void txlora (void) {
+static void txlora () {
     // select LoRa modem (from sleep mode)
     //writeReg(RegOpMode, OPMODE_LORA);
     opmodeLora();
@@ -753,6 +778,10 @@
 
     // enter standby mode (required for FIFO loading))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // configure LoRa modem (cfg1, cfg2)
     configLoraModem();
     // configure frequency
@@ -786,7 +815,7 @@
 }
 
 // start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
-static void starttx (void) {
+static void starttx () {
     ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
     if(getSf(LMIC.rps) == FSK) { // FSK modem
         txfsk();
@@ -812,6 +841,10 @@
     ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
     // enter standby mode (warm up))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // don't use MAC settings at startup
     if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
         writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
@@ -846,9 +879,15 @@
     // now instruct the radio to receive
     if (rxmode == RXMODE_SINGLE) { // single rx
         hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
-    opmode(OPMODE_RX_SINGLE);
+        opmode(OPMODE_RX_SINGLE);
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
     } else { // continous rx (scan or rssi)
-    opmode(OPMODE_RX); 
+        opmode(OPMODE_RX); 
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
     }
 }
 
@@ -861,6 +900,10 @@
     ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
     // enter standby mode (warm up))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // configure frequency
     configChannel();
     // set LNA gain
@@ -901,6 +944,9 @@
     // now instruct the radio to receive
     hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
     opmode(OPMODE_RX); // no single rx mode available in FSK
+#if defined(RADIO_DBG)
+    rxStateIo = 1;
+#endif
 }
 
 static void startrx (u1_t rxmode) {
@@ -915,7 +961,7 @@
 }
 
 // get random seed from wideband noise rssi
-void radio_init (void) {
+void radio_init () {
     hal_disableIRQs();
 
     // manually reset radio
@@ -929,6 +975,10 @@
     hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
 
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // some sanity checks, e.g., read version number
     u1_t v = readReg(RegVersion);
 #ifdef CFG_sx1276_radio
@@ -950,7 +1000,7 @@
     }
     randbuf[0] = 16; // set initial index
   
-#ifdef CFG_sx1276_radio
+#ifdef CFG_sx1276mb1_board
     // chain calibration
     writeReg(RegPaConfig, 0);
     
@@ -967,15 +1017,19 @@
     // Launch Rx chain calibration for HF band 
     writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
     while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
-#endif /* CFG_sx1276_radio */
+#endif /* CFG_sx1276mb1_board */
 
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     hal_enableIRQs();
 }
 
 // return next random byte derived from seed buffer
 // (buf[0] holds index of next byte to be returned)
-u1_t radio_rand1 (void) {
+u1_t radio_rand1 () {
     u1_t i = randbuf[0];
     ASSERT( i != 0 );
     if( i==16 ) {
@@ -987,7 +1041,7 @@
     return v;
 }
 
-u1_t radio_rssi (void) {
+u1_t radio_rssi () {
     hal_disableIRQs();
     u1_t r = readReg(LORARegRssiValue);
     hal_enableIRQs();
@@ -1009,56 +1063,63 @@
 void radio_irq_handler (u1_t dio) {
     ostime_t now = os_getTime();
     if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
-    u1_t flags = readReg(LORARegIrqFlags);
-    if( flags & IRQ_LORA_TXDONE_MASK ) {
-        // save exact tx time
-        LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
-    } else if( flags & IRQ_LORA_RXDONE_MASK ) {
-        // save exact rx time
-        LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
-        // read the PDU and inform the MAC that we received something
-        LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
-        readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
-        // set FIFO read address pointer
-        writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); 
-        // now read the FIFO
-        readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
-        // read rx quality parameters
-        LMIC.rxq.snr  = readReg(LORARegPktSnrValue); // SNR [dB] * 4
-        LMIC.rxq.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
-    } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
-        // indicate timeout
-        LMIC.dataLen = 0;
-    }
+        u1_t flags = readReg(LORARegIrqFlags);
+        if( flags & IRQ_LORA_TXDONE_MASK ) {
+            // save exact tx time
+            LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
+        } else if( flags & IRQ_LORA_RXDONE_MASK ) {
+            // save exact rx time
+            if(getBw(LMIC.rps) == BW125) {
+                now -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
+            }
+            LMIC.rxtime = now;
+            // read the PDU and inform the MAC that we received something
+            LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
+                readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
+            // set FIFO read address pointer
+            writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); 
+            // now read the FIFO
+            readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+            // read rx quality parameters
+            LMIC.snr  = readReg(LORARegPktSnrValue); // SNR [dB] * 4
+            LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
+        } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
+            // indicate timeout
+            LMIC.dataLen = 0;
+        }
         // mask all radio IRQs
         writeReg(LORARegIrqFlagsMask, 0xFF);
         // clear radio IRQ flags
         writeReg(LORARegIrqFlags, 0xFF);
     } else { // FSK modem
-    u1_t flags1 = readReg(FSKRegIrqFlags1);
-    u1_t flags2 = readReg(FSKRegIrqFlags2);
-    if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
-        // save exact tx time
-        LMIC.txend = now;
+        u1_t flags1 = readReg(FSKRegIrqFlags1);
+        u1_t flags2 = readReg(FSKRegIrqFlags2);
+        if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
+            // save exact tx time
+            LMIC.txend = now;
         } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
-        // save exact rx time
-        LMIC.rxtime = now;
-        // read the PDU and inform the MAC that we received something
-        LMIC.dataLen = readReg(FSKRegPayloadLength);
-        // now read the FIFO
-        readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
-        // read rx quality parameters
-        LMIC.rxq.snr  = 0; // determine snr
-        LMIC.rxq.rssi = 0; // determine rssi
-    } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
-        // indicate timeout
-        LMIC.dataLen = 0;
-    } else {
+            // save exact rx time
+            LMIC.rxtime = now;
+            // read the PDU and inform the MAC that we received something
+            LMIC.dataLen = readReg(FSKRegPayloadLength);
+            // now read the FIFO
+            readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+            // read rx quality parameters
+            LMIC.snr  = 0; // determine snr
+            LMIC.rssi = 0; // determine rssi
+        } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
+            // indicate timeout
+            LMIC.dataLen = 0;
+        } else {
             while(1);
         }
     }
     // go from stanby to sleep
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // run os job (use preset func ptr)
     os_setCallback(&LMIC.osjob, LMIC.osjob.func);
 }
@@ -1069,21 +1130,34 @@
       case RADIO_RST:
         // put radio to sleep
         opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+        txStateIo = 0;
+        rxStateIo = 0;
+#endif
         break;
 
       case RADIO_TX:
-    // transmit frame now
+        // transmit frame now
         starttx(); // buf=LMIC.frame, len=LMIC.dataLen
+#if defined(RADIO_DBG)
+        txStateIo = 1;
+#endif
         break;
       
       case RADIO_RX:
-    // receive frame now (exactly at rxtime)
+        // receive frame now (exactly at rxtime)
         startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
         break;
 
       case RADIO_RXON:
         // start scanning for beacon now
         startrx(RXMODE_SCAN); // buf=LMIC.frame
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
         break;
     }
     hal_enableIRQs();