diff --git a/src/lmic/config.h b/src/lmic/config.h
index 253fd0a..32e30a9 100755
--- a/src/lmic/config.h
+++ b/src/lmic/config.h
@@ -123,15 +123,7 @@
 //#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING
 //#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatically disabled by DISABLE_BEACON
 
-// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the
-// same on RX. This ensures that gateways can talk to nodes and vice
-// versa, but gateways will not hear other gateways and nodes will not
-// hear other nodes. By defining this macro in lmic_project_config.h,
-// this inversion is disabled and this node can hear other nodes. If
-// two nodes both have this macro set, they can talk to each other
-// (but they can no longer hear gateways). This should probably only
-// be used when debugging and/or when talking to the radio directly
-// (e.g. like in the "raw" example).
+// DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this.
 //#define DISABLE_INVERT_IQ_ON_RX
 
 // This allows choosing between multiple included AES implementations.
@@ -178,4 +170,24 @@
 # define LMIC_ENABLE_DeviceTimeReq 0
 #endif
 
+// LMIC_ENABLE_user_events
+// Enable/disable support for programmable callbacks for events, rx, and tx.
+// This is always defined, and non-zero to enable.  Default is enabled.
+#if !defined(LMIC_ENABLE_user_events)
+# define LMIC_ENABLE_user_events 1
+#endif
+
+// LMIC_ENABLE_onEvent
+// Enable/disable support for out-call to user-supplied `onEvent()` function.
+// This is always defined, and non-zero to enable. Default is enabled.
+#if !defined(LMIC_ENABLE_onEvent)
+# define LMIC_ENABLE_onEvent 1
+#endif
+
+// LMIC_ENABLE_long_messages
+// LMIC certification requires that this be enabled.
+#if !defined(LMIC_ENABLE_long_messages)
+# define LMIC_ENABLE_long_messages 1        /* PARAM */
+#endif
+
 #endif // _lmic_config_h_
diff --git a/src/lmic/hal.h b/src/lmic/hal.h
index 5b610f4..8fadeb6 100755
--- a/src/lmic/hal.h
+++ b/src/lmic/hal.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
- * Copyright (c) 2016, 2018 MCCI Corporation.
+ * Copyright (c) 2016, 2018-2019 MCCI Corporation.
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -33,10 +33,17 @@
 # include "oslmic_types.h"
 #endif
 
+#ifndef _lmic_env_h_
+# include "lmic_env.h"
+#endif
+
 #ifdef __cplusplus
 extern "C"{
 #endif
 
+// The type of an optional user-defined failure handler routine
+typedef void LMIC_ABI_STD hal_failure_handler_t(const char* const file, const uint16_t line);
+
 /*
  * initialize hardware (IO, SPI, TIMER, IRQ).
  * This API is deprecated as it uses the const global lmic_pins,
@@ -87,6 +94,11 @@ void hal_disableIRQs (void);
  */
 void hal_enableIRQs (void);
 
+/*
+ * return CPU interrupt nesting count
+ */
+uint8_t hal_getIrqLevel (void);
+
 /*
  * put system and CPU in low-power mode, sleep until interrupt.
  */
@@ -116,6 +128,12 @@ u1_t hal_checkTimer (u4_t targettime);
  */
 void hal_failed (const char *file, u2_t line);
 
+/*
+ * set a custom hal failure handler routine. The default behaviour, defined in
+ * hal_failed(), is to halt by looping infintely.
+ */
+void hal_set_failure_handler(const hal_failure_handler_t* const);
+
 /*
  * get the calibration value for radio_rssi
  */
diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c
index a7f253c..d825bdd 100755
--- a/src/lmic/lmic.c
+++ b/src/lmic/lmic.c
@@ -2,7 +2,7 @@
  * Copyright (c) 2014-2016 IBM Corporation.
  * All rights reserved.
  *
- * Copyright (c) 2016-2018 MCCI Corporation.
+ * Copyright (c) 2016-2019 MCCI Corporation.
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,12 @@ DEFINE_LMIC;
 
 
 // Fwd decls.
+static void reportEventNoUpdate(ev_t);
+static void reportEventAndUpdate(ev_t);
 static void engineUpdate(void);
+static bit_t processJoinAccept_badframe(void);
+static bit_t processJoinAccept_nojoinframe(void);
+
 
 #if !defined(DISABLE_BEACONS)
 static void startScan (void);
@@ -406,7 +411,9 @@ void LMICcore_setDrJoin (u1_t reason, u1_t dr) {
 }
 
 
-static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
+static bit_t setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
+    bit_t result = 0;
+
     LMIC_EV_PARAMETER(reason);
 
     EV(drChange, INFO, (e_.reason    = reason,
@@ -416,13 +423,17 @@ static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
                         e_.prevdr    = LMIC.datarate|DR_PAGE,
                         e_.prevtxpow = LMIC.adrTxPow));
 
-    if( pow != KEEP_TXPOW )
+    if( pow != KEEP_TXPOW && pow != LMIC.adrTxPow ) {
         LMIC.adrTxPow = pow;
+        result = 1;
+    }
     if( LMIC.datarate != dr ) {
         LMIC.datarate = dr;
         DO_DEVDB(LMIC.datarate,datarate);
         LMIC.opmode |= OP_NEXTCHNL;
+        result = 1;
     }
+    return result;
 }
 
 
@@ -450,25 +461,131 @@ static void runEngineUpdate (xref2osjob_t osjob) {
     engineUpdate();
 }
 
-
-static void reportEvent (ev_t ev) {
-    EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
-                       e_.eui    = MAIN::CDEV->getEui(),
-                       e_.info   = ev));
-    ON_LMIC_EVENT(ev);
+static void reportEventAndUpdate(ev_t ev) {
+    reportEventNoUpdate(ev);
     engineUpdate();
 }
 
+static void reportEventNoUpdate (ev_t ev) {
+    uint32_t const evSet = UINT32_C(1) << ev;
+    EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
+                       e_.eui    = MAIN::CDEV->getEui(),
+                       e_.info   = ev));
+#if LMIC_ENABLE_onEvent
+    void (*pOnEvent)(ev_t) = onEvent;
+
+    // rxstart is critical timing; legacy onEvent handlers
+    // don't comprehend this; so don't report.
+    if (pOnEvent != NULL && (evSet & (UINT32_C(1)<<EV_RXSTART)) == 0)
+        pOnEvent(ev);
+#endif // LMIC_ENABLE_onEvent
+
+    // we want people who need tiny RAM footprints to be able
+    // to use onEvent and overide the dynamic mechanism.
+#if LMIC_ENABLE_user_events
+    // create a mask to test against sets of events.
+
+    // if a message was received, notify the user.
+    if ((evSet & ((UINT32_C(1)<<EV_TXCOMPLETE) | (UINT32_C(1)<<EV_RXCOMPLETE))) != 0 &&
+        LMIC.client.rxMessageCb != NULL &&
+        (LMIC.dataLen  != 0 || LMIC.dataBeg != 0)) {
+        uint8_t port;
+
+        // assume no port.
+        port = 0;
+
+        // correct assumption if a port was provided.
+        if (LMIC.txrxFlags & TXRX_PORT)
+            port = LMIC.frame[LMIC.dataBeg - 1];
+
+        // notify the user.
+        LMIC.client.rxMessageCb(
+                LMIC.client.rxMessageUserData,
+                port,
+                LMIC.frame + LMIC.dataBeg,
+                LMIC.dataLen
+                );
+    }
+
+    // tell the client about completed transmits -- the buffer
+    // is now available again.  We use set notation again in case
+    // we later discover another event completes messages
+    if ((evSet & ((UINT32_C(1)<<EV_TXCOMPLETE) | (UINT32_C(1) <<EV_TXCANCELED))) != 0) {
+        lmic_txmessage_cb_t * const pTxMessageCb = LMIC.client.txMessageCb;
+
+        if (pTxMessageCb != NULL) {
+            int fSuccess;
+            // reset before notifying user. If we reset after
+            // notifying, then if user does a recursive call
+            // in their message processing
+            // function, we would clobber the value
+            LMIC.client.txMessageCb = NULL;
+
+            // compute exit status
+            if (ev == EV_TXCANCELED) {
+                // canceled: unsuccessful.
+                fSuccess = 0;
+            } else if (/* ev == EV_TXCOMPLETE  && */ LMIC.pendTxConf) {
+                fSuccess = (LMIC.txrxFlags & TXRX_ACK) != 0;
+            } else {
+                // unconfirmed uplinks are successful if they were sent.
+                fSuccess = 1;
+            }
+
+            // notify the user.
+            pTxMessageCb(LMIC.client.txMessageUserData, fSuccess);
+        }
+    }
+
+    // tell the client about events in general
+    if (LMIC.client.eventCb != NULL)
+        LMIC.client.eventCb(LMIC.client.eventUserData, ev);
+#endif // LMIC_ENABLE_user_events
+}
+
+int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData) {
+#if LMIC_ENABLE_user_events
+    LMIC.client.rxMessageCb = pRxMessageCb;
+    LMIC.client.rxMessageUserData = pUserData;
+    return 1;
+#else // !LMIC_ENABLE_user_events
+    return 0;
+#endif // !LMIC_ENABLE_user_events
+}
+
+int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData) {
+#if LMIC_ENABLE_user_events
+    LMIC.client.eventCb = pEventCb;
+    LMIC.client.eventUserData = pUserData;
+    return 1;
+#else // ! LMIC_ENABLE_user_events
+    return 0;
+#endif // ! LMIC_ENABLE_user_events
+}
 
 static void runReset (xref2osjob_t osjob) {
     LMIC_API_PARAMETER(osjob);
 
+    // clear pending TX.
+    LMIC_clrTxData();
+
     // Disable session
     LMIC_reset();
+
+    // report event before the join event.
+    reportEventNoUpdate(EV_RESET);
+
 #if !defined(DISABLE_JOIN)
     LMIC_startJoining();
+#else
+    os_setCallback(&LMIC.osjob, FUNC_ADDR(runEngineUpdate));
 #endif // !DISABLE_JOIN
-    reportEvent(EV_RESET);
+}
+
+static void resetJoinParams(void) {
+    LMIC.rx1DrOffset = 0;
+    LMIC.dn2Dr       = DR_DNW2;
+    LMIC.dn2Freq     = FREQ_DNW2;
 }
 
 static void stateJustJoined (void) {
@@ -489,9 +606,7 @@ static void stateJustJoined (void) {
     LMIC.pingSetAns  = 0;
 #endif
     LMIC.upRepeat    = 0;
-    LMIC.adrAckReq   = LINK_CHECK_INIT;
-    LMIC.dn2Dr       = DR_DNW2;
-    LMIC.dn2Freq     = FREQ_DNW2;
+    resetJoinParams();
 #if !defined(DISABLE_BEACONS)
     LMIC.bcnChnl     = CHNL_BCN;
 #endif
@@ -538,6 +653,86 @@ static int decodeBeacon (void) {
 }
 #endif // !DISABLE_BEACONS
 
+static CONST_TABLE(u1_t, macCmdSize)[] = {
+    /* 2: LinkCheckAns */ 3,
+    /* 3: LinkADRReq */ 5,
+    /* 4: DutyCycleReq */ 2,
+    /* 5: RXParamSetupReq */ 5,
+    /* 6: DevStatusReq */ 1,
+    /* 7: NewChannel Req */ 6,
+    /* 8: RXTiminSetupReq */ 2,
+    /* 9: TxParamSetupReq */ 2,
+    /* 0x0A: DlChannelReq */ 5,
+    /* B, C: RFU */ 0, 0,
+    /* 0x0D: DeviceTimeAns */ 6,
+    /* 0x0E, 0x0F */ 0, 0,
+    /* 0x10: PingSlotInfoAns */ 1,
+    /* 0x11: PingSlotChannelReq */ 4,
+    /* 0x12: BeaconTimingAns */ 4,
+    /* 0x13: BeaconFreqReq */ 4
+};
+
+static u1_t getMacCmdSize(u1_t macCmd) {
+    if (macCmd < 2)
+        return 0;
+    if (macCmd >= LENOF_TABLE(macCmdSize) - 2)
+        return 0;
+    return TABLE_GET_U1(macCmdSize, macCmd - 2);
+}
+
+static void
+applyAdrRequests(
+    const uint8_t *opts,
+    int olen
+) {
+    if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
+        lmic_saved_adr_state_t initialState;
+        int oidx;
+        u1_t p1 = 0;
+        u1_t p4 = 0;
+
+        LMICbandplan_saveAdrState(&initialState);
+
+        for (oidx = 0; oidx < olen; ) {
+            u1_t const cmd = opts[oidx];
+
+            if (cmd == MCMD_LADR_REQ) {
+                u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
+
+                p1     = opts[oidx+1];            // txpow + DR, in case last
+                p4     = opts[oidx+4];
+                u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK;     // channel page
+
+                LMICbandplan_mapChannels(chpage, chmap);
+            }
+
+            int cmdlen = getMacCmdSize(cmd);
+
+            // this really is an assert, we should never here
+            // unless all the commands are valid.
+            ASSERT(cmdlen != 0);
+
+            oidx += cmdlen;
+        }
+
+        // all done scanning options
+        bit_t changes = LMICbandplan_compareAdrState(&initialState);
+
+        // handle uplink repeat count
+        u1_t uprpt  = p4 & MCMD_LADR_REPEAT_MASK;     // up repeat count
+        if (LMIC.upRepeat != uprpt) {
+            LMIC.upRepeat = uprpt;
+            changes = 1;
+        }
+
+        // handle power changes
+        dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
+        changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
+
+        LMIC.adrChanged = changes;  // move the ADR FSM up to "time to request"
+    }
+}
+
 // scan mac commands starting at opts[] for olen, return count of bytes consumed.
 static int
 scan_mac_cmds(
@@ -545,28 +740,37 @@ scan_mac_cmds(
     int olen
     ) {
     int oidx = 0;
+    // this parser is *really* fragile, especially for LinkADR requests.
+    // it won't crash, but acks will be wrong if all ADR requests are
+    // not contiguous.
+    bit_t fSawAdrReq = 0;
+    uint8_t cmd;
+
     while( oidx < olen ) {
-        switch( opts[oidx] ) {
+        cmd = opts[oidx];
+        switch( cmd ) {
         case MCMD_LCHK_ANS: {
             //int gwmargin = opts[oidx+1];
             //int ngws = opts[oidx+2];
-            oidx += 3;
-            continue;
+            break;
         }
         case MCMD_LADR_REQ: {
             u1_t p1     = opts[oidx+1];            // txpow + DR
             u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
             u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK;     // channel page
             u1_t uprpt  = opts[oidx+4] & MCMD_LADR_REPEAT_MASK;     // up repeat count
-            oidx += 5;
 
-            // TODO(tmm@mcci.com): LoRaWAN 1.1 requires us to process multiple
-            // LADR requests, and only update if all pass. So this should check
-            // ladrAns == 0, and only initialize if so. Need to repeat ACKs, so
-            // we need to count the number we see.
-            LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
-                MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
-            if( !LMICbandplan_mapChannels(chpage, chmap) )
+            // TODO(tmm@mcci.com): LoRaWAN 1.1 & 1.0.3 requires us to send one ack
+            // for each LinkADRReq in a given MAC message. This code only sends
+            // ack for all the LinkADRReqs. Fixing this is a lot of work, and TTN
+            // behaves correctly with the current LMIC, so we'll leave this for
+            // the fix of issue #87.
+            if (! fSawAdrReq) {
+                fSawAdrReq = 1;
+                LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
+                    MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
+            }
+            if( !LMICbandplan_canMapChannels(chpage, chmap) )
                 LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
             dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
             if( !validDR(dr) ) {
@@ -576,23 +780,7 @@ scan_mac_cmds(
                                    e_.info   = Base::lsbf4(&d[pend]),
                                    e_.info2  = Base::msbf4(&opts[oidx-4])));
             }
-            // TODO(tmm@mcci.com): see above; this needs to move outside the
-            // txloop. And we need to have "consistent" answers for the block
-            // of contiguous commands (whatever that means), and ignore the
-            // data rate, NbTrans (uprpt) and txPow until the last one.
-#if LMIC_DEBUG_LEVEL > 0
-            LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n",
-		os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns
-		);
-#endif /* LMIC_DEBUG_LEVEL */
-
-            if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
-                // Nothing went wrong - use settings
-                LMIC.upRepeat = uprpt;
-                setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
-            }
-            LMIC.adrChanged = 1;  // Trigger an ACK to NWK
-            continue;
+            break;
         }
         case MCMD_DEVS_REQ: {
             LMIC.devsAns = 1;
@@ -600,8 +788,7 @@ scan_mac_cmds(
             const int snr = (LMIC.snr + 2) / 4;
             // per [1.02] 5.5. the margin is the SNR.
             LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr));
-            oidx += 1;
-            continue;
+            break;
         }
         case MCMD_DN2P_SET: {
 #if !defined(DISABLE_MCMD_DN2P_SET)
@@ -624,8 +811,7 @@ scan_mac_cmds(
                 DO_DEVDB(LMIC.dn2Freq,dn2Freq);
             }
 #endif // !DISABLE_MCMD_DN2P_SET
-            oidx += 5;
-            continue;
+            break;
         }
         case MCMD_DCAP_REQ: {
 #if !defined(DISABLE_MCMD_DCAP_REQ)
@@ -637,9 +823,8 @@ scan_mac_cmds(
             LMIC.globalDutyAvail = os_getTime();
             DO_DEVDB(cap,dutyCap);
             LMIC.dutyCapAns = 1;
-            oidx += 2;
 #endif // !DISABLE_MCMD_DCAP_REQ
-            continue;
+            break;
         }
         case MCMD_SNCH_REQ: {
 #if !defined(DISABLE_MCMD_SNCH_REQ)
@@ -650,8 +835,7 @@ scan_mac_cmds(
             if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
                 LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
 #endif // !DISABLE_MCMD_SNCH_REQ
-            oidx += 6;
-            continue;
+            break;
         }
         case MCMD_PING_SET: {
 #if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
@@ -666,8 +850,7 @@ scan_mac_cmds(
             }
             LMIC.pingSetAns = flags;
 #endif // !DISABLE_MCMD_PING_SET && !DISABLE_PING
-            oidx += 4;
-            continue;
+            break;
         }
         case MCMD_BCNI_ANS: {
 #if !defined(DISABLE_MCMD_BCNI_ANS) && !defined(DISABLE_BEACONS)
@@ -695,8 +878,7 @@ scan_mac_cmds(
                                      e_.time    = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
             }
 #endif // !DISABLE_MCMD_BCNI_ANS && !DISABLE_BEACONS
-            oidx += 4;
-            continue;
+            break;
         } /* end case */
         case MCMD_TxParamSetupReq: {
 #if LMIC_ENABLE_TxParamSetupReq
@@ -710,8 +892,7 @@ scan_mac_cmds(
             LMIC.txParam = txParam;
             LMIC.txParamSetupAns = 1;
 #endif // LMIC_ENABLE_TxParamSetupReq
-            oidx += 2;
-            continue;
+            break;
         } /* end case */
         case MCMD_DeviceTimeAns: {
 #if LMIC_ENABLE_DeviceTimeReq
@@ -742,21 +923,41 @@ scan_mac_cmds(
 #endif
             }
 #endif // LMIC_ENABLE_DeviceTimeReq
-            oidx += 6;
-            continue;
+            break;
+        } /* end case */
+
+        default: {
+            // force olen to current oidx so we'll exit the while()
+            olen = oidx;
+            break;
         } /* end case */
         } /* end switch */
-        /* unrecognized mac commands fall out of switch to here */
-        EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
-                           e_.eui    = MAIN::CDEV->getEui(),
-                           e_.info   = Base::lsbf4(&d[pend]),
-                           e_.info2  = Base::msbf4(&opts[oidx])));
-        /* stop processing options */
-        break;
+
+    /* compute length, and exit for illegal commands */
+    int const cmdlen = getMacCmdSize(cmd);
+    if (cmdlen == 0) {
+        // "the first unknown command terminates processing"
+        // force olen to current oidx so we'll exit the while().
+        olen = oidx;
+    }
+
+    oidx += cmdlen;
     } /* end while */
+
+    // go back and apply the ADR changes, if any -- use the effective length
+    if (fSawAdrReq)
+        applyAdrRequests(opts, olen);
+
     return oidx;
 }
 
+// change the ADR ack request count, unless adr ack is diabled.
+static void setAdrAckCount (s1_t count) {
+    if (LMIC.adrAckReq != LINK_CHECK_OFF) {
+        LMIC.adrAckReq = count;
+    }
+}
+
 static bit_t decodeFrame (void) {
     xref2u1_t d = LMIC.frame;
     u1_t hdr    = d[0];
@@ -858,8 +1059,7 @@ static bit_t decodeFrame (void) {
 
     // We heard from network
     LMIC.adrChanged = LMIC.rejoinCnt = 0;
-    if( LMIC.adrAckReq != LINK_CHECK_OFF )
-        LMIC.adrAckReq = LINK_CHECK_INIT;
+    setAdrAckCount(LINK_CHECK_INIT);
 
     int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
     // for legacy reasons, LMIC.margin is set to the unsigned sensitivity. It can never be negative.
@@ -877,6 +1077,7 @@ static bit_t decodeFrame (void) {
         EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
                            e_.eui    = MAIN::CDEV->getEui(),
                            e_.info   = 0x1000000 + (oidx) + (olen<<8)));
+        oidx = olen;
     }
 
     if( !replayConf ) {
@@ -956,13 +1157,19 @@ static bit_t decodeFrame (void) {
 // ================================================================================
 // TX/RX transaction support
 
+// start reception and log.
+static void radioRx (void) {
+    reportEventNoUpdate(EV_RXSTART);
+    os_radio(RADIO_RX);
+}
 
+// start RX in window 2.
 static void setupRx2 (void) {
     initTxrxFlags(__func__, TXRX_DNW2);
     LMIC.rps = dndr2rps(LMIC.dn2Dr);
     LMIC.freq = LMIC.dn2Freq;
     LMIC.dataLen = 0;
-    os_radio(RADIO_RX);
+    radioRx();
 }
 
 
@@ -973,11 +1180,11 @@ static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) {
 
     // If a clock error is specified, compensate for it by extending the
     // receive window
-    if (LMIC.clockError != 0) {
+    if (LMIC.client.clockError != 0) {
         // Calculate how much the clock will drift maximally after delay has
         // passed. This indicates the amount of time we can be early
         // _or_ late.
-        ostime_t drift = (int64_t)delay * LMIC.clockError / MAX_CLOCK_ERROR;
+        ostime_t drift = (int64_t)delay * LMIC.client.clockError / MAX_CLOCK_ERROR;
 
         // Increase the receive window by twice the maximum drift (to
         // compensate for a slow or a fast clock).
@@ -1006,7 +1213,7 @@ static void setupRx1 (osjobcb_t func) {
     LMIC.rps = setNocrc(LMIC.rps,1);
     LMIC.dataLen = 0;
     LMIC.osjob.func = func;
-    os_radio(RADIO_RX);
+    radioRx();
 }
 
 
@@ -1033,7 +1240,6 @@ static void txDone (ostime_t delay, osjobcb_t func) {
     }
 }
 
-
 // ======================================== Join frames
 
 
@@ -1043,53 +1249,19 @@ static void onJoinFailed (xref2osjob_t osjob) {
 
     // Notify app - must call LMIC_reset() to stop joining
     // otherwise join procedure continues.
-    reportEvent(EV_JOIN_FAILED);
+    reportEventAndUpdate(EV_JOIN_FAILED);
 }
 
-
+// process join-accept message or deal with no join-accept in slot 2.
 static bit_t processJoinAccept (void) {
-    ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0);
+    if ((LMIC.txrxFlags & TXRX_DNW1) != 0 && LMIC.dataLen == 0)
+        return 0;
+
     ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
 
     if( LMIC.dataLen == 0 ) {
-      nojoinframe:
-        if( (LMIC.opmode & OP_JOINING) == 0 ) {
-            ASSERT((LMIC.opmode & OP_REJOIN) != 0);
-            // REJOIN attempt for roaming
-            LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
-            if( LMIC.rejoinCnt < 10 )
-                LMIC.rejoinCnt++;
-            reportEvent(EV_REJOIN_FAILED);
-            return 1;
-        }
-        LMIC.opmode &= ~OP_TXRXPEND;
-        int failed = LMICbandplan_nextJoinState();
-        EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
-                            e_.eui    = MAIN::CDEV->getEui(),
-                            e_.info   = LMIC.datarate|DR_PAGE,
-                            e_.info2  = failed));
-        // Build next JOIN REQUEST with next engineUpdate call
-        // Optionally, report join failed.
-        // Both after a random/chosen amount of ticks. That time
-	// is in LMIC.txend. The delay here is either zero or 1
-	// tick; onJoinFailed()/runEngineUpdate() are responsible
-	// for honoring that. XXX(tmm@mcci.com) The IBM 1.6 code
-	// claimed to return a delay but really returns 0 or 1.
-	// Once we update as923 to return failed after dr2, we
-	// can take out this #if.
-#if CFG_region != LMIC_REGION_as923
-        os_setTimedCallback(&LMIC.osjob, os_getTime()+failed,
-                            failed
-                            ? FUNC_ADDR(onJoinFailed)      // one JOIN iteration done and failed
-                            : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
-#else
-       // in the join of AS923 v1.1 older, only DR2 is used. Therefore,
-       // not much improvement when it handles two different behavior;
-       // onJoinFailed or runEngineUpdate.
-        os_setTimedCallback(&LMIC.osjob, os_getTime()+failed,
-                            FUNC_ADDR(onJoinFailed));
-#endif
-        return 1;
+        // we didn't get any data and we're in slot 2. So... there's no join frame.
+        return processJoinAccept_nojoinframe();
     }
     u1_t hdr  = LMIC.frame[0];
     u1_t dlen = LMIC.dataLen;
@@ -1102,16 +1274,13 @@ static bit_t processJoinAccept (void) {
                            e_.eui    = MAIN::CDEV->getEui(),
                            e_.info   = dlen < 4 ? 0 : mic,
                            e_.info2  = hdr + (dlen<<8)));
-      badframe:
-        if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
-            return 0;
-        goto nojoinframe;
+        return processJoinAccept_badframe();
     }
     aes_encrypt(LMIC.frame+1, dlen-1);
     if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
         EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
                            e_.info   = mic));
-        goto badframe;
+        return processJoinAccept_badframe();
     }
 
     u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
