IBM LoRa MAC in C (LMiC) mbed library port

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

LoRa WAN in C for sx1276 shield

Currently version 1.5


LoRaWAN network configuration for end-device

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

AppEUI

Uniquely identifies application provider of end-device.

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

example C code

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

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

DevEUI

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

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

example C code

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

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

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

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

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

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


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

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.
CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71

Files at this revision

API Documentation at this revision

Comitter:
mluis
Date:
Thu Nov 26 17:17:08 2015 +0000
Parent:
3:519c71d29a06
Parent:
2:974cafbfb159
Commit message:
Merged branches

Changed in this revision

lmic.cpp Show annotated file Show diff for this revision Revisions of this file
lmic.h Show annotated file Show diff for this revision Revisions of this file
radio.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/lmic.cpp	Thu Nov 26 12:46:56 2015 +0000
+++ b/lmic.cpp	Thu Nov 26 17:17:08 2015 +0000
@@ -236,7 +236,7 @@
 #elif defined(CFG_us915) // ========================================
 
 #define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
-const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255,  66,142 };
+//const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255,  66,142 };
 
 const u1_t _DR2RPS_CRC[] = {
     ILLEGAL_RPS,
@@ -315,10 +315,10 @@
     return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
 }
 
-extern inline s1_t  rssi2s1 (int v);
+/*extern inline s1_t  rssi2s1 (int v);
 extern inline int   s12rssi (s1_t v);
 extern inline float  s12snr (s1_t v);
-extern inline s1_t   snr2s1 (double v);
+extern inline s1_t   snr2s1 (double v);*/
 
 extern inline rps_t updr2rps (dr_t dr);
 extern inline rps_t dndr2rps (dr_t dr);
@@ -501,8 +501,11 @@
                         e_.prevdr    = LMIC.datarate|DR_PAGE,
                         e_.prevtxpow = LMIC.adrTxPow));
     
