Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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_HYBRID | 125KHz | 500KHz |
|---|---|---|
| defined value | channels | channel |
| 0 | 0 to 7 | 64 |
| 1 | 8 to 15 | 65 |
| 2 | 16 to 23 | 66 |
| 3 | 24 to 31 | 67 |
| 4 | 32 to 39 | 68 |
| 5 | 40 to 47 | 69 |
| 6 | 48 to 55 | 70 |
| 7 | 56 to 63 | 71 |
| undef | 0 to 63 | 64 to 71 |
Revision 4:85b2b647cb64, committed 2015-11-26
- 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
--- 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 };