@@ -1159,15 +1328,19 @@ static bit_t processJoinAccept (void) {
     //
     // XXX(tmm@mcci.com) OP_REJOIN confuses me, and I'm not sure why we're
     // adjusting DRs here. We've just recevied a join accept, and the
-    // datarate therefore shouldn't be in play.
+    // datarate therefore shouldn't be in play.  In effect, we set the
+    // initial data rate based on the number of times we tried to rejoin.
     //
     if( (LMIC.opmode & OP_REJOIN) != 0 ) {
 #if CFG_region != LMIC_REGION_as923
-	// TODO(tmm@mcci.com) regionalize
+	    // TODO(tmm@mcci.com) regionalize
         // Lower DR every try below current UP DR
         LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
 #else
         // in the join of AS923 v1.1 or older, only DR2 (SF10) is used.
+        // TODO(tmm@mcci.com) if the rejoin logic is at all correct, we
+        // should be setting the uplink datarate based on the number of
+        // tries; this doesn't set the AS923 join data rate.
         LMIC.datarate = AS923_DR_SF10;
 #endif
     }
@@ -1175,14 +1348,74 @@ static bit_t processJoinAccept (void) {
     LMIC.opmode |= OP_NEXTCHNL;
     LMIC.txCnt = 0;
     stateJustJoined();
+    // transition to the ADR_ACK initial state.
+    setAdrAckCount(LINK_CHECK_INIT);
+
     LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F;
     LMIC.rx1DrOffset = (LMIC.frame[OFF_JA_DLSET] >> 4) & 0x7;
     LMIC.rxDelay = LMIC.frame[OFF_JA_RXDLY];
     if (LMIC.rxDelay == 0) LMIC.rxDelay = 1;
-    reportEvent(EV_JOINED);
+    reportEventAndUpdate(EV_JOINED);
     return 1;
 }
 
+static bit_t processJoinAccept_badframe(void) {
+        if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
+            // continue the join process: there's another window.
+            return 0;
+        else
+            // stop the join process
+            return processJoinAccept_nojoinframe();
+}
+
+static bit_t processJoinAccept_nojoinframe(void) {
+        // Valid states are JOINING (in which caise REJOIN is ignored)
+        // or ~JOINING and REJOIN. If it's a REJOIN,
+        // we need to turn off rejoin, signal an event, and increment
+        // the rejoin-sent count. Internal callers will turn on rejoin
+        // occasionally.
+        if( (LMIC.opmode & OP_JOINING) == 0) {
+            ASSERT((LMIC.opmode & OP_REJOIN) != 0);
+            LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
+            if( LMIC.rejoinCnt < 10 )
+                LMIC.rejoinCnt++;
+            reportEventAndUpdate(EV_REJOIN_FAILED);
+            // stop the join process.
+            return 1;
+        }
+        // otherwise it's a normal join. At end of rx2, so we
+        // need to schedule something.
+        LMIC.opmode &= ~OP_TXRXPEND;
+        reportEventNoUpdate(EV_JOIN_TXCOMPLETE);
+        int failed = LMICbandplan_nextJoinState();
+        EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
+                            e_.eui    = MAIN::CDEV->getEui(),
+                            e_.info   = LMIC.datarate|DR_PAGE,
+                            e_.info2  = failed));
+        // Build next JOIN REQUEST with next engineUpdate call
+        // Optionally, report join failed.
+        // Both after a random/chosen amount of ticks. That time
+        // is in LMIC.txend. The delay here is either zero or 1
+        // tick; onJoinFailed()/runEngineUpdate() are responsible
+        // for honoring that. XXX(tmm@mcci.com) The IBM 1.6 code
+        // claimed to return a delay but really returns 0 or 1.
+        // Once we update as923 to return failed after dr2, we
+        // can take out this #if.
+#if CFG_region != LMIC_REGION_as923
+        os_setTimedCallback(&LMIC.osjob, os_getTime()+failed,
+                            failed
+                            ? FUNC_ADDR(onJoinFailed)      // one JOIN iteration done and failed
+                            : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
+#else
+       // in the join of AS923 v1.1 older, only DR2 is used. Therefore,
+       // not much improvement when it handles two different behavior;
+       // onJoinFailed or runEngineUpdate.
+        os_setTimedCallback(&LMIC.osjob, os_getTime()+failed,
+                            FUNC_ADDR(onJoinFailed));
+#endif
+        // stop this join process.
+        return 1;
+}
 
 static void processRx2Jacc (xref2osjob_t osjob) {
     LMIC_API_PARAMETER(osjob);
@@ -1190,7 +1423,9 @@ static void processRx2Jacc (xref2osjob_t osjob) {
     if( LMIC.dataLen == 0 ) {
         initTxrxFlags(__func__, 0);  // nothing in 1st/2nd DN slot
     }
-    processJoinAccept();
+    // we're done with this join cycle anyway, so ignore the
+    // result of processJoinAccept()
+    (void) processJoinAccept();
 }
 
 
@@ -1326,8 +1561,11 @@ static void buildDataFrame (void) {
     }
 #endif // !DISABLE_BEACONS
     if( LMIC.adrChanged ) {
-        if( LMIC.adrAckReq < 0 )
-            LMIC.adrAckReq = 0;
+        // if ADR is enabled, and we were just counting down the
+        // transmits before starting an ADR, advance the timer so
+        // we'll do an ADR now.
+        if (LMIC.adrAckReq < LINK_CHECK_CONT)
+            setAdrAckCount(LINK_CHECK_CONT);
         LMIC.adrChanged = 0;
     }
 #if !defined(DISABLE_MCMD_DN2P_SET)
@@ -1378,7 +1616,7 @@ static void buildDataFrame (void) {
     }
     LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
     LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
-                              | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
+                              | (LMIC.adrAckReq >= LINK_CHECK_CONT ? FCT_ADRARQ : 0)
                               | (end-OFF_DAT_OPTS));
     os_wlsbf4(LMIC.frame+OFF_DAT_ADDR,  LMIC.devaddr);
 
