LMIC for MOTE_L152RC

Dependents:   lmic_transmit

LoRa WAN in C for NA-mote 72

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 LMIC_reverse_memcpy() for AppEUI to keep same byte order as that 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 LMIC_reverse_memcpy() for DevEUI to keep same byte order as that 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.


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.

Information

DevEUI is entered in reverse order into C-code from that shown on server (unique device ID).

AppEUI is entered in reverse order into C-code from that shown on server.

AppEUI is equivalent to "Application"

transmit power limits

FCC Part 15 rules permit one watt of transmit power when more than 50 channels are used. When received by a 64-channel gateway, the maximum power may be used.

However, if end-device is sending to a 8-channel gateway (single SX1301), the maximum transmit power permitted is +20dBm.

To configure LMIC for use with 8-channel gateway, CHNL_HYBRID should be defined in in config.h, and should be undefined for use with 64-channel gateway.

CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Revision:
8:0faa1bb768b5
Parent:
7:9095e54e381f
Child:
9:83ae7f34e88c
--- a/lmic.cpp	Mon Sep 21 17:59:42 2015 +0000
+++ b/lmic.cpp	Wed Oct 21 01:03:34 2015 +0000
@@ -11,6 +11,7 @@
 
 //! \file
 #include "lmic.h"
+#include "debug.h"  // tmp wbr
 
 #if !defined(MINRX_SYMS)
 #define MINRX_SYMS 5
@@ -496,8 +497,11 @@
                         e_.prevdr    = LMIC.datarate|DR_PAGE,
                         e_.prevtxpow = LMIC.adrTxPow));
     
-    if( pow != KEEP_TXPOW )
+    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);
@@ -675,7 +679,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);
@@ -750,6 +754,9 @@
     
     LMIC.txpow_limit = 30;
 #endif
+
+    LMIC.txpow = LMIC.txpow_limit;
+    LMIC.adrTxPow = LMIC.txpow_limit;
 }
 
 static u4_t convFreq (xref2u1_t ptr) {
@@ -790,12 +797,18 @@
 
 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 = LMIC.txpow_limit;
+#ifdef JOIN_REQ_DEBUG
+    printf("%d (125khz)\r\n", LMIC.freq);
+#endif /* JOIN_REQ_DEBUG */    
         return;
     }
     
+    /* using 500KHz channel */
     if (LMIC.txpow_limit >= 26)
         LMIC.txpow = 26;
     else
@@ -803,9 +816,15 @@
         
     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
@@ -900,25 +919,28 @@
 
 static void initJoinLoop (void) {
     LMIC.chRnd = 0;
-    _nextTx();  //LMIC.txChnl = 0;
-    LMIC.adrTxPow = 20;
+    LMIC.txChnl = 0;
+    LMIC.joinBlockChnl = 0;
+    LMIC.joinBlock = 0;
+    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) {
+    u1_t failed = 0;
+    
+#if 0
     // 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);
         _nextTx();
         setDrJoin(DRCHG_SET, DR_SF8C);
     } else {
-        //LMIC._txChnl = os_getRndU1() & 0x3F;
         _nextTx();
         s1_t dr = DR_SF7 - ++LMIC.txCnt;
         if( dr < DR_SF10 ) {
@@ -927,6 +949,26 @@
         }
         setDrJoin(DRCHG_SET, dr);
     }
+#endif /* #if 0 */
+
+    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 {
+        // 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()
@@ -1419,6 +1461,7 @@
                            e_.info   = dlen < 4 ? 0 : mic,
                            e_.info2  = hdr + (dlen<<8)));
       badframe:
+        printf("pja badframe dlen:%d, hdr:%02x\r\n", dlen, hdr);
         if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
             return 0;
         goto nojoinframe;
@@ -1436,11 +1479,7 @@
 
 #if defined(CFG_eu868)
     initDefaultChannels(0);
-#endif
     if( dlen > LEN_JA ) {
-#if defined(CFG_us915)
-        goto badframe;
-#endif
         dlen = OFF_CFLIST;
         for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) {
             u4_t freq = convFreq(&LMIC.frame[dlen]);
@@ -1448,6 +1487,29 @@
                 LMIC_setupChannel(chidx, freq, 0, -1);
         }
     }
+#elif defined(CFG_us915)
+#ifdef JA_DEBUG
+    debug_buf(LMIC.frame, dlen);
+#endif /* JA_DEBUG */
+    /*                                                     11 12  13 14  15 16  17 18  19 1a  1b 1c  1d 1e 1f  20 */
+    /* 20 27 2a b1 01 00 00 d0 44 76 03 00 00 0c c5 59 6c, xx xx, xx xx, xx xx, xx xx, xx xx, xx xx, xx xx xx, xx */
+    /*  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 10,  {0} ,  {1} ,  {2} ,  {3} ,  {4} ,  {5} ,   res   , type */
+    /* join accept: [0] to [16], CFList: [17] to [32] */
+    if (dlen == LEN_JAEXT) {
+#ifdef JA_DEBUG        
+        printf("cflistType:%d\r\n", LMIC.frame[32]);
+#endif /* JA_DEBUG */
+        if (LMIC.frame[32] == 1) {
+            u2_t *u2_ptr = (u2_t *)&LMIC.frame[17]; // LoRaWAN is little endian
+            for (u1_t idx = 0; idx < 5; idx++)
+                LMIC.channelMap[idx] = u2_ptr[idx];
+#ifdef JA_DEBUG                
+            for (u1_t idx = 0; idx < 5; idx++)
+                printf("%d map %04x\r\n", idx, LMIC.channelMap[idx]);
+#endif /* JA_DEBUG */                
+        }
+    }
+#endif /* CFG_us915 */
 
     // already incremented when JOIN REQ got sent off
     aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
@@ -1627,6 +1689,7 @@
 
     if( LMIC.txCnt == 0 ) {
         LMIC.seqnoUp += 1;
+        //printf("seqnoUp inc %d\r\n", LMIC.seqnoUp);
         DO_DEVDB(LMIC.seqnoUp,seqnoUp);
     } else {
         EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
@@ -2121,7 +2184,7 @@
     LMIC.devNonce     =  os_getRndU2();
     LMIC.opmode       =  OP_NONE;
     LMIC.errcr        =  CR_4_5;
-    LMIC.adrEnabled   =  FCT_ADREN;
+    LMIC.adrEnabled   =  0; /* FCT_ADREN */
     LMIC.dn2Dr        =  DR_DNW2;   // we need this for 2nd DN window of join accept
     LMIC.dn2Freq      =  FREQ_DNW2; // ditto
     LMIC.ping.freq    =  FREQ_PING; // defaults for ping
@@ -2241,4 +2304,11 @@
     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];    
+}