-    if( pow != KEEP_TXPOW )
-        LMIC.adrTxPow = pow;
+    if( pow != KEEP_TXPOW ) {
+         LMIC.adrTxPow = pow;
+        if (pow < LMIC.txpow_limit)
+            LMIC.txpow = pow;
+    }
     if( LMIC.datarate != dr ) {
         LMIC.datarate = dr;
         DO_DEVDB(LMIC.datarate,datarate);
@@ -548,7 +551,7 @@
     os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
     os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
 
-    LMIC.channelMap = 0x1F;
+    LMIC.channelMap = 0x3;
     u1_t su = join ? 0 : 6;
     for( u1_t fu=0; fu<6; fu++,su++ ) {
         LMIC.channelFreq[fu]  = iniChannelFreq[su];
@@ -680,7 +683,7 @@
 
 #define setRx1Params() /*LMIC.freq/rps remain unchanged*/
 
-static void initJoinLoop (void) {
+static void initJoinLoop (void) {   // eu868
     LMIC.txChnl = os_getRndU1() % 6;
     LMIC.adrTxPow = 14;
     setDrJoin(DRCHG_SET, DR_SF7);
@@ -731,11 +734,31 @@
 // BEG: US915 related stuff
 //
 
-
-static void initDefaultChannels (void) {
+static void initDefaultChannels (void)
+{
+#ifdef CHNL_HYBRID
+        int idx = CHNL_HYBRID >> 1;
+        LMIC.channelMap[0] = 0x0000;
+        LMIC.channelMap[1] = 0x0000;
+        LMIC.channelMap[2] = 0x0000;
+        LMIC.channelMap[3] = 0x0000;
+        if (CHNL_HYBRID & 1)
+            LMIC.channelMap[idx] = 0xff00;
+        else
+            LMIC.channelMap[idx] = 0x00ff;
+            
+        LMIC.channelMap[4] = 1 << CHNL_HYBRID;
+        LMIC.txpow_limit = 20;
+#else
     for( u1_t i=0; i<4; i++ )
         LMIC.channelMap[i] = 0xFFFF;
     LMIC.channelMap[4] = 0x00FF;
+    
+    LMIC.txpow_limit = 30;
+#endif
+
+    LMIC.txpow = LMIC.txpow_limit;
+    LMIC.adrTxPow = LMIC.txpow_limit;
 }
 
 static u4_t convFreq (xref2u1_t ptr) {
@@ -777,17 +800,41 @@
 
 static void updateTx (ostime_t txbeg) {
     u1_t chnl = LMIC.txChnl;
+#ifdef JOIN_REQ_DEBUG    
+    printf("chnl%d ", chnl);
+#endif /* JOIN_REQ_DEBUG */  
     if( chnl < 64 ) {
         LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
-        LMIC.txpow = 30;
+
+        if (LMIC.opmode & OP_JOINING) {
+            /* use max allowed power for joining */
+            if (LMIC.txpow <  LMIC.txpow_limit)
+                LMIC.txpow = LMIC.txpow_limit;
+        }
+
+#ifdef JOIN_REQ_DEBUG
+    printf("%d (125khz)\r\n", LMIC.freq);
+#endif /* JOIN_REQ_DEBUG */    
         return;
     }
-    LMIC.txpow = 26;
+    
+    /* using 500KHz channel */
+    if (LMIC.txpow_limit >= 26)
+        LMIC.txpow = 26;
+    else
+        LMIC.txpow = LMIC.txpow_limit;
+        
     if( chnl < 64+8 ) {
         LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
+#ifdef JOIN_REQ_DEBUG
+    printf("%d (500k)\r\n", LMIC.freq);
+#endif /* JOIN_REQ_DEBUG */            
     } else {
         ASSERT(chnl < 64+8+MAX_XCHANNELS);
         LMIC.freq = LMIC.xchFreq[chnl-72];
+#ifdef JOIN_REQ_DEBUG
+    printf("%d (x)\r\n", LMIC.freq);
+#endif /* JOIN_REQ_DEBUG */           
     }
 
     // Update global duty cycle stats
@@ -797,29 +844,72 @@
     }
 }
 
+int count_bits(u2_t v)
+{
+    int c;
+    
+    for (c = 0; v; c++) {
+        v &= v - 1; // clear the last significant bit set
+    }
+
+    return c;
+}
+
 // US does not have duty cycling - return now as earliest TX time
 #define nextTx(now) (_nextTx(),(now))
 static void _nextTx (void) {
-    if( LMIC.chRnd==0 )
-        LMIC.chRnd = os_getRndU1() & 0x3F;
+    u1_t prev_ch = LMIC.txChnl;
+    u1_t tries = 0;
+    u1_t en_cnt;
+    
     if( LMIC.datarate >= DR_SF8C ) { // 500kHz
-        u1_t map = LMIC.channelMap[64/16]&0xFF;
-        for( u1_t i=0; i<8; i++ ) {
-            if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
-                LMIC.txChnl = 64 + (LMIC.chRnd & 7);
-                return;
-            }
-        }
+#ifdef CHNL_HYBRID
+        LMIC.txChnl = 1 << CHNL_HYBRID; // only one channel possible
+#else
+        en_cnt = count_bits(LMIC.channelMap[4]);
+        do {
+            do {
+                LMIC.chRnd = os_getRndU1() & 7;
+                if (++tries > 48)
+                    return;
+            } while ( !(LMIC.channelMap[4] & (1 << LMIC.chRnd)) );
+            LMIC.txChnl = 64 + LMIC.chRnd;
+            if (en_cnt < 2)
+                prev_ch = LMIC.txChnl + 1;  // not enough enabled, skip the following test
+                
+        } while (prev_ch == LMIC.txChnl);
+#endif
     } else { // 125kHz
-        for( u1_t i=0; i<64; i++ ) {
-            u1_t chnl = ++LMIC.chRnd & 0x3F;
-            if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
-                LMIC.txChnl = chnl;
-                return;
-            }
-        }
+#ifdef CHNL_HYBRID
+        u1_t idx = CHNL_HYBRID >> 1;
+        en_cnt = count_bits(LMIC.channelMap[idx]);
+        do {
+            do {
+                LMIC.chRnd = os_getRndU1() & 15;
+                if (++tries > 96)
+                    return;
+            } while ( !(LMIC.channelMap[idx] & (1 << LMIC.chRnd)) );
+            LMIC.txChnl = (idx << 4) + LMIC.chRnd;
+            if (en_cnt < 2)
+                prev_ch = LMIC.txChnl + 1;  // not enough enabled, skip the following test
+                            
+        } while (prev_ch == LMIC.txChnl);
+#else
+        en_cnt = count_bits(LMIC.channelMap[0]);
+        en_cnt += count_bits(LMIC.channelMap[1]);
+        en_cnt += count_bits(LMIC.channelMap[2]);
+        en_cnt += count_bits(LMIC.channelMap[3]);
+        do {
+            do {
+                LMIC.chRnd = os_getRndU1() & 63;
+            } while ( !(LMIC.channelMap[LMIC.chRnd >> 4] & (1 << (LMIC.chRnd & 15))) );
+            LMIC.txChnl = LMIC.chRnd;
+            if (en_cnt < 2)
+                prev_ch = LMIC.txChnl + 1;  // not enough enabled, skip the following test
+                
+        } while (prev_ch == LMIC.txChnl);
+#endif
     }
-    // No feasible channel  found! Keep old one.
 }
 
 static void setBcnRxParams (void) {
@@ -839,31 +929,43 @@
 
 static void initJoinLoop (void) {
     LMIC.chRnd = 0;
+#ifdef CHNL_HYBRID
+    LMIC.joinBlockChnl = 0;
+    LMIC.joinBlock = CHNL_HYBRID;
+    LMIC.txChnl = LMIC.joinBlock << 3;
+#else
     LMIC.txChnl = 0;
-    LMIC.adrTxPow = 20;
+    LMIC.joinBlockChnl = 0;
+    LMIC.joinBlock = 0;
+#endif
+    LMIC.datarate = DR_SF10;
+    LMIC.adrTxPow = LMIC.txpow_limit;
     ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
     LMIC.txend = os_getTime();
     setDrJoin(DRCHG_SET, DR_SF7);
 }
 
 static ostime_t nextJoinState (void) {
-    // Try the following:
-    //   SF7/8/9/10  on a random channel 0..63
-    //   SF8C        on a random channel 64..71
-    //
     u1_t failed = 0;
-    if( LMIC.datarate != DR_SF8C ) {
-        LMIC.txChnl = 64+(LMIC.txChnl&7);
-        setDrJoin(DRCHG_SET, DR_SF8C);
+    
+    if( LMIC.datarate == DR_SF8C ) {
+        // attempted 500khz channel, try 125khz channel in next block
+        LMIC.datarate = DR_SF10;
+        if (++LMIC.joinBlock == 8) {
+            LMIC.joinBlock = 0;
+            if (++LMIC.joinBlockChnl == 8)
+                LMIC.joinBlockChnl = 0;
+        }
+        LMIC.txChnl = (LMIC.joinBlock << 3) + LMIC.joinBlockChnl;
     } else {
-        LMIC.txChnl = os_getRndU1() & 0x3F;
-        s1_t dr = DR_SF7 - ++LMIC.txCnt;
-        if( dr < DR_SF10 ) {
-            dr = DR_SF10;
-            failed = 1; // All DR exhausted - signal failed
-        }
-        setDrJoin(DRCHG_SET, dr);
+        // attempted 125khz channel, try 500khz channel
+        LMIC.datarate = DR_SF8C;
+        LMIC.txChnl = LMIC.joinBlock + 64;
     }
+#ifdef JOIN_REQ_DEBUG
+    printf("njs blk%d, dr%d, txChnl%d ", LMIC.joinBlock, LMIC.datarate, LMIC.txChnl); // crlf in updateTx()
+#endif /* JOIN_REQ_DEBUG */
+    
     LMIC.opmode &= ~OP_NEXTCHNL;
     LMIC.txend = os_getTime() +
         (isTESTMODE()
@@ -931,9 +1033,9 @@
     ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
     xref2u1_t d = LMIC.frame;
     if(
-#if CFG_eu868
+#ifdef CFG_eu868
         d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
-#elif CFG_us915
+#elif defined(CFG_us915)
         os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
 #endif
         )
@@ -1322,7 +1424,7 @@
 
     if( LMIC.dataLen == 0 ) {
       nojoinframe:
-        if( (LMIC.opmode & OP_JOINING) == 0 ) {
+        /* keep retrying -- if( (LMIC.opmode & OP_JOINING) == 0 ) {
             ASSERT((LMIC.opmode & OP_REJOIN) != 0);
             // REJOIN attempt for roaming
             LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
@@ -1330,7 +1432,7 @@
                 LMIC.rejoinCnt++;
             reportEvent(EV_REJOIN_FAILED);
             return 1;
-        }
+        }*/
         LMIC.opmode &= ~OP_TXRXPEND;
         ostime_t delay = nextJoinState();
         EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
@@ -1860,7 +1962,7 @@
     LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
     LMIC.bcnRxsyms = LMIC.rxsyms;    
   rev:
-#if CFG_us915
+#ifdef CFG_us915
     LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
 #endif
     if( (LMIC.opmode & OP_PINGINI) != 0 )
@@ -1930,11 +2032,13 @@
         if( txbeg - (now + TX_RAMPUP) < 0 ) {
             // We could send right now!
             dr_t txdr = (dr_t)LMIC.datarate;
+            txbeg = now;
             if( jacc ) {
                 u1_t ftype;
                 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
                     txdr = lowerDR(txdr, LMIC.rejoinCnt);
-                    ftype = HDR_FTYPE_REJOIN;
+                    //ftype = HDR_FTYPE_REJOIN;
+                    ftype = HDR_FTYPE_JREQ;
                 } else {
                     ftype = HDR_FTYPE_JREQ;
                 }
@@ -2170,4 +2274,10 @@
     LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
 }
 
- 
+void LMIC_reverse_memcpy(u1_t *dst, const u1_t *src, size_t n)
+{
+    size_t i;
+
+    for (i=0; i < n; ++i)
+        dst[n-1-i] = src[i];    
+}
--- a/lmic.h	Thu Nov 26 12:46:56 2015 +0000
+++ b/lmic.h	Thu Nov 26 17:17:08 2015 +0000
@@ -16,8 +16,9 @@
 #define _lmic_h_
 
 // MBED compiler options
-#define CFG_eu868                                   1
-//#define CFG_us915                                   1
+//#define CFG_eu868                                   1
+#define CFG_us915                                   1
+#define CHNL_HYBRID     0       /* US915: 0-7 to select block of 8 channels used */
 
 #define USE_SMTC_RADIO_DRIVER                       1
 
@@ -30,16 +31,16 @@
 
 // LMIC version
 #define LMIC_VERSION_MAJOR 1
-#define LMIC_VERSION_MINOR 4
-#define LMIC_VERSION_BUILD 1426605786
+#define LMIC_VERSION_MINOR 5
+#define LMIC_VERSION_BUILD 1431528305
 
 enum { MAX_FRAME_LEN      =  64 };   //!< 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    =   6 ,    // continue with this after reported dead link
-       LINK_CHECK_DEAD    =  12 ,    // after this UP frames and no response from NWK assume link is dead
+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
        LINK_CHECK_OFF     =-128 };   // link check disabled
 
@@ -157,6 +158,7 @@
     u1_t        rxsyms;
     u1_t        dndr;
     s1_t        txpow;     // dBm
+    s1_t        txpow_limit;    // dBm maximum permitted
 
     osjob_t     osjob;
 
@@ -183,6 +185,8 @@
     u1_t        datarate;     // current data rate
     u1_t        errcr;        // error coding rate (used for TX only)
     u1_t        rejoinCnt;    // adjustment for rejoin datarate
+    u1_t        joinBlock;    // during join attempt: current channel block
+    u1_t        joinBlockChnl;    // during join attempt: current 125KHz channel    
     s2_t        drift;        // last measured drift
     s2_t        lastDriftDiff;
     s2_t        maxDriftDiff;
@@ -268,6 +272,7 @@
 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
 void LMIC_setLinkCheckMode (bit_t enabled);
 
+void LMIC_reverse_memcpy(u1_t *dst, const u1_t *src, size_t len);
 // Special APIs - for development or testing
 // !!!See implementation for caveats!!!
 
--- a/lorabase.h	Thu Nov 26 12:46:56 2015 +0000
+++ b/lorabase.h	Thu Nov 26 17:17:08 2015 +0000
@@ -84,8 +84,7 @@
 enum { DR_PING           = SF9 };       // default ping DR
 enum { CHNL_DNW2         = 5 };
 enum { FREQ_DNW2         = EU868_F6 };
-enum { DR_DNW2           = DR_SF9 };    // changed from LoRaWAN specification.
-                                        // Default value is DR_SF12
+enum { DR_DNW2           = DR_SF12 };
 enum { CHNL_BCN          = 5 };
 enum { FREQ_BCN          = EU868_F6 };
 enum { DR_BCN            = DR_SF9 };