@@ -1438,7 +1676,7 @@ static void onBcnRx (xref2osjob_t osjob) {
     if( LMIC.dataLen == 0 ) {
         // Nothing received - timeout
         LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
-        reportEvent(EV_SCAN_TIMEOUT);
+        reportEventAndUpdate(EV_SCAN_TIMEOUT);
         return;
     }
     if( decodeBeacon() <= 0 ) {
@@ -1454,7 +1692,7 @@ static void onBcnRx (xref2osjob_t osjob) {
     calcBcnRxWindowFromMillis(13,1);
     LMIC.opmode &= ~OP_SCAN;          // turn SCAN off
     LMIC.opmode |=  OP_TRACK;         // auto enable tracking
-    reportEvent(EV_BEACON_FOUND);    // can be disabled in callback
+    reportEventAndUpdate(EV_BEACON_FOUND);    // can be disabled in callback
 }
 
 
@@ -1554,7 +1792,28 @@ static void buildJoinRequest (u1_t ftype) {
 static void startJoining (xref2osjob_t osjob) {
     LMIC_API_PARAMETER(osjob);
 
-    reportEvent(EV_JOINING);
+    // see issue #244: for backwards compatibility
+    // don't override what the user does after os_init().
+    if (LMIC.initBandplanAfterReset)
+        LMICbandplan_resetDefaultChannels();
+    else
+        LMIC.initBandplanAfterReset = 1;
+
+    // let the client know that now's the time to update
+    // network settings.
+    reportEventAndUpdate(EV_JOINING);
+}
+
+// reset the joined-to-network state (and clean up)
+void LMIC_unjoin(void) {
+    // reset any joining flags
+    LMIC.opmode &= ~(OP_SCAN|OP_REJOIN);
+
+    // put us in unjoined state:
+    LMIC.devaddr = 0;
+
+    // clear transmit.
+    LMIC_clrTxData();
 }
 
 // Start join procedure if not already joined.
@@ -1568,9 +1827,10 @@ bit_t LMIC_startJoining (void) {
         LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
         // Setup state
         LMIC.rejoinCnt = LMIC.txCnt = 0;
+        resetJoinParams();
         LMICbandplan_initJoinLoop();
         LMIC.opmode |= OP_JOINING;
-        // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
+        // reportEventAndUpdate will call engineUpdate which then starts sending JOIN REQUESTS
         os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
         return 1;
     }
@@ -1592,8 +1852,7 @@ static void processPingRx (xref2osjob_t osjob) {
     if( LMIC.dataLen != 0 ) {
         initTxrxFlags(__func__, TXRX_PING);
         if( decodeFrame() ) {
-            reportEvent(EV_RXCOMPLETE);
-            return;
+            reportEventNoUpdate(EV_RXCOMPLETE);
         }
     }
     // Pick next ping slot
@@ -1622,8 +1881,7 @@ static bit_t processDnData (void) {
             // Nothing received - implies no port
             initTxrxFlags(__func__, TXRX_NOPORT);
         }
-        if( LMIC.adrAckReq != LINK_CHECK_OFF )
-            LMIC.adrAckReq += 1;
+        setAdrAckCount(LMIC.adrAckReq + 1);
         LMIC.dataBeg = LMIC.dataLen = 0;
       txcomplete:
         LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
@@ -1631,25 +1889,25 @@ static bit_t processDnData (void) {
 #if LMIC_ENABLE_DeviceTimeReq
         lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState;
         if ( requestTimeState != lmic_RequestTimeState_idle ) {
-            lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.pNetworkTimeCb;
+            lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.client.pNetworkTimeCb;
             int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success);
             LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
             if (pNetworkTimeCb != NULL) {
                 // reset the callback, so that the user's routine
                 // can post another request if desired.
-                LMIC.pNetworkTimeCb = NULL;
+                LMIC.client.pNetworkTimeCb = NULL;
 
                 // call the user's notification routine.
-                (*pNetworkTimeCb)(LMIC.pNetworkTimeUserData, flagSuccess);
+                (*pNetworkTimeCb)(LMIC.client.pNetworkTimeUserData, flagSuccess);
             }
         }
 #endif // LMIC_ENABLE_DeviceTimeReq
 
         if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0  &&  (LMIC.opmode & OP_LINKDEAD) != 0 ) {
             LMIC.opmode &= ~OP_LINKDEAD;
-            reportEvent(EV_LINK_ALIVE);
+            reportEventNoUpdate(EV_LINK_ALIVE);
         }
-        reportEvent(EV_TXCOMPLETE);
+        reportEventAndUpdate(EV_TXCOMPLETE);
         // If we haven't heard from NWK in a while although we asked for a sign
         // assume link is dead - notify application and keep going
         if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
@@ -1658,16 +1916,38 @@ static bit_t processDnData (void) {
             EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
                               e_.eui    = MAIN::CDEV->getEui(),
                               e_.info   = LMIC.adrAckReq));
-            setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
-            LMIC.adrAckReq = LINK_CHECK_CONT;
-            LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
-            reportEvent(EV_LINK_DEAD);
+            dr_t newDr = decDR((dr_t)LMIC.datarate);
+            if( newDr == (dr_t)LMIC.datarate) {
+                // We are already at the minimum datarate
+                // if the link is already marked dead, we need to join.
+                // REJOIN sends a single join packet then brings back
+                // up the normal tx loop. If we miss the downlink for the
+                // join-accept, all the TXs until the next window will
+                // fail... so we leave in the OP_REJOIN, but set things
+                // to trigger a REJOIN after each uplink from here on.
+                LMIC.adrAckReq = LINK_CHECK_DEAD;
+#if !defined(DISABLE_JOIN)
+                LMIC.opmode |= OP_REJOIN;
+#endif // !defined(DISABLE_JOIN)
+            } else {
+                // not in the dead state... let's wait another 32
+                // uplinks before panicking.
+                setAdrAckCount(LINK_CHECK_CONT);
+            }
+            // Decrease DataRate and restore fullpower.
+            setDrTxpow(DRCHG_NOADRACK, newDr, pow2dBm(0));
+
+            // be careful only to report EV_LINK_DEAD once.
+            u2_t old_opmode = LMIC.opmode;
+            LMIC.opmode = old_opmode | OP_LINKDEAD;
+            if (LMIC.opmode != old_opmode)
+                reportEventNoUpdate(EV_LINK_DEAD); // update?
         }
 #if !defined(DISABLE_BEACONS)
         // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
         if( LMIC.bcninfoTries > 0 ) {
             if( (LMIC.opmode & OP_TRACK) != 0 ) {
-                reportEvent(EV_BEACON_FOUND);
+                reportEventNoUpdate(EV_BEACON_FOUND); // update?
                 LMIC.bcninfoTries = 0;
             }
             else if( --LMIC.bcninfoTries == 0 ) {
@@ -1734,7 +2014,7 @@ static void processBeacon (xref2osjob_t osjob) {
             LMIC.opmode |= OP_REJOIN;  // try if we can roam to another network
         if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
             LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
-            reportEvent(EV_LOST_TSYNC);
+            reportEventAndUpdate(EV_LOST_TSYNC);
             return;
         }
     }
@@ -1746,25 +2026,26 @@ static void processBeacon (xref2osjob_t osjob) {
     if( (LMIC.opmode & OP_PINGINI) != 0 )
         rxschedInit(&LMIC.ping);  // note: reuses LMIC.frame buffer!
 #endif // !DISABLE_PING
-    reportEvent(ev);
+    reportEventAndUpdate(ev);
 }
 
-
+// job entry: time to start receiving a beacon.
 static void startRxBcn (xref2osjob_t osjob) {
     LMIC_API_PARAMETER(osjob);
 
     LMIC.osjob.func = FUNC_ADDR(processBeacon);
-    os_radio(RADIO_RX);
+    radioRx();
 }
 #endif // !DISABLE_BEACONS
 
 
 #if !defined(DISABLE_PING)
+// job entry: time to start receiving in our scheduled downlink slot.
 static void startRxPing (xref2osjob_t osjob) {
     LMIC_API_PARAMETER(osjob);
 
     LMIC.osjob.func = FUNC_ADDR(processPingRx);
-    os_radio(RADIO_RX);
+    radioRx();
 }
 #endif // !DISABLE_PING
 
@@ -1837,10 +2118,8 @@ static void engineUpdate (void) {
                     // in AS923 v1.1 or older, no need to change the datarate.
                     txdr = lowerDR(txdr, LMIC.rejoinCnt);
 #endif
-                    ftype = HDR_FTYPE_REJOIN;
-                } else {
-                    ftype = HDR_FTYPE_JREQ;
                 }
+                ftype = HDR_FTYPE_JREQ;
                 buildJoinRequest(ftype);
                 LMIC.osjob.func = FUNC_ADDR(jreqDone);
             } else
@@ -1874,7 +2153,9 @@ static void engineUpdate (void) {
             LMIC.dndr   = txdr;  // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
             LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
             LMICbandplan_updateTx(txbeg);
-            reportEvent(EV_TXSTART);
+            // limit power to value asked in adr
+            LMIC.radio_txpow = LMIC.txpow > LMIC.adrTxPow ? LMIC.adrTxPow : LMIC.txpow;
+            reportEventNoUpdate(EV_TXSTART);
             os_radio(RADIO_TX);
             return;
         }
@@ -1920,7 +2201,8 @@ static void engineUpdate (void) {
     LMIC.rxtime = LMIC.bcnRxtime;
     if( now - rxtime >= 0 ) {
         LMIC.osjob.func = FUNC_ADDR(processBeacon);
-        os_radio(RADIO_RX);
+
+        radioRx();
         return;
     }
     os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
@@ -1954,7 +2236,10 @@ void LMIC_shutdown (void) {
     LMIC.opmode |= OP_SHUTDOWN;
 }
 
-
+// reset the LMIC. This is called at startup; the clear of LMIC.osjob
+// only works because the LMIC is guaranteed to be zero in that case.
+// But it's also called at frame-count rollover; in that case we have
+// to ensure that the user callback pointers are not clobbered.
 void LMIC_reset (void) {
     EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
                        e_.eui    = MAIN::CDEV->getEui(),
@@ -1962,8 +2247,16 @@ void LMIC_reset (void) {
     os_radio(RADIO_RST);
     os_clearCallback(&LMIC.osjob);
 
-    os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
-    LMIC.devaddr      =  0;
+    // save callback info, clear LMIC, restore.
+    do {
+        lmic_client_data_t  client = LMIC.client;
+
+        os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
+
+        LMIC.client = client;
+    } while (0);
+
+    // LMIC.devaddr      =  0;      // true from os_clearMem().
     LMIC.devNonce     =  os_getRndU2();
     LMIC.opmode       =  OP_NONE;
     LMIC.errcr        =  CR_4_5;
@@ -2001,8 +2294,13 @@ void LMIC_init (void) {
 
 
 void LMIC_clrTxData (void) {
+    bit_t const txActive = LMIC.opmode & OP_TXDATA;
     LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
     LMIC.pendTxLen = 0;
+
+    if (txActive)
+        reportEventNoUpdate(EV_TXCANCELED);
+
     if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
         return;
     os_clearCallback(&LMIC.osjob);
@@ -2019,7 +2317,7 @@ void LMIC_setTxData (void) {
 }
 
 
-//
+// send a message w/o callback
 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
     if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
         return -2;
@@ -2032,6 +2330,20 @@ int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
     return 0;
 }
 
+// send a message with callback
+int LMIC_sendWithCallback(
+    u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed,
+    lmic_txmessage_cb_t *pCb, void *pUserData
+) {
+    int const result = LMIC_setTxData2(port, data, dlen, confirmed);
+    if (result == 0) {
+        LMIC.client.txMessageCb = pCb;
+        LMIC.client.txMessageUserData = pUserData;
+    }
+    return result;
+}
+
+
 // Send a payload-less message to signal device is alive
 void LMIC_sendAlive (void) {
     LMIC.opmode |= OP_POLL;
@@ -2075,6 +2387,9 @@ void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t
     LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
     LMIC.opmode |= OP_NEXTCHNL;
     stateJustJoined();
+    // transition to the ADR_ACK_DELAY state.
+    setAdrAckCount(LINK_CHECK_CONT);
+
     DO_DEVDB(LMIC.netid,   netid);
     DO_DEVDB(LMIC.devaddr, devaddr);
     DO_DEVDB(LMIC.nwkKey,  nwkkey);
@@ -2100,7 +2415,7 @@ void LMIC_setLinkCheckMode (bit_t enabled) {
 // allows for +/- 640 at SF7BW250). MAX_CLOCK_ERROR represents +/-100%,
 // so e.g. for a +/-1% error you would pass MAX_CLOCK_ERROR * 1 / 100.
 void LMIC_setClockError(u2_t error) {
-    LMIC.clockError = error;
+    LMIC.client.clockError = error;
 }
 
 // \brief return the uplink sequence number for the next transmission.
@@ -2135,8 +2450,8 @@ void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *
 #if LMIC_ENABLE_DeviceTimeReq
     if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) {
         LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx;
-        LMIC.pNetworkTimeCb = pCallbackfn;
-        LMIC.pNetworkTimeUserData = pUserData;
+        LMIC.client.pNetworkTimeCb = pCallbackfn;
+        LMIC.client.pNetworkTimeUserData = pUserData;
         return;
     }
 #endif // LMIC_ENABLE_DeviceTimeReq
diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h
index 331d5a3..faa8a12 100755
--- a/src/lmic/lmic.h
+++ b/src/lmic/lmic.h
@@ -105,7 +105,7 @@ extern "C"{
 #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local)	\
 	(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
 
-#define	ARDUINO_LMIC_VERSION	ARDUINO_LMIC_VERSION_CALC(2, 3, 2, 0)	/* v2.3.2 */
+#define	ARDUINO_LMIC_VERSION	ARDUINO_LMIC_VERSION_CALC(2, 3, 2, 50)	/* v2.3.2.50 */
 
 #define	ARDUINO_LMIC_VERSION_GET_MAJOR(v)	\
 	(((v) >> 24u) & 0xFFu)
@@ -122,14 +122,17 @@ extern "C"{
 //! Only For Antenna Tuning Tests !
 //#define CFG_TxContinuousMode 1
 
-enum { MAX_FRAME_LEN      =  64 };   //!< Library cap on max frame length
+// since this was annouunced as the API variable, we keep it. But it's not used,
+// MAX_LEN_FRAME is what the code uses.
+enum { MAX_FRAME_LEN      =  MAX_LEN_FRAME };   //!< Library cap on max frame length
+
 enum { TXCONF_ATTEMPTS    =   8 };   //!< Transmit attempts for confirmed frames
 enum { MAX_MISSED_BCNS    =  20 };   // threshold for triggering rejoin requests
 enum { MAX_RXSYMS         = 100 };   // stop tracking beacon beyond this
 
-enum { LINK_CHECK_CONT    =  12 ,    // continue with this after reported dead link
-       LINK_CHECK_DEAD    =  24 ,    // after this UP frames and no response from NWK assume link is dead
-       LINK_CHECK_INIT    = -12 ,    // UP frame count until we inc datarate
+enum { LINK_CHECK_CONT    =  0  ,    // continue with this after reported dead link
+       LINK_CHECK_DEAD    =  32 ,    // after this UP frames and no response to ack from NWK assume link is dead (ADR_ACK_DELAY)
+       LINK_CHECK_INIT    = -64 ,    // UP frame count until we ask for ack (ADR_ACK_LIMIT)
        LINK_CHECK_OFF     =-128 };   // link check disabled
 
 enum { TIME_RESYNC        = 6*128 }; // secs
@@ -153,12 +156,23 @@ struct band_t {
 };
 TYPEDEF_xref2band_t; //!< \internal
 
+struct lmic_saved_adr_state_s {
+    u4_t        channelFreq[MAX_CHANNELS];
+    u2_t        channelMap;
+};
+
 #elif CFG_LMIC_US_like  // US915 spectrum =================================================
 
 enum { MAX_XCHANNELS = 2 };      // extra channels in RAM, channels 0-71 are immutable
 
+struct lmic_saved_adr_state_s {
+    u2_t        channelMap[(72+MAX_XCHANNELS+15)/16];  // enabled bits
+};
+
 #endif // ==========================================================================
 
+typedef struct lmic_saved_adr_state_s   lmic_saved_adr_state_t;
+
 // Keep in sync with evdefs.hpp::drChange
 enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
 enum { KEEP_TXPOW = -128 };
@@ -189,14 +203,14 @@ enum { BCN_NONE    = 0x00,   //!< No beacon received
 //! Information about the last and previous beacons.
 struct bcninfo_t {
     ostime_t txtime;  //!< Time when the beacon was sent
+    u4_t     time;    //!< GPS time in seconds of last beacon (received or surrogate)
+    s4_t     lat;     //!< Lat field of last beacon (valid only if BCN_FULL set)
+    s4_t     lon;     //!< Lon field of last beacon (valid only if BCN_FULL set)
     s1_t     rssi;    //!< Adjusted RSSI value of last received beacon
     s1_t     snr;     //!< Scaled SNR value of last received beacon
     u1_t     flags;   //!< Last beacon reception and tracking states. See BCN_* values.
-    u4_t     time;    //!< GPS time in seconds of last beacon (received or surrogate)
     //
     u1_t     info;    //!< Info field of last beacon (valid only if BCN_FULL set)
-    s4_t     lat;     //!< Lat field of last beacon (valid only if BCN_FULL set)
-    s4_t     lon;     //!< Lon field of last beacon (valid only if BCN_FULL set)
 };
 #endif // !DISABLE_BEACONS
 
@@ -235,7 +249,7 @@ enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
              EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
              EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
              EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND,
-             EV_TXSTART };
+             EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE };
 typedef enum _ev_t ev_t;
 
 enum {
@@ -243,6 +257,12 @@ enum {
         MAX_CLOCK_ERROR = 65536,
 };
 
+// callbacks for client alerts.
+// types and functions are always defined, to reduce #ifs in example code and libraries.
+typedef void LMIC_ABI_STD lmic_rxmessage_cb_t(void *pUserData, uint8_t port, const uint8_t *pMessage, size_t nMessage);
+typedef void LMIC_ABI_STD lmic_txmessage_cb_t(void *pUserData, int fSuccess);
+typedef void LMIC_ABI_STD lmic_event_cb_t(void *pUserData, ev_t e);
+
 // network time request callback function
 // defined unconditionally, because APIs and types can't change based on config.
 // This is called when a time-request succeeds or when we get a downlink
@@ -272,26 +292,108 @@ enum lmic_request_time_state_e {
 
 typedef u1_t lmic_request_time_state_t;
 
+/*
+
+Structure:  lmic_client_data_t
+
+Function:
+        Holds LMIC client data that must live through LMIC_reset().
+
+Description:
+        There are a variety of client registration linkage items that
+        must live through LMIC_reset(), because LMIC_reset() is called
+        at frame rollover time.  We group them together into a structure
+        to make copies easy.
+
+*/
+
+//! abstract type for collection of client data that survives LMIC_reset().
+typedef struct lmic_client_data_s lmic_client_data_t;
+
+//! contents of lmic_client_data_t
+struct lmic_client_data_s {
+
+    /* pointer-width things come first */
+#if LMIC_ENABLE_DeviceTimeReq
+    lmic_request_network_time_cb_t *pNetworkTimeCb; //! call-back routine for network time
+    void        *pNetworkTimeUserData;              //! call-back data for network time.
+#endif
+
+#if LMIC_ENABLE_user_events
+    lmic_event_cb_t     *eventCb;           //! user-supplied callback function for events.
+    void                *eventUserData;     //! data for eventCb
+    lmic_rxmessage_cb_t *rxMessageCb;       //! user-supplied message-received callback
+    void                *rxMessageUserData; //! data for rxMessageCb
+    lmic_txmessage_cb_t *txMessageCb;       //! transmit-complete message handler; reset on each tx complete.
+    void                *txMessageUserData; //! data for txMessageCb.
+#endif // LMIC_ENABLE_user_events
+
+    /* next we have things that are (u)int32_t */
+    /* none at the moment */
+
+    /* next we have things that are (u)int16_t */
+
+    u2_t        clockError;                 //! Inaccuracy in the clock. CLOCK_ERROR_MAX represents +/-100% error
+
+    /* finally, things that are (u)int8_t */
+    /* none at the moment */
+};
+
+/*
+
+Structure:  lmic_t
+
+Function:
+        Provides the instance data for the LMIC.
+
+*/
+
 struct lmic_t {
+    // client setup data, survives LMIC_reset().
+    lmic_client_data_t  client;
+
+    // the OS job object. pointer alignment.
+    osjob_t     osjob;
+
+#if !defined(DISABLE_BEACONS)
+    bcninfo_t   bcninfo;      // Last received beacon info
+#endif
+
+#if !defined(DISABLE_PING)
+    rxsched_t   ping;         // pingable setup
+#endif
+
+    /* (u)int32_t things */
+
     // Radio settings TX/RX (also accessed by HAL)
     ostime_t    txend;
     ostime_t    rxtime;
 
     // LBT info
     ostime_t    lbt_ticks;      // ticks to listen
-    s1_t        lbt_dbmax;      // max permissible dB on our channel (eg -80)
 
     u4_t        freq;
-    s1_t        rssi;
-    s1_t        snr;            // LMIC.snr is SNR times 4
-    rps_t       rps;
-    u1_t        rxsyms;
-    u1_t        dndr;
-    s1_t        txpow;     // dBm
 
-    osjob_t     osjob;
+    ostime_t    globalDutyAvail; // time device can send again
 
-    // Channel scheduling
+    u4_t        netid;        // current network id (~0 - none)
+    devaddr_t   devaddr;
+    u4_t        seqnoDn;      // device level down stream seqno
+    u4_t        seqnoUp;
+    u4_t        dn2Freq;
+
+#if !defined(DISABLE_BEACONS)
+    ostime_t    bcnRxtime;
+#endif
+
+#if LMIC_ENABLE_DeviceTimeReq
+    // put here for alignment, to reduce RAM use.
+    ostime_t    localDeviceTime;    // the LMIC.txend value for last DeviceTimeAns
+    lmic_gpstime_t netDeviceTime;   // the netDeviceTime for lastDeviceTimeAns
+                                    // zero ==> not valid.
+#endif // LMIC_ENABLE_DeviceTimeReq
+
+    // Channel scheduling -- very much private
 #if CFG_LMIC_EU_like
     band_t      bands[MAX_BANDS];
     u4_t        channelFreq[MAX_CHANNELS];
@@ -304,45 +406,46 @@ struct lmic_t {
     u2_t        activeChannels125khz;
     u2_t        activeChannels500khz;
 #endif
+
+    /* (u)int16_t things */
+
+    rps_t       rps;            // radio parameter selections: SF, BW, CodingRate, NoCrc, implicit hdr
+    u2_t        opmode;         // engineUpdate() operating mode flags
+    u2_t        devNonce;       // last generated nonce
+
+#if !defined(DISABLE_BEACONS)
+    s2_t        drift;          // last measured drift
+    s2_t        lastDriftDiff;
+    s2_t        maxDriftDiff;
+#endif
+
+    /* (u)int8_t things */
+    s1_t        rssi;
+    s1_t        snr;            // LMIC.snr is SNR times 4
+    u1_t        rxsyms;
+    u1_t        dndr;
+    s1_t        txpow;          // transmit dBm (administrative)
+    s1_t        radio_txpow;    // the radio driver's copy of txpow, limited by adrTxPow.
+    s1_t        lbt_dbmax;      // max permissible dB on our channel (eg -80)
+
     u1_t        txChnl;          // channel for next TX
     u1_t        globalDutyRate;  // max rate: 1/2^k
-    ostime_t    globalDutyAvail; // time device can send again
 
-    u4_t        netid;        // current network id (~0 - none)
-    u2_t        opmode;
     u1_t        upRepeat;     // configured up repeat
     s1_t        adrTxPow;     // ADR adjusted TX power
     u1_t        datarate;     // current data rate
     u1_t        errcr;        // error coding rate (used for TX only)
     u1_t        rejoinCnt;    // adjustment for rejoin datarate
-#if !defined(DISABLE_BEACONS)
-    s2_t        drift;        // last measured drift
-    s2_t        lastDriftDiff;
-    s2_t        maxDriftDiff;
-#endif
 
-    u2_t        clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
-                            // represents +/-100% error
+    bit_t       initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244
 
     u1_t        pendTxPort;
     u1_t        pendTxConf;   // confirmed data
     u1_t        pendTxLen;    // +0x80 = confirmed
     u1_t        pendTxData[MAX_LEN_PAYLOAD];
 
-    u2_t        devNonce;     // last generated nonce
     u1_t        nwkKey[16];   // network session key
     u1_t        artKey[16];   // application router session key
-    devaddr_t   devaddr;
-    u4_t        seqnoDn;      // device level down stream seqno
-    u4_t        seqnoUp;
-#if LMIC_ENABLE_DeviceTimeReq
-    // put here for alignment, to reduce RAM use.
-    ostime_t    localDeviceTime;    // the LMIC.txend value for last DeviceTimeAns
-    lmic_gpstime_t netDeviceTime;   // the netDeviceTime for lastDeviceTimeAns
-                                    // zero ==> not valid.
-    lmic_request_network_time_cb_t *pNetworkTimeCb;	// call-back routine
-    void        *pNetworkTimeUserData; // call-back data
-#endif // LMIC_ENABLE_DeviceTimeReq
 
     u1_t        dnConf;       // dn frame confirm pending: LORA::FCT_ACK or 0
     s1_t        adrAckReq;    // counter until we reset data rate (0=off)
@@ -376,7 +479,6 @@ struct lmic_t {
 
     // 2nd RX window (after up stream)
     u1_t        dn2Dr;
-    u4_t        dn2Freq;
 #if !defined(DISABLE_MCMD_DN2P_SET)
     u1_t        dn2Ans;       // 0=no answer pend, 0x80+ACKs
 #endif
@@ -389,10 +491,6 @@ struct lmic_t {
 #if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
     u1_t        pingSetAns;   // answer set cmd and ACK bits
 #endif
-#if !defined(DISABLE_PING)
-    rxsched_t   ping;         // pingable setup
-#endif
-
     // Public part of MAC state
     u1_t        txCnt;
     u1_t        txrxFlags;  // transaction flags (TX-RX combo)
@@ -403,11 +501,10 @@ struct lmic_t {
 #if !defined(DISABLE_BEACONS)
     u1_t        bcnChnl;
     u1_t        bcnRxsyms;    //
-    ostime_t    bcnRxtime;
-    bcninfo_t   bcninfo;      // Last received beacon info
 #endif
 
     u1_t        noRXIQinversion;
+    u1_t        saveIrqFlags;   // last LoRa IRQ flags
 };
 
 //! \var struct lmic_t LMIC
@@ -418,16 +515,19 @@ DECLARE_LMIC; //!< \internal
 #define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
 bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
 bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
-void  LMIC_disableChannel (u1_t channel);
-void  LMIC_enableSubBand(u1_t band);
-void  LMIC_enableChannel(u1_t channel);
-void  LMIC_disableSubBand(u1_t band);
-void  LMIC_selectSubBand(u1_t band);
+bit_t LMIC_disableChannel (u1_t channel);
+bit_t LMIC_enableSubBand(u1_t band);
+bit_t LMIC_enableChannel(u1_t channel);
+bit_t LMIC_disableSubBand(u1_t band);
+bit_t LMIC_selectSubBand(u1_t band);
 
 void  LMIC_setDrTxpow   (dr_t dr, s1_t txpow);  // set default/start DR/txpow
 void  LMIC_setAdrMode   (bit_t enabled);        // set ADR mode (if mobile turn off)
+
 #if !defined(DISABLE_JOIN)
 bit_t LMIC_startJoining (void);
+void  LMIC_tryRejoin    (void);
+void  LMIC_unjoin       (void);
 #endif
 
 void  LMIC_shutdown     (void);
@@ -436,6 +536,7 @@ void  LMIC_reset        (void);
 void  LMIC_clrTxData    (void);
 void  LMIC_setTxData    (void);
 int   LMIC_setTxData2   (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
+int   LMIC_sendWithCallback(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData);
 void  LMIC_sendAlive    (void);
 
 #if !defined(DISABLE_BEACONS)
@@ -447,9 +548,6 @@ void  LMIC_disableTracking (void);
 void  LMIC_stopPingable  (void);
 void  LMIC_setPingable   (u1_t intvExp);
 #endif
-#if !defined(DISABLE_JOIN)
-void  LMIC_tryRejoin     (void);
-#endif
 
 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
 void LMIC_setLinkCheckMode (bit_t enabled);
@@ -462,11 +560,26 @@ void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xre
 void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData);
 int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference);
 
+int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData);
+int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData);
+
+// APIs for client half of compliance.
+typedef u1_t lmic_compliance_rx_action_t;
+
+enum lmic_compliance_rx_action_e {
+    LMIC_COMPLIANCE_RX_ACTION_PROCESS = 0,  // process this message normally
+    LMIC_COMPLIANCE_RX_ACTION_START,        // enter compliance mode, discard this message
+    LMIC_COMPLIANCE_RX_ACTION_IGNORE,       // continue in compliance mode, discard this message
+    LMIC_COMPLIANCE_RX_ACTION_END           // exit compliance mode, discard this message
+};
+
+lmic_compliance_rx_action_t LMIC_complianceRxMessage(u1_t port, const u1_t *pMessage, size_t nMessage);
+
 // Declare onEvent() function, to make sure any definition will have the
 // C conventions, even when in a C++ file.
+#if LMIC_ENABLE_onEvent
 DECL_ON_LMIC_EVENT;
-
-
+#endif /* LMIC_ENABLE_onEvent */
 
 // Special APIs - for development or testing
 // !!!See implementation for caveats!!!
diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c
index 7bdce7d..5050ff7 100755
--- a/src/lmic/lmic_as923.c
+++ b/src/lmic/lmic_as923.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -138,7 +138,7 @@ int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) {
 			(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT
 			);
 			
-	return adj;
+	return LMICas923_getMaxEIRP(LMIC.txParam) + adj;
 }
 
 // only used in this module, but used by variant macro dr2hsym().
diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c
index bd0b629..a6d0fdb 100755
--- a/src/lmic/lmic_au921.c
+++ b/src/lmic/lmic_au921.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -109,9 +109,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
         return 0; // all channels are hardwired.
 }
 
-void LMIC_disableChannel(u1_t channel) {
+bit_t LMIC_disableChannel(u1_t channel) {
+        bit_t result = 0;
         if (channel < 72) {
                 if (ENABLED_CHANNEL(channel)) {
+                        result = 1;
                         if (IS_CHANNEL_125khz(channel))
                                 LMIC.activeChannels125khz--;
                         else if (IS_CHANNEL_500khz(channel))
@@ -119,11 +121,14 @@ void LMIC_disableChannel(u1_t channel) {
                 }
                 LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF));
         }
+        return result;
 }
 
-void LMIC_enableChannel(u1_t channel) {
+bit_t LMIC_enableChannel(u1_t channel) {
+        bit_t result = 0;
         if (channel < 72) {
                 if (!ENABLED_CHANNEL(channel)) {
+                        result = 1;
                         if (IS_CHANNEL_125khz(channel))
                                 LMIC.activeChannels125khz++;
                         else if (IS_CHANNEL_500khz(channel))
@@ -131,42 +136,52 @@ void LMIC_enableChannel(u1_t channel) {
                 }
                 LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF));
         }
+        return result;
 }
 
-void  LMIC_enableSubBand(u1_t band) {
+bit_t LMIC_enableSubBand(u1_t band) {
         ASSERT(band < 8);
         u1_t start = band * 8;
         u1_t end = start + 8;
+        bit_t result = 0;
 
         // enable all eight 125 kHz channels in this subband
         for (int channel = start; channel < end; ++channel)
-                LMIC_enableChannel(channel);
+                result |= LMIC_enableChannel(channel);
 
         // there's a single 500 kHz channel associated with
         // each group of 8 125 kHz channels. Enable it, too.
-        LMIC_enableChannel(64 + band);
+        result |= LMIC_enableChannel(64 + band);
+        return result;
 }
-void  LMIC_disableSubBand(u1_t band) {
+
+bit_t LMIC_disableSubBand(u1_t band) {
         ASSERT(band < 8);
         u1_t start = band * 8;
         u1_t end = start + 8;
+        bit_t result = 0;
 
         // disable all eight 125 kHz channels in this subband
         for (int channel = start; channel < end; ++channel)
-                LMIC_disableChannel(channel);
+                result |= LMIC_disableChannel(channel);
 
         // there's a single 500 kHz channel associated with
         // each group of 8 125 kHz channels. Disable it, too.
-        LMIC_disableChannel(64 + band);
+        result |= LMIC_disableChannel(64 + band);
+        return result;
 }
-void  LMIC_selectSubBand(u1_t band) {
+
+bit_t LMIC_selectSubBand(u1_t band) {
+        bit_t result = 0;
+
         ASSERT(band < 8);
         for (int b = 0; b<8; ++b) {
                 if (band == b)
-                        LMIC_enableSubBand(b);
+                        result |= LMIC_enableSubBand(b);
                 else
-                        LMIC_disableSubBand(b);
+                        result |= LMIC_disableSubBand(b);
         }
+        return result;
 }
 
 void LMICau921_updateTx(ostime_t txbeg) {
@@ -213,6 +228,13 @@ void LMICau921_setRx1Params(void) {
         LMIC.rps = dndr2rps(LMIC.dndr);
 }
 
+void LMICau921_initJoinLoop(void) {
+        LMICuslike_initJoinLoop();
+
+        // initialize the adrTxPower.
+        LMIC.adrTxPow = 30; // dBm
+
+}
 
 //
 // END: AU921 related stuff
diff --git a/src/lmic/lmic_bandplan.h b/src/lmic/lmic_bandplan.h
index 0c3c503..5c837a4 100755
--- a/src/lmic/lmic_bandplan.h
+++ b/src/lmic/lmic_bandplan.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -104,6 +104,10 @@
 # error "LMICbandplan_setBcnRxParams() not defined by bandplan"
 #endif
 
+#if !defined(LMICbandplan_canMapChannels)
+# error "LMICbandplan_canMapChannels() not defined by bandplan"
+#endif
+
 #if !defined(LMICbandplan_mapChannels)
 # error "LMICbandplan_mapChannels() not defined by bandplan"
 #endif
@@ -143,6 +147,15 @@
 #if !defined(LMICbandplan_init)
 # error "LMICbandplan_init() not defined by bandplan"
 #endif
+
+#if !defined(LMICbandplan_saveAdrState)
+# error "LMICbandplan_saveAdrState() not defined by bandplan"
+#endif
+
+#if !defined(LMICbandplan_compareAdrState)
+# error "LMICbandplan_compareAdrState() not defined by bandplan"
+#endif
+
 //
 // Things common to lmic.c code
 //
diff --git a/src/lmic/lmic_bandplan_as923.h b/src/lmic/lmic_bandplan_as923.h
index 50017f2..9695605 100755
--- a/src/lmic/lmic_bandplan_as923.h
+++ b/src/lmic/lmic_bandplan_as923.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -26,8 +26,8 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#ifndef _lmic_as923_h_
-# define _lmic_as923_h_
+#ifndef _lmic_bandplan_as923_h_
+# define _lmic_bandplan_as923_h_
 
 #ifndef _lmic_eu_like_h_
 # include "lmic_eu_like.h"
@@ -112,4 +112,4 @@ void LMICas923_updateTx(ostime_t txbeg);
 ostime_t LMICas923_nextJoinTime(ostime_t now);
 #define LMICbandplan_nextJoinTime(now)     LMICas923_nextJoinTime(now)
 
-#endif // _lmic_as923_h_
+#endif // _lmic_bandplan_as923_h_
diff --git a/src/lmic/lmic_bandplan_au921.h b/src/lmic/lmic_bandplan_au921.h
index f173187..560a1f5 100755
--- a/src/lmic/lmic_bandplan_au921.h
+++ b/src/lmic/lmic_bandplan_au921.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -26,12 +26,12 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#ifndef _lmic_au921_h_
-# define _lmic_au921_h_
+#ifndef _lmic_bandplan_au921_h_
+# define _lmic_bandplan_au921_h_
 
 // preconditions for lmic_us_like.h
-#define LMICuslike_getFirst500kHzDR()   (AU921_DR_SF8C)
-
+#define LMICuslike_getFirst500kHzDR()   (LORAWAN_DR6)
+#define	LMICuslike_getJoin125kHzDR()	(LORAWAN_DR2)
 
 #ifndef _lmic_us_like_h_
 # include "lmic_us_like.h"
@@ -46,7 +46,10 @@ ostime_t LMICau921_dr2hsym(uint8_t dr);
 #define dr2hsym(dr) LMICau921_dr2hsym(dr)
 
 
-#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7)
+#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR2)
+
+void LMICau921_initJoinLoop(void);
+#define LMICbandplan_initJoinLoop()     LMICau921_initJoinLoop()
 
 void LMICau921_setBcnRxParams(void);
 #define LMICbandplan_setBcnRxParams() LMICau921_setBcnRxParams()
@@ -60,4 +63,4 @@ void LMICau921_setRx1Params(void);
 void LMICau921_updateTx(ostime_t txbeg);
 #define LMICbandplan_updateTx(txbeg)    LMICau921_updateTx(txbeg)
 
-#endif // _lmic_au921_h_
+#endif // _lmic_bandplan_au921_h_
diff --git a/src/lmic/lmic_bandplan_eu868.h b/src/lmic/lmic_bandplan_eu868.h
index d1e3adc..63c419a 100755
--- a/src/lmic/lmic_bandplan_eu868.h
+++ b/src/lmic/lmic_bandplan_eu868.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -89,4 +89,7 @@ void LMICeu868_initDefaultChannels(bit_t join);
 ostime_t LMICeu868_nextJoinTime(ostime_t now);
 #define LMICbandplan_nextJoinTime(now)     LMICeu868_nextJoinTime(now)
 
+void LMICeu868_setRx1Params(void);
+#define LMICbandplan_setRx1Params()     LMICeu868_setRx1Params()
+
 #endif // _lmic_eu868_h_
diff --git a/src/lmic/lmic_bandplan_in866.h b/src/lmic/lmic_bandplan_in866.h
index ad1b4e4..8c0cb95 100755
--- a/src/lmic/lmic_bandplan_in866.h
+++ b/src/lmic/lmic_bandplan_in866.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -26,8 +26,8 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#ifndef _lmic_in866_h_
-# define _lmic_in866_h_
+#ifndef _lmic_bandplan_in866_h_
+# define _lmic_bandplan_in866_h_
 
 #ifndef _lmic_eu_like_h_
 # include "lmic_eu_like.h"
@@ -82,4 +82,7 @@ ostime_t LMICin866_nextJoinState(void);
 void LMICin866_initDefaultChannels(bit_t join);
 #define LMICbandplan_initDefaultChannels(join)  LMICin866_initDefaultChannels(join)
 
-#endif // _lmic_in866_h_
+void LMICin866_setRx1Params(void);
+#define LMICbandplan_setRx1Params()     LMICin866_setRx1Params()
+
+#endif // _lmic_bandplan_in866_h_
diff --git a/src/lmic/lmic_bandplan_us915.h b/src/lmic/lmic_bandplan_us915.h
index 28ae2c8..f687095 100755
--- a/src/lmic/lmic_bandplan_us915.h
+++ b/src/lmic/lmic_bandplan_us915.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -26,11 +26,12 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#ifndef _lmic_us915_h_
-# define _lmic_us915_h_
+#ifndef _lmic_bandplan_us915_h_
+# define _lmic_bandplan_us915_h_
 
 // preconditions for lmic_us_like.h
-#define LMICuslike_getFirst500kHzDR()   (US915_DR_SF8C)
+#define LMICuslike_getFirst500kHzDR()   (LORAWAN_DR4)
+#define LMICuslike_getJoin125kHzDR()    (LORAWAN_DR0)
 
 #ifndef _lmic_us_like_h_
 # include "lmic_us_like.h"
@@ -45,7 +46,7 @@ ostime_t LMICus915_dr2hsym(uint8_t dr);
 #define dr2hsym(dr) LMICus915_dr2hsym(dr)
 
 
-#define LMICbandplan_getInitialDrJoin() (US915_DR_SF7)
+#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR0)
 
 void LMICus915_setBcnRxParams(void);
 #define LMICbandplan_setBcnRxParams() LMICus915_setBcnRxParams()
@@ -53,10 +54,13 @@ void LMICus915_setBcnRxParams(void);
 u4_t LMICus915_convFreq(xref2cu1_t ptr);
 #define LMICbandplan_convFreq(ptr)      LMICus915_convFreq(ptr)
 
+void LMICus915_initJoinLoop(void);
+#define LMICbandplan_initJoinLoop()     LMICus915_initJoinLoop()
+
 void LMICus915_setRx1Params(void);
 #define LMICbandplan_setRx1Params()     LMICus915_setRx1Params()
 
 void LMICus915_updateTx(ostime_t txbeg);
 #define LMICbandplan_updateTx(txbeg)    LMICus915_updateTx(txbeg)
 
-#endif // _lmic_us915_h_
+#endif // _lmic_bandplan_us915_h_
diff --git a/src/lmic/lmic_env.h b/src/lmic/lmic_env.h
index 4610fb3..032f44b 100755
--- a/src/lmic/lmic_env.h
+++ b/src/lmic/lmic_env.h
@@ -214,4 +214,38 @@ Returns:
 # endif
 #endif
 
+/*
+
+Macro:	LMIC_DECLARE_FUNCTION_WEAK()
+
+Function:
+	Declare an external function as a weak reference.
+
+Definition:
+	#define LMIC_DECLARE_FUNCTION_WEAK(ReturnType, FunctionName, Params) ...
+
+Description:
+	This macro generates a weak reference to the specified function.
+
+Example:
+	LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e));
+
+	This saya that onEvent is a weak external reference. When calling
+	onEvent, you must always first check whether it's supplied:
+
+	if (onEvent != NULL)
+		onEvent(e);
+
+Returns:
+	This macro expands to a declaration, without a trailing semicolon.
+
+Notes:
+	This form allows for compilers that use _Pragma(weak, name) instead
+	of inline attributes.
+
+*/
+
+#define LMIC_DECLARE_FUNCTION_WEAK(a_ReturnType, a_FunctionName, a_Params)	\
+	a_ReturnType __attribute__((__weak__)) a_FunctionName a_Params
+
 #endif /* _lmic_env_h_ */
\ No newline at end of file
diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c
index 3c87334..e47082b 100755
--- a/src/lmic/lmic_eu868.c
+++ b/src/lmic/lmic_eu868.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -59,7 +59,7 @@ uint8_t LMICeu868_maxFrameLen(uint8_t dr) {
 }
 
 static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
-        20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
+        16, 14, 12, 10, 8, 6, 4, 2, 0,0,0,0, 0,0,0,0
 };
 
 int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) {
@@ -221,6 +221,28 @@ LMICeu868_txDoneFSK(ostime_t delay, osjobcb_t func) {
         os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
 }
 
+// set the Rx1 dndr, rps.
+void LMICeu868_setRx1Params(void) {
+    u1_t const txdr = LMIC.dndr;
+    s1_t drOffset;
+    s1_t candidateDr;
+
+    if ( LMIC.rx1DrOffset <= 5)
+        drOffset = (s1_t) LMIC.rx1DrOffset;
+    else
+        // make a reasonable assumption for unspecified value.
+        drOffset = 5;
+
+    candidateDr = (s1_t) txdr - drOffset;
+    if (candidateDr < LORAWAN_DR0)
+            candidateDr = 0;
+    else if (candidateDr > LORAWAN_DR7)
+            candidateDr = LORAWAN_DR7;
+
+    LMIC.dndr = (u1_t) candidateDr;
+    LMIC.rps = dndr2rps(LMIC.dndr);
+}
+
 void
 LMICeu868_initJoinLoop(void) {
         LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ EU868_TX_EIRP_MAX_DBM);
diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c
index 60c3006..2f51069 100755
--- a/src/lmic/lmic_eu_like.c
+++ b/src/lmic/lmic_eu_like.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -32,37 +32,47 @@
 
 #if CFG_LMIC_EU_like
 
-void  LMIC_enableSubBand(u1_t band) {
+bit_t LMIC_enableSubBand(u1_t band) {
         LMIC_API_PARAMETER(band);
+        return 0;
 }
 
-void  LMIC_disableSubBand(u1_t band) {
+bit_t LMIC_disableSubBand(u1_t band) {
         LMIC_API_PARAMETER(band);
+        return 0;
 }
 
-void LMIC_disableChannel(u1_t channel) {
+bit_t LMIC_disableChannel(u1_t channel) {
+        u2_t old_chmap = LMIC.channelMap;
         LMIC.channelFreq[channel] = 0;
         LMIC.channelDrMap[channel] = 0;
-        LMIC.channelMap &= ~(1 << channel);
+        LMIC.channelMap = old_chmap & ~(1 << channel);
+        return LMIC.channelMap != old_chmap;
 }
 
 // this is a no-op provided for compatibilty
-void LMIC_enableChannel(u1_t channel) {
+bit_t LMIC_enableChannel(u1_t channel) {
         LMIC_API_PARAMETER(channel);
+        return 0;
 }
 
-u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
-        // Bad page, disable all channel, enable non-existent
+bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap) {
         if (chpage != 0 || chmap == 0 || (chmap & ~LMIC.channelMap) != 0)
                 return 0;  // illegal input
         for (u1_t chnl = 0; chnl<MAX_CHANNELS; chnl++) {
                 if ((chmap & (1 << chnl)) != 0 && LMIC.channelFreq[chnl] == 0)
-                        chmap &= ~(1 << chnl); // ignore - channel is not defined
+                        return 0; // fail - channel is not defined
         }
-        LMIC.channelMap = chmap;
         return 1;
 }
 
+// assumes that LMICeulike_canMapChannels passed. Return true if something changed.
+bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
+        u2_t const old_chmap = LMIC.channelMap;
+        LMIC.channelMap = chmap;
+        return old_chmap != chmap;
+}
+
 #if !defined(DISABLE_JOIN)
 void LMICeulike_initJoinLoop(uint8_t nDefaultChannels, s1_t adrTxPow) {
 #if CFG_TxContinuousMode
@@ -159,4 +169,19 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
 }
 #endif // !DISABLE_JOIN
 
+void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
+        memcpy(
+                pStateBuffer->channelFreq,
+                LMIC.channelFreq,
+                sizeof(LMIC.channelFreq)
+        );
+        pStateBuffer->channelMap = LMIC.channelMap;
+}
+
+bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
+        if (memcmp(pStateBuffer->channelFreq, LMIC.channelFreq, sizeof(LMIC.channelFreq)) != 0)
+                return 1;
+        return pStateBuffer->channelMap != LMIC.channelMap;
+}
+
 #endif // CFG_LMIC_EU_like
diff --git a/src/lmic/lmic_eu_like.h b/src/lmic/lmic_eu_like.h
index f147790..0f51c85 100755
--- a/src/lmic/lmic_eu_like.h
+++ b/src/lmic/lmic_eu_like.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -74,14 +74,14 @@ enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 };
 #define LMICbandplan_setSessionInitDefaultChannels()    \
         do { LMICbandplan_initDefaultChannels(/* normal, not join */ 0); } while (0)
 
-u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap);
+bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap);
+#define LMICbandplan_canMapChannels(c, m)  LMICeulike_canMapChannels(c, m)
+
+bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap);
 #define LMICbandplan_mapChannels(c, m)  LMICeulike_mapChannels(c, m)
 
 void LMICeulike_initJoinLoop(u1_t nDefaultChannels, s1_t adrTxPow);
 
-#define LMICbandplan_setRx1Params() \
-        do { /*LMIC.freq/rps remain unchanged*/ } while (0)
-
 void LMICeulike_updateTx(ostime_t txbeg);
 #define LMICbandplan_updateTx(t)        LMICeulike_updateTx(t)
 
@@ -95,4 +95,10 @@ static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) {
 #define LMICbandplan_init()     \
         do { /* nothing */ } while (0)
 
+void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
+#define LMICbandplan_saveAdrState(pState) LMICeulike_saveAdrState(pState)
+
+bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer);
+#define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState)
+
 #endif // _lmic_eu_like_h_
diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c
index ad52134..1788ca4 100755
--- a/src/lmic/lmic_in866.c
+++ b/src/lmic/lmic_in866.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -59,7 +59,7 @@ uint8_t LMICin866_maxFrameLen(uint8_t dr) {
 }
 
 static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
-        20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
+        30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 0, 0,0,0,0
 };
 
 int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) {
@@ -195,6 +195,27 @@ LMICin866_txDoneFSK(ostime_t delay, osjobcb_t func) {
         os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
 }
 
+// set the Rx1 dndr, rps.
+void LMICin866_setRx1Params(void) {
+    u1_t const txdr = LMIC.dndr;
+    s1_t drOffset;
+    s1_t candidateDr;
+
+    if ( LMIC.rx1DrOffset <= 5)
+        drOffset = (s1_t) LMIC.rx1DrOffset;
+    else
+        drOffset = 5 - (s1_t) LMIC.rx1DrOffset;
+
+    candidateDr = (s1_t) txdr - drOffset;
+    if (candidateDr < LORAWAN_DR0)
+            candidateDr = 0;
+    else if (candidateDr > LORAWAN_DR5)
+            candidateDr = LORAWAN_DR5;
+
+    LMIC.dndr = (u1_t) candidateDr;
+    LMIC.rps = dndr2rps(LMIC.dndr);
+}
+
 void
 LMICin866_initJoinLoop(void) {
         LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ IN866_TX_EIRP_MAX_DBM);
diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c
index 46f16b6..1d524d4 100755
--- a/src/lmic/lmic_us915.c
+++ b/src/lmic/lmic_us915.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -98,9 +98,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
         return 1;
 }
 
-void LMIC_disableChannel(u1_t channel) {
+bit_t LMIC_disableChannel(u1_t channel) {
+        bit_t result = 0;
         if (channel < 72 + MAX_XCHANNELS) {
                 if (ENABLED_CHANNEL(channel)) {
+                        result = 1;
                         if (IS_CHANNEL_125khz(channel))
                                 LMIC.activeChannels125khz--;
                         else if (IS_CHANNEL_500khz(channel))
@@ -108,11 +110,14 @@ void LMIC_disableChannel(u1_t channel) {
                 }
                 LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF));
         }
+        return result;
 }
 
-void LMIC_enableChannel(u1_t channel) {
+bit_t LMIC_enableChannel(u1_t channel) {
+        bit_t result = 0;
         if (channel < 72 + MAX_XCHANNELS) {
                 if (!ENABLED_CHANNEL(channel)) {
+                        result = 1;
                         if (IS_CHANNEL_125khz(channel))
                                 LMIC.activeChannels125khz++;
                         else if (IS_CHANNEL_500khz(channel))
@@ -120,42 +125,52 @@ void LMIC_enableChannel(u1_t channel) {
                 }
                 LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF));
         }
+        return result;
 }
 
-void  LMIC_enableSubBand(u1_t band) {
+bit_t  LMIC_enableSubBand(u1_t band) {
         ASSERT(band < 8);
         u1_t start = band * 8;
         u1_t end = start + 8;
+        bit_t result = 0;
 
         // enable all eight 125 kHz channels in this subband
         for (int channel = start; channel < end; ++channel)
-                LMIC_enableChannel(channel);
+                result |= LMIC_enableChannel(channel);
 
         // there's a single 500 kHz channel associated with
         // each group of 8 125 kHz channels. Enable it, too.
-        LMIC_enableChannel(64 + band);
+        result |= LMIC_enableChannel(64 + band);
+        return result;
 }
-void  LMIC_disableSubBand(u1_t band) {
+
+bit_t  LMIC_disableSubBand(u1_t band) {
         ASSERT(band < 8);
         u1_t start = band * 8;
         u1_t end = start + 8;
+        bit_t result = 0;
 
         // disable all eight 125 kHz channels in this subband
         for (int channel = start; channel < end; ++channel)
-                LMIC_disableChannel(channel);
+                result |= LMIC_disableChannel(channel);
 
         // there's a single 500 kHz channel associated with
         // each group of 8 125 kHz channels. Disable it, too.
-        LMIC_disableChannel(64 + band);
+        result |= LMIC_disableChannel(64 + band);
+        return result;
 }
-void  LMIC_selectSubBand(u1_t band) {
+
+bit_t  LMIC_selectSubBand(u1_t band) {
+        bit_t result = 0;
+
         ASSERT(band < 8);
         for (int b = 0; b<8; ++b) {
                 if (band == b)
-                        LMIC_enableSubBand(b);
+                        result |= LMIC_enableSubBand(b);
                 else
-                        LMIC_disableSubBand(b);
+                        result |= LMIC_disableSubBand(b);
         }
+        return result;
 }
 
 void LMICus915_updateTx(ostime_t txbeg) {
@@ -193,16 +208,31 @@ void LMICus915_setBcnRxParams(void) {
 }
 #endif // !DISABLE_BEACONS
 
-// TODO(tmm@mcci.com): parmeterize for US-like
+// set the Rx1 dndr, rps.
 void LMICus915_setRx1Params(void) {
+    u1_t const txdr = LMIC.dndr;
+    u1_t candidateDr;
     LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP;
-    if( /* TX datarate */LMIC.dndr < US915_DR_SF8C )
-        LMIC.dndr += US915_DR_SF10CR - US915_DR_SF10;
-    else if( LMIC.dndr == US915_DR_SF8C )
-        LMIC.dndr = US915_DR_SF7CR;
+    if ( /* TX datarate */txdr < LORAWAN_DR4)
+            candidateDr = txdr + 10 - LMIC.rx1DrOffset;
+    else
+            candidateDr = LORAWAN_DR13 - LMIC.rx1DrOffset;
+
+    if (candidateDr < LORAWAN_DR8)
+            candidateDr = LORAWAN_DR8;
+    else if (candidateDr > LORAWAN_DR13)
+            candidateDr = LORAWAN_DR13;
+
+    LMIC.dndr = candidateDr;
     LMIC.rps = dndr2rps(LMIC.dndr);
 }
 
+void LMICus915_initJoinLoop(void) {
+    LMICuslike_initJoinLoop();
+
+    // initialize the adrTxPower.
+    LMIC.adrTxPow = 20; // dBm
+}
 
 //
 // END: US915 related stuff
diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c
index d1ff69b..9ec4e61 100755
--- a/src/lmic/lmic_us_like.c
+++ b/src/lmic/lmic_us_like.c
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -91,7 +91,46 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) {
         LMIC.activeChannels500khz = 8;
 }
 
-u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
+// verify that a given setting is permitted
+bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) {
+	/*
+	|| MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_125OFF are special. The
+	|| channel map appllies to 500kHz (ch 64..71) and in addition
+	|| all channels 0..63 are turned off or on.  MCMC_LADR_CHP_BANK
+	|| is also special, in that it enables subbands.
+	*/
+	if (chpage < MCMD_LADR_CHP_USLIKE_SPECIAL) {
+		if (chmap == 0)
+			return 0;
+
+		// operate on channels 0..15, 16..31, 32..47, 48..63, 64..71
+		if (chpage == (64 >> 4)) {
+			if (chmap & 0xFF00) {
+				// those are reserved bits, fail.
+				return 0;
+			}
+		}
+	} else if (chpage == MCMD_LADR_CHP_BANK) {
+		if (chmap == 0 || (chmap & 0xFF00) != 0) {
+			// no bits set, or reserved bitsset , fail.
+			return 0;
+		}
+	} else if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) {
+                u1_t const en125 = chpage == MCMD_LADR_CHP_125ON;
+
+		// if disabling all 125kHz chans, must have at least one 500kHz chan
+		// don't allow reserved bits to be set in chmap.
+		if ((! en125 && chmap == 0) || (chmap & 0xFF00) != 0)
+			return 0;
+	} else {
+		return 0;
+	}
+
+	// if we get here, it looks legal.
+	return 1;
+}
+
+bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
 	/*
 	|| MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_125OFF are special. The
 	|| channel map appllies to 500kHz (ch 64..71) and in addition
@@ -99,61 +138,53 @@ u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
 	|| is also special, in that it enables subbands.
 	*/
 	u1_t base, top;
+	bit_t result = 0;
+
+	if (chpage == MCMD_LADR_CHP_BANK) {
+		// each bit enables a bank of channels
+		for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) {
+			if (chmap & 1) {
+				result |= LMIC_enableSubBand(subband);
+			} else {
+				result |= LMIC_disableSubBand(subband);
+			}
+		}
+
+		return result;
+	}
 
 	if (chpage < MCMD_LADR_CHP_USLIKE_SPECIAL) {
 		// operate on channels 0..15, 16..31, 32..47, 48..63
 		base = chpage << 4;
 		top = base + 16;
 		if (base == 64) {
-			if (chmap & 0xFF00) {
-				// those are reserved bits, fail.
-				return 0;
-			}
 			top = 72;
 		}
-	} else if (chpage == MCMD_LADR_CHP_BANK) {
-		if (chmap & 0xFF00) {
-			// those are resreved bits, fail.
-			return 0;
-		}
-		// each bit enables a bank of channels
-		for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) {
-			if (chmap & 1) {
-				LMIC_enableSubBand(subband);
-			} else {
-				LMIC_disableSubBand(subband);
-			}
-
-		// don't change any channels below
-		base = top = 0;
-		}
-	} else if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) {
+	} else /* if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) */ {
                 u1_t const en125 = chpage == MCMD_LADR_CHP_125ON;
 
 		// enable or disable all 125kHz channels
 		for (u1_t chnl = 0; chnl < 64; ++chnl) {
 			if (en125)
-				LMIC_enableChannel(chnl);
+				result |= LMIC_enableChannel(chnl);
 			else
-				LMIC_disableChannel(chnl);
+				result |= LMIC_disableChannel(chnl);
 		}
 
 		// then apply mask to top 8 channels.
 		base = 64;
 		top = 72;
-	} else {
-		return 0;
 	}
 
 	// apply chmap to channels in [base..top-1].
 	// Use enable/disable channel to keep activeChannel counts in sync.
 	for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) {
 		if (chmap & 0x0001)
-			LMIC_enableChannel(chnl);
+			result |= LMIC_enableChannel(chnl);
 		else
-			LMIC_disableChannel(chnl);
+			result |= LMIC_disableChannel(chnl);
         }
-        return 1;
+        return result;
 }
 
 // US does not have duty cycling - return now as earliest TX time
@@ -183,18 +214,13 @@ void LMICuslike_initJoinLoop(void) {
         // starting point.
         setNextChannel(0, 64, LMIC.activeChannels125khz);
 
-        // initialize the adrTxPower.
-        // TODO(tmm@mcci.com): is this right for all US-like regions
-        LMIC.adrTxPow = 20; // dBm
-        ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0);
-
         // make sure LMIC.txend is valid.
         LMIC.txend = os_getTime();
+        ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0);
 
-        // make sure the datarate is set to DR0 per LoRaWAN regional reqts V1.0.2,
-        // section 2.2.2
-        // TODO(tmm@mcci.com): parameterize this for US-like
-        LMICcore_setDrJoin(DRCHG_SET, LORAWAN_DR0);
+        // make sure the datarate is set to DR2 per LoRaWAN regional reqts V1.0.2,
+        // section 2.*.2
+        LMICcore_setDrJoin(DRCHG_SET, LMICbandplan_getInitialDrJoin());
 
         // TODO(tmm@mcci.com) need to implement the transmit randomization and
         // duty cycle restrictions from LoRaWAN V1.0.2 section 7.
@@ -233,7 +259,7 @@ ostime_t LMICuslike_nextJoinState(void) {
                 setNextChannel(0, 64, LMIC.activeChannels125khz);
 
                 // TODO(tmm@mcci.com) parameterize
-                s1_t dr = LORAWAN_DR0;
+                s1_t dr = LMICuslike_getJoin125kHzDR();
                 if ((++LMIC.txCnt & 0x7) == 0) {
                         failed = 1; // All DR exhausted - signal failed
                 }
@@ -260,4 +286,16 @@ ostime_t LMICuslike_nextJoinState(void) {
 }
 #endif
 
+void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
+        memcpy(
+                pStateBuffer->channelMap,
+                LMIC.channelMap,
+                sizeof(LMIC.channelMap)
+        );
+}
+
+bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
+        return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0;
+}
+
 #endif // CFG_LMIC_US_like
diff --git a/src/lmic/lmic_us_like.h b/src/lmic/lmic_us_like.h
index 66bc549..ba6dc89 100755
--- a/src/lmic/lmic_us_like.h
+++ b/src/lmic/lmic_us_like.h
@@ -1,6 +1,6 @@
 /*
 * Copyright (c) 2014-2016 IBM Corporation.
-* Copyright (c) 2017 MCCI Corporation.
+* Copyright (c) 2017, 2019 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
@@ -43,6 +43,9 @@
 #define IS_CHANNEL_500khz(c) (c>=64 && c<72)
 #define ENABLED_CHANNEL(chnl) ((LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0x0F))) != 0)
 
+// library functions: called from bandplan
+void LMICuslike_initJoinLoop(void);
+
 // provide the isValidBeacon1 function -- int for bool.
 static inline int
 LMICuslike_isValidBeacon1(const uint8_t *d) {
@@ -77,24 +80,30 @@ void LMICuslike_initDefaultChannels(bit_t fJoin);
 #define LMICbandplan_setSessionInitDefaultChannels()    \
         do { /* nothing */} while (0)
 
-u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap);
+bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap);
+#define LMICbandplan_canMapChannels(chpage, chmap) LMICuslike_canMapChannels(chpage, chmap)
+
+bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap);
 #define LMICbandplan_mapChannels(chpage, chmap) LMICuslike_mapChannels(chpage, chmap)
 
 ostime_t LMICuslike_nextTx(ostime_t now);
 #define LMICbandplan_nextTx(now)        LMICuslike_nextTx(now)
 
-void LMICuslike_initJoinLoop(void);
-#define LMICbandplan_initJoinLoop()     LMICuslike_initJoinLoop()
-
 ostime_t LMICuslike_nextJoinState(void);
 #define LMICbandplan_nextJoinState()    LMICuslike_nextJoinState();
 
-static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) {
+static inline ostime_t LMICuslike_nextJoinTime(ostime_t now) {
         return now;
 }
-#define LMICbandplan_nextJoinTime(now)     LMICeulike_nextJoinTime(now)
+#define LMICbandplan_nextJoinTime(now)     LMICuslike_nextJoinTime(now)
 
 #define LMICbandplan_init()     \
         do { /* nothing */ } while (0)
 
+void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
+#define LMICbandplan_saveAdrState(pState) LMICuslike_saveAdrState(pState)
+
+bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer);
+#define LMICbandplan_compareAdrState(pState) LMICuslike_compareAdrState(pState)
+
 #endif // _lmic_us_like_h_
diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h
index 656ea2c..ff2f10b 100755
--- a/src/lmic/lorabase.h
+++ b/src/lmic/lorabase.h
@@ -44,7 +44,14 @@ typedef u1_t cr_t;
 typedef u1_t sf_t;
 typedef u1_t bw_t;
 typedef u1_t dr_t;
+
 // Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
+// 2..0:    Spreading factor
+// 4..3:    bandwidth: 0 == 125kHz, 1 == 250 kHz, 2 == 500 kHz. 3 == reserved.
+// 6..5:    coding rate: 0 == 4/5, 1 == 4/6, 2 == 4/7, 3 == 4/8
+// 7:       nocrc: 0 == with crc, 1 == without crc
+// 15..8:   Implicit header control: 0 ==> none, 1..0xFF ==> length in bytes.
+
 typedef u2_t rps_t;
 TYPEDEF_xref2rps_t;
 
@@ -52,7 +59,7 @@ enum { ILLEGAL_RPS = 0xFF };
 
 // Global maximum frame length
 enum { STD_PREAMBLE_LEN  =  8 };
-enum { MAX_LEN_FRAME     = 64 };
+enum { MAX_LEN_FRAME     =  LMIC_ENABLE_long_messages ? 255 : 64 };
 enum { LEN_DEVNONCE      =  2 };
 enum { LEN_ARTNONCE      =  3 };
 enum { LEN_NETID         =  3 };
@@ -414,7 +421,6 @@ enum {
     HDR_FTYPE_DADN   = 0x60,  // data (unconfirmed) dn
     HDR_FTYPE_DCUP   = 0x80,  // data confirmed up
     HDR_FTYPE_DCDN   = 0xA0,  // data confirmed dn
-    HDR_FTYPE_REJOIN = 0xC0,  // rejoin for roaming
     HDR_FTYPE_PROP   = 0xE0
 };
 enum {
diff --git a/src/lmic/oslmic.c b/src/lmic/oslmic.c
index 0552d91..74b8692 100755
--- a/src/lmic/oslmic.c
+++ b/src/lmic/oslmic.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
- * Copyright (c) 2016-2017 MCCI Corporation.
+ * Copyright (c) 2016-2017, 2019 MCCI Corporation.
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -68,13 +68,15 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
     return 0;
 }
 
+static osjob_t** getJobQueue(osjob_t* job) {
+    return os_jobIsTimed(job) ? &OS.scheduledjobs : &OS.runnablejobs;
+}
+
 // clear scheduled job
 void os_clearCallback (osjob_t* job) {
     hal_disableIRQs();
 
-    // if it's not in the scheduled jobs, look in the runnable...
-    if (! unlinkjob(&OS.scheduledjobs, job))
-        unlinkjob(&OS.runnablejobs, job);
+    unlinkjob(getJobQueue(job), job);
 
     hal_enableIRQs();
 }
@@ -83,11 +85,15 @@ void os_clearCallback (osjob_t* job) {
 void os_setCallback (osjob_t* job, osjobcb_t cb) {
     osjob_t** pnext;
     hal_disableIRQs();
+
     // remove if job was already queued
-    unlinkjob(&OS.runnablejobs, job);
-    // fill-in job
-    job->func = cb;
+    unlinkjob(getJobQueue(job), job);
+
+    // fill-in job. Ascending memory order is write-queue friendly
     job->next = NULL;
+    job->deadline = 0;
+    job->func = cb;
+
     // add to end of run queue
     for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
     *pnext = job;
@@ -97,13 +103,21 @@ void os_setCallback (osjob_t* job, osjobcb_t cb) {
 // schedule timed job
 void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
     osjob_t** pnext;
+
+    // special case time 0 -- it will be one tick late.
+    if (time == 0)
+        time = 1;
+
     hal_disableIRQs();
+
     // remove if job was already queued
-    unlinkjob(&OS.scheduledjobs, job);
+    unlinkjob(getJobQueue(job), job);
+
     // fill-in job
+    job->next = NULL;
     job->deadline = time;
     job->func = cb;
-    job->next = NULL;
+
     // insert into schedule
     for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
         if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
@@ -141,3 +155,13 @@ void os_runloop_once() {
         j->func(j);
     }
 }
+
+// return true if there are any jobs scheduled within time ticks from now.
+// return false if any jobs scheduled are at least time ticks in the future.
+bit_t os_queryTimeCriticalJobs(ostime_t time) {
+    if (OS.scheduledjobs &&
+        OS.scheduledjobs->deadline - os_getTime() < time)
+        return 1;
+    else
+        return 0;
+}
diff --git a/src/lmic/oslmic.h b/src/lmic/oslmic.h
index 7710c1d..8126704 100755
--- a/src/lmic/oslmic.h
+++ b/src/lmic/oslmic.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
- * Copyright (c) 2018 MCCI Corporation
+ * Copyright (c) 2018, 2019 MCCI Corporation
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -84,8 +84,7 @@ typedef              u1_t* xref2u1_t;
 
 #define SIZEOFEXPR(x) sizeof(x)
 
-#define ON_LMIC_EVENT(ev)  onEvent(ev)
-#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
+#define DECL_ON_LMIC_EVENT LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e))
 
 extern u4_t AESAUX[];
 extern u4_t AESKEY[];
@@ -149,7 +148,13 @@ void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi);
 
 
 struct osjob_t;  // fwd decl.
-typedef void (*osjobcb_t) (struct osjob_t*);
+
+//! the function type for osjob_t callbacks
+typedef void (osjobcbfn_t)(struct osjob_t*);
+
+//! the pointer-to-function for osjob_t callbacks
+typedef osjobcbfn_t *osjobcb_t;
+
 struct osjob_t {
     struct osjob_t* next;
     ostime_t deadline;
@@ -157,6 +162,11 @@ struct osjob_t {
 };
 TYPEDEF_xref2osjob_t;
 
+//! determine whether a job is timed or immediate. os_setTimedCallback()
+// must treat incoming == 0 as being 1 instead.
+static inline int os_jobIsTimed(xref2osjob_t job) {
+    return (job->deadline != 0);
+}
 
 #ifndef HAS_os_calls
 
@@ -190,6 +200,10 @@ void os_radio (u1_t mode);
 #ifndef os_getBattLevel
 u1_t os_getBattLevel (void);
 #endif
+#ifndef os_queryTimeCriticalJobs
+//! Return non-zero if any jobs are scheduled between now and now+time.
+bit_t os_queryTimeCriticalJobs(ostime_t time);
+#endif
 
 #ifndef os_rlsbf4
 //! Read 32-bit quantity from given pointer in little endian byte order.
diff --git a/src/lmic/radio.c b/src/lmic/radio.c
index 5320a1c..4d33cf0 100755
--- a/src/lmic/radio.c
+++ b/src/lmic/radio.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
- * Copyright (c) 2016-2018 MCCI Corporation.
+ * Copyright (c) 2016-2019 MCCI Corporation.
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -188,6 +188,12 @@
 
 #define SX1276_MC1_IMPLICIT_HEADER_MODE_ON    0x01
 
+#ifdef CFG_sx1276_radio
+# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON	SX1276_MC1_IMPLICIT_HEADER_MODE_ON
+#else
+# define SX127X_MC1_IMPLICIT_HEADER_MODE_ON	SX1272_MC1_IMPLICIT_HEADER_MODE_ON
+#endif
+
 // sx1276 RegModemConfig2
 #define SX1276_MC2_RX_PAYLOAD_CRCON        0x04
 
@@ -264,7 +270,7 @@
 #define MAP_DIO0_LORA_TXDONE   0x40  // 01------
 #define MAP_DIO1_LORA_RXTOUT   0x00  // --00----
 #define MAP_DIO1_LORA_NOP      0x30  // --11----
-#define MAP_DIO2_LORA_NOP      0xC0  // ----11--
+#define MAP_DIO2_LORA_NOP      0x0C  // ----11--
 
 #define MAP_DIO0_FSK_READY     0x00  // 00------ (packet sent / payload ready)
 #define MAP_DIO1_FSK_NOP       0x30  // --11----
@@ -445,7 +451,7 @@ static void configChannel () {
 static void configPower () {
 #ifdef CFG_sx1276_radio
     // PA_BOOST output is assumed but not 20 dBm.
-    s1_t pw = (s1_t)LMIC.txpow;
+    s1_t pw = (s1_t)LMIC.radio_txpow;
     if(pw > 17) {
         pw = 17;
     } else if(pw < 2) {
@@ -461,7 +467,7 @@ static void configPower () {
 
 #elif CFG_sx1272_radio
     // set PA config (2-17 dBm using PA_BOOST)
-    s1_t pw = (s1_t)LMIC.txpow;
+    s1_t pw = (s1_t)LMIC.radio_txpow;
     if(pw > 17) {
         pw = 17;
     } else if(pw < 2) {
@@ -634,8 +640,8 @@ static void rxlora (u1_t rxmode) {
     // set LNA gain
     writeReg(RegLna, LNA_RX_GAIN);
     // set max payload size
-    writeReg(LORARegPayloadMaxLength, 64);
-#if !defined(DISABLE_INVERT_IQ_ON_RX)
+    writeReg(LORARegPayloadMaxLength, MAX_LEN_FRAME);
+#if !defined(DISABLE_INVERT_IQ_ON_RX) /* DEPRECATED(tmm@mcci.com); #250. remove test, always include code in V3 */
     // use inverted I/Q signal (prevent mote-to-mote communication)
 
     // XXX: use flag to switch on/off inversion
@@ -660,6 +666,9 @@ static void rxlora (u1_t rxmode) {
     // enable antenna switch for RX
     hal_pin_rxtx(0);
 
+    writeReg(LORARegFifoAddrPtr, 0);
+    writeReg(LORARegFifoRxBaseAddr, 0);
+
     // now instruct the radio to receive
     if (rxmode == RXMODE_SINGLE) { // single rx
         hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
@@ -958,6 +967,7 @@ void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
 #endif
     if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
         u1_t flags = readReg(LORARegIrqFlags);
+	LMIC.saveIrqFlags = flags;
         LMIC_X_DEBUG_PRINTF("IRQ=%02x\n", flags);
         if( flags & IRQ_LORA_TXDONE_MASK ) {
             // save exact tx time
@@ -969,7 +979,7 @@ void radio_irq_handler_v2 (u1_t dio, ostime_t now) {
             }
             LMIC.rxtime = now;
             // read the PDU and inform the MAC that we received something
-            LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
+            LMIC.dataLen = (readReg(LORARegModemConfig1) & SX127X_MC1_IMPLICIT_HEADER_MODE_ON) ?
                 readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
             // set FIFO read address pointer
             writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));