From ad4fd70a1b9575ac525f4c68133d2cfa1dd4d4ca Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 15 Aug 2019 04:01:39 -0400 Subject: [PATCH 01/27] Fix #87: refactor to parse MAC messages properly --- README.md | 6 +- src/lmic/config.h | 10 +- src/lmic/lmic.c | 428 +++++++++++++++++++-------------- src/lmic/lmic.h | 20 +- src/lmic/lmic_as923.c | 36 ++- src/lmic/lmic_au921.c | 7 + src/lmic/lmic_bandplan_au921.h | 3 +- src/lmic/lmic_bandplan_us915.h | 3 +- src/lmic/lmic_eu868.c | 24 +- src/lmic/lmic_in866.c | 25 +- src/lmic/lmic_kr920.c | 25 +- src/lmic/lmic_us915.c | 7 + src/lmic/lorabase.h | 91 +++---- 13 files changed, 417 insertions(+), 268 deletions(-) diff --git a/README.md b/README.md index 016e2b61..324665dc 100644 --- a/README.md +++ b/README.md @@ -314,14 +314,14 @@ If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_star #### Disabling Class A MAC commands -`DISABLE_MCMD_DCAP_REQ`, `DISABLE_MCMD_DN2P_SET`, and `DISABLE_MCMD_SNCH_REQ` respectively disable code for various Class A MAC +`DISABLE_MCMD_DutyCycleReq`, `DISABLE_MCMD_RXParamSetupReq`, and `DISABLE_MCMD_NewChannelReq` respectively disable code for various Class A MAC commands. #### Disabling Class B MAC commands -`DISABLE_MCMD_PING_SET` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. +`DISABLE_MCMD_PingSlotChannelReq` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. -`DISABLE_MCMD_BCNI_ANS` disables the next-beacon start command. It's implied by `DISABLE_BEACON` +`ENABLE_MCMD_BeaconTimingAns` enables the next-beacon start command. It's disabled by default, and overridden (if enabled) by `DISABLE_BEACON`. (This command is deprecated.) #### Disabling user events diff --git a/src/lmic/config.h b/src/lmic/config.h index 32e30a92..2e59ab59 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -116,12 +116,12 @@ // define these in lmic_project_config.h to disable the corresponding MAC commands. // Class A -//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap -//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param -//#define DISABLE_MCMD_SNCH_REQ // set new channel +//#define DISABLE_MCMD_DutyCycleReq // duty cycle cap +//#define DISABLE_MCMD_RXParamSetupReq // 2nd DN window param +//#define DISABLE_MCMD_NewChannelReq // set new channel // Class B -//#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 +//#define DISABLE_MCMD_PingSlotChannelReq // set ping freq, automatically disabled by DISABLE_PING +//#define ENABLE_MCMD_BeaconTimingAns // next beacon start, DEPRECATED, normally disabled by DISABLE_BEACON // DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this. //#define DISABLE_INVERT_IQ_ON_RX diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index f2f0e690..fd539b7c 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -591,19 +591,13 @@ static void resetJoinParams(void) { static void stateJustJoined (void) { LMIC.seqnoDn = LMIC.seqnoUp = 0; LMIC.rejoinCnt = 0; - LMIC.dnConf = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0; -#if !defined(DISABLE_MCMD_SNCH_REQ) - LMIC.snchAns = 0; -#endif -#if !defined(DISABLE_MCMD_DN2P_SET) + LMIC.dnConf = LMIC.adrChanged = 0; +#if !defined(DISABLE_MCMD_RXParamSetupReq) LMIC.dn2Ans = 0; #endif LMIC.moreData = 0; -#if !defined(DISABLE_MCMD_DCAP_REQ) +#if !defined(DISABLE_MCMD_DutyCycleReq) LMIC.dutyCapAns = 0; -#endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - LMIC.pingSetAns = 0; #endif LMIC.upRepeat = 0; resetJoinParams(); @@ -653,6 +647,36 @@ static int decodeBeacon (void) { } #endif // !DISABLE_BEACONS +// put a mac response to the current output buffer. +static bit_t put_mac_uplink_byte(uint8_t b) { + uint8_t *pEnd; + uint8_t *pBuffer; + + if (LMIC.pendMacLen < 0) { + if (LMIC.pendTxLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendTxLen++] = b; + return 1; + } else { + return 0; + } + } else { + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } +} + +static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t b2) { + return put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2); +} + +static bit_t put_mac_uplink_byte3(u1_t b1, u1_t b2, u1_t b3) { + return put_mac_uplink_byte(b1) && put_mac_uplink_byte2(b2, b3); +} + static CONST_TABLE(u1_t, macCmdSize)[] = { /* 2: LinkCheckAns */ 3, /* 3: LinkADRReq */ 5, @@ -667,7 +691,7 @@ static CONST_TABLE(u1_t, macCmdSize)[] = { /* 0x0D: DeviceTimeAns */ 6, /* 0x0E, 0x0F */ 0, 0, /* 0x10: PingSlotInfoAns */ 1, - /* 0x11: PingSlotChannelReq */ 4, + /* 0x11: PingSlotChannelReq */ 5, /* 0x12: BeaconTimingAns */ 4, /* 0x13: BeaconFreqReq */ 4 }; @@ -683,138 +707,184 @@ static u1_t getMacCmdSize(u1_t macCmd) { static void applyAdrRequests( const uint8_t *opts, - int olen + int olen, + u1_t adrAns ) { - 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; + lmic_saved_adr_state_t initialState; + int const kAdrReqSize = 5; + int oidx; + u1_t p1 = 0; + u1_t p4 = 0; - LMICbandplan_saveAdrState(&initialState); + LMICbandplan_saveAdrState(&initialState); - for (oidx = 0; oidx < olen; ) { - u1_t const cmd = opts[oidx]; + for (oidx = 0; oidx < olen; ) { + if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels - 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]; // ChMaskCtl, NbTrans + u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK; // channel page - 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); + } - LMICbandplan_mapChannels(chpage, chmap); - } + oidx += kAdrReqSize; + put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); + } - int cmdlen = getMacCmdSize(cmd); + // all done scanning options + bit_t changes = LMICbandplan_compareAdrState(&initialState); - // this really is an assert, we should never here - // unless all the commands are valid. - ASSERT(cmdlen != 0); + // handle uplink repeat count + u1_t uprpt = p4 & MCMD_LADR_REPEAT_MASK; // up repeat count + if (LMIC.upRepeat != uprpt) { + LMIC.upRepeat = uprpt; + changes = 1; + } - oidx += cmdlen; + if (adrAns & MCMD_LinkADRAns_DataRateACK) { + dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); + + // handle power changes here, too. + changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + } + + LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" +} + + +int scan_mac_cmds_link_adr( + const char *opts, + int olen + ) + { + if (olen == 0) + return 0; + + int oidx = 0; + int const kAdrReqSize = 5; + int lastOidx; + u1_t adrAns = MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK; + + // process the contiguous slots + for (;;) { + lastOidx = oidx; + + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; } + 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 + dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - // all done scanning options - bit_t changes = LMICbandplan_compareAdrState(&initialState); + if( !LMICbandplan_canMapChannels(chpage, chmap) ) + adrAns &= ~MCMD_LinkADRAns_ChannelACK; - // handle uplink repeat count - u1_t uprpt = p4 & MCMD_LADR_REPEAT_MASK; // up repeat count - if (LMIC.upRepeat != uprpt) { - LMIC.upRepeat = uprpt; - changes = 1; + if( !validDR(dr) ) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + } + if (pow2dBm(p1) == -128) { + adrAns &= ~MCMD_LinkADRAns_PowerACK; } - // handle power changes - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + oidx += kAdrReqSize; + if (opts[oidx] != MCMD_LinkADRReq) + break; + } + + // go back and apply the ADR changes, if any -- use the effective length, + // and process. + applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); - LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + return lastOidx; } -} // scan mac commands starting at opts[] for olen, return count of bytes consumed. +// build response either in pendMacData[] or pendTxData[], depending on whether the +// pointer is before static int scan_mac_cmds( const uint8_t *opts, - int olen + int olen, + int port ) { 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; + if (port == 0) + LMIC.pendMacLen = -1; + else + LMIC.pendMacLen = 0; + while( oidx < olen ) { cmd = opts[oidx]; switch( cmd ) { - case MCMD_LCHK_ANS: { + case MCMD_LinkCheckAns: { + // TODO(tmm@mcci.com) capture these, reliably.. //int gwmargin = opts[oidx+1]; //int ngws = opts[oidx+2]; 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 - - // 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) ) { - LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK; - 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-4]))); - } + // from 1.0.3 spec section 5.2: + // For the purpose of configuring the end-device channel mask, the end-device will + // process all contiguous LinkAdrReq messages, in the order present in the downlink message, + // as a single atomic block command. The end-device will accept or reject all Channel Mask + // controls in the contiguous block, and provide consistent Channel Mask ACK status + // indications for each command in the contiguous block in each LinkAdrAns message, + // reflecting the acceptance or rejection of this atomic channel mask setting. + // + // So we need to process all the contigious commands + case MCMD_LinkADRReq: { + // skip over all but the last command. + oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx); break; } - case MCMD_DEVS_REQ: { - LMIC.devsAns = 1; + + case MCMD_DevStatusReq: { // LMIC.snr is SNR time 4, convert to real SNR; rounding towards zero. 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)); + + put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); break; } - case MCMD_DN2P_SET: { -#if !defined(DISABLE_MCMD_DN2P_SET) + + case MCMD_RXParamSetupReq: { +#if !defined(DISABLE_MCMD_RXParamSetupReq) dr_t dr = (dr_t)(opts[oidx+1] & 0x0F); u1_t rx1DrOffset = (u1_t)((opts[oidx+1] & 0x70) >> 4); u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); - LMIC.dn2Ans = 0x80; // answer pending + LMIC.dn2Ans = 0xC0; // answer pending, but send this one in order. if( validDR(dr) ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX2DataRateACK; if( freq != 0 ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_ChannelACK; if (rx1DrOffset <= 3) - LMIC.dn2Ans |= MCMD_DN2P_ANS_RX1DrOffsetAck; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX1DrOffsetAck; - if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK| MCMD_DN2P_ANS_RX1DrOffsetAck) ) { + if( LMIC.dn2Ans == (0x80|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { LMIC.dn2Dr = dr; LMIC.dn2Freq = freq; LMIC.rx1DrOffset = rx1DrOffset; DO_DEVDB(LMIC.dn2Dr,dn2Dr); DO_DEVDB(LMIC.dn2Freq,dn2Freq); } -#endif // !DISABLE_MCMD_DN2P_SET + + /* put the first copy of the message */ + put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); +#endif // !DISABLE_MCMD_RXParamSetupReq break; } - case MCMD_DCAP_REQ: { -#if !defined(DISABLE_MCMD_DCAP_REQ) + + case MCMD_DutyCycleReq: { +#if !defined(DISABLE_MCMD_DutyCycleReq) u1_t cap = opts[oidx+1]; // A value cap=0xFF means device is OFF unless enabled again manually. if( cap==0xFF ) @@ -822,38 +892,67 @@ scan_mac_cmds( LMIC.globalDutyRate = cap & 0xF; LMIC.globalDutyAvail = os_getTime(); DO_DEVDB(cap,dutyCap); - LMIC.dutyCapAns = 1; -#endif // !DISABLE_MCMD_DCAP_REQ + + put_mac_uplink_byte(MCMD_DutyCycleAns); +#endif // !DISABLE_MCMD_DutyCycleReq break; } - case MCMD_SNCH_REQ: { -#if !defined(DISABLE_MCMD_SNCH_REQ) + + case MCMD_NewChannelReq: { +#if !defined(DISABLE_MCMD_NewChannelReq) u1_t chidx = opts[oidx+1]; // channel + u4_t raw_feq0 = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq u1_t drs = opts[oidx+5]; // datarate span - LMIC.snchAns = 0x80; - 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 + u1_t ans = MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK; + + if (freq == 0 && raw_feq0 != 0) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + u1_t MaxDR = drs >> 4; + u1_t MinDR = drs & 0xF; + if (MaxDR < MinDR || !validDR(MaxDR) || validDR(MinDR)) + ans &= ~MCMD_NewChannelAns_DataRateACK; + + if( ans == MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK) { + if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + } + + put_mac_uplink_byte2(MCMD_NewChannelAns, ans); +#endif // !DISABLE_MCMD_NewChannelReq break; } - case MCMD_PING_SET: { -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) + + case MCMD_PingSlotChannelReq: { +#if !defined(DISABLE_MCMD_PingSlotChannelReq) && !defined(DISABLE_PING) + u4_t raw_feq0 = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); - u1_t flags = 0x80; - if( freq != 0 ) { - flags |= MCMD_PING_ANS_FQACK; + u1_t dr = opts[oidx+4] & 0xF; + u1_t ans = MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK; + if (raw_feq0) { + freq = FREQ_PING; + } else if (freq == 0) { + ans &= ~MCMD_PingSlotFreqAns_ChannelACK; + } + if (! validDR(dr)) + ans &= ~MCMD_PingSlotFreqAns_DataRateACK; + + if (ans == MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK) { LMIC.ping.freq = freq; + LMIC.ping.dr = dr; DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); DO_DEVDB(LMIC.ping.freq, pingFreq); DO_DEVDB(LMIC.ping.dr, pingDr); } - LMIC.pingSetAns = flags; -#endif // !DISABLE_MCMD_PING_SET && !DISABLE_PING + put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); +#endif // !DISABLE_MCMD_PingSlotChannelReq && !DISABLE_PING break; } - case MCMD_BCNI_ANS: { -#if !defined(DISABLE_MCMD_BCNI_ANS) && !defined(DISABLE_BEACONS) + + case MCMD_BeaconTimingAns: { +#if defined(ENABLE_MCMD_BeaconTimingAns) && !defined(DISABLE_BEACONS) // Ignore if tracking already enabled if( (LMIC.opmode & OP_TRACK) == 0 ) { LMIC.bcnChnl = opts[oidx+3]; @@ -863,13 +962,13 @@ scan_mac_cmds( ASSERT(LMIC.bcninfoTries!=0); // Setup RX parameters LMIC.bcninfo.txtime = (LMIC.rxtime - + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT) - + ms2osticksCeil(MCMD_BCNI_TUNIT/2) + + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BeaconTimingAns_TUNIT) + + ms2osticksCeil(MCMD_BeaconTimingAns_TUNIT/2) - BCN_INTV_osticks); LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared) - calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms + calcBcnRxWindowFromMillis(MCMD_BeaconTimingAns_TUNIT,1); // error of +/-N ms - EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS, + EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BeaconTimingAns, e_.eui = MAIN::CDEV->getEui(), e_.lostmic = Base::lsbf4(&d[pend]), e_.info = (LMIC.missedBcns | @@ -877,23 +976,25 @@ scan_mac_cmds( - LMIC.bcnRxtime) << 8)), e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); } -#endif // !DISABLE_MCMD_BCNI_ANS && !DISABLE_BEACONS +#endif // !ENABLE_MCMD_BeaconTimingAns && !DISABLE_BEACONS break; } /* end case */ + case MCMD_TxParamSetupReq: { #if LMIC_ENABLE_TxParamSetupReq uint8_t txParam; txParam = opts[oidx+1]; - // we don't allow unrecognized bits to come through + // we don't allow unrecognized bits to get to txParam. txParam &= (MCMD_TxParam_RxDWELL_MASK| MCMD_TxParam_TxDWELL_MASK| MCMD_TxParam_MaxEIRP_MASK); LMIC.txParam = txParam; - LMIC.txParamSetupAns = 1; + put_mac_uplink_byte(MCMD_TxParamSetupAns); #endif // LMIC_ENABLE_TxParamSetupReq break; } /* end case */ + case MCMD_DeviceTimeAns: { #if LMIC_ENABLE_DeviceTimeReq // don't process a spurious downlink. @@ -944,10 +1045,6 @@ scan_mac_cmds( oidx += cmdlen; } /* end while */ - // go back and apply the ADR changes, if any -- use the effective length - if (fSawAdrReq) - applyAdrRequests(opts, olen); - return oidx; } @@ -1060,19 +1157,30 @@ static bit_t decodeFrame (void) { // We heard from network LMIC.adrChanged = LMIC.rejoinCnt = 0; setAdrAckCount(LINK_CHECK_INIT); +#if !defined(DISABLE_MCMD_RXParamSetupReq) + LMIC.dn2Ans = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) 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. // it's only computed for legacy clients LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; + if (port == 0 && olen != 0) { #if LMIC_DEBUG_LEVEL > 0 - // Process OPTS - LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen); + // discard, section 4.3.1.6 line 544-546 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); #endif + goto norx; + } else { +#if LMIC_DEBUG_LEVEL > 0 + // Process OPTS + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen); +#endif + } xref2u1_t opts = &d[OFF_DAT_OPTS]; - int oidx = scan_mac_cmds(opts, olen); + int oidx = scan_mac_cmds(opts, olen, port); if( oidx != olen ) { EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, e_.eui = MAIN::CDEV->getEui(), @@ -1090,7 +1198,7 @@ static bit_t decodeFrame (void) { #if LMIC_DEBUG_LEVEL > 0 LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff); #endif - int optendindex = scan_mac_cmds(d+poff, pend-poff); + int optendindex = scan_mac_cmds(d+poff, pend-poff, port); if (optendindex != pend-poff) { #if LMIC_DEBUG_LEVEL > 0 LMIC_DEBUG_PRINTF( @@ -1522,41 +1630,26 @@ static void buildDataFrame (void) { // Piggyback MAC options // Prioritize by importance + // highest importance are the ones in the pendMac buffer. int end = OFF_DAT_OPTS; -#if !defined(DISABLE_PING) - if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) { - // Indicate pingability in every UP frame - LMIC.frame[end] = MCMD_PING_IND; - LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4); - end += 2; - } -#endif // !DISABLE_PING -#if !defined(DISABLE_MCMD_DCAP_REQ) - if( LMIC.dutyCapAns ) { - LMIC.frame[end] = MCMD_DCAP_ANS; - end += 1; - LMIC.dutyCapAns = 0; - } -#endif // !DISABLE_MCMD_DCAP_REQ - if( LMIC.devsAns ) { // answer to device status - LMIC.frame[end+0] = MCMD_DEVS_ANS; - LMIC.frame[end+1] = os_getBattLevel(); - LMIC.frame[end+2] = LMIC.devAnsMargin; - end += 3; - LMIC.devsAns = 0; - } - if( LMIC.ladrAns ) { // answer to ADR change - LMIC.frame[end+0] = MCMD_LADR_ANS; - LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU; - end += 2; - LMIC.ladrAns = 0; + + if (LMIC.pendMacLen > 0) { + os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); + end += LMIC.pendMacLen; + LMIC.pendMacLen = 0; } -#if !defined(DISABLE_BEACONS) - if( LMIC.bcninfoTries > 0 ) { - LMIC.frame[end] = MCMD_BCNI_REQ; - end += 1; +#if !defined(DISABLE_MCMD_RXParamSetupReq) + if (LMIC.dn2Ans) { + if (LMIC.dn2Ans & 0x40) { + LMIC.dn2Ans ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_RXParamSetupAns; + LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU; + end += 2; + } } -#endif // !DISABLE_BEACONS +#endif // !DISABLE_MCMD_RXParamSetupReq + if( LMIC.adrChanged ) { // if ADR is enabled, and we were just counting down the // transmits before starting an ADR, advance the timer so @@ -1565,37 +1658,6 @@ static void buildDataFrame (void) { setAdrAckCount(LINK_CHECK_CONT); LMIC.adrChanged = 0; } -#if !defined(DISABLE_MCMD_DN2P_SET) - if (LMIC.dn2Ans) { - LMIC.frame[end + 0] = MCMD_DN2P_ANS; - LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU; - end += 2; - LMIC.dn2Ans = 0; - } -#endif // !DISABLE_MCMD_DN2P_SET -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - if( LMIC.pingSetAns != 0 ) { - LMIC.frame[end+0] = MCMD_PING_ANS; - LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU; - end += 2; - LMIC.pingSetAns = 0; - } -#endif // !DISABLE_MCMD_PING_SET && !DISABLE_PING -#if !defined(DISABLE_MCMD_SNCH_REQ) - if( LMIC.snchAns ) { - LMIC.frame[end+0] = MCMD_SNCH_ANS; - LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU; - end += 2; - LMIC.snchAns = 0; - } -#endif // !DISABLE_MCMD_SNCH_REQ -#if LMIC_ENABLE_TxParamSetupReq - if ( LMIC.txParamSetupAns ) { - LMIC.frame[end+0] = MCMD_TxParamSetupAns; - end += 1; - LMIC.txParamSetupAns = 0; - } -#endif #if LMIC_ENABLE_DeviceTimeReq if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { LMIC.frame[end+0] = MCMD_DeviceTimeReq; @@ -1716,7 +1778,7 @@ bit_t LMIC_enableTracking (u1_t tryBcnInfo) { if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 ) return 0; // already in progress or failed to enable // If BCN info requested from NWK then app has to take are - // of sending data up so that MCMD_BCNI_REQ can be attached. + // of sending data up so that MCMD_BeaconTimingReq can be attached. if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) startScan(); return 1; // enabled @@ -1950,7 +2012,7 @@ static bit_t processDnData (void) { 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 this falls to zero the NWK did not answer our MCMD_BeaconTimingReq commands - try full scan if( LMIC.bcninfoTries > 0 ) { if( (LMIC.opmode & OP_TRACK) != 0 ) { reportEventNoUpdate(EV_BEACON_FOUND); // update? diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index b9764da4..f2442579 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -399,6 +399,7 @@ struct lmic_t { #if CFG_LMIC_EU_like band_t bands[MAX_BANDS]; u4_t channelFreq[MAX_CHANNELS]; + // bit map of enabled datarates for each channel u2_t channelDrMap[MAX_CHANNELS]; u2_t channelMap; #elif CFG_LMIC_US_like @@ -449,9 +450,13 @@ struct lmic_t { u1_t pendTxPort; u1_t pendTxConf; // confirmed data - u1_t pendTxLen; // +0x80 = confirmed + u1_t pendTxLen; // count of bytes in pendTxData. u1_t pendTxData[MAX_LEN_PAYLOAD]; + s1_t pendMacLen; // number of bytes of pending Mac response data; -1 + // implies port 0. + u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; + u1_t nwkKey[16]; // network session key u1_t artKey[16]; // application router session key @@ -461,19 +466,13 @@ struct lmic_t { u1_t rxDelay; // Rx delay after TX u1_t margin; - bit_t ladrAns; // link adr adapt answer pending - bit_t devsAns; // device status answer pending s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command u1_t adrEnabled; u1_t moreData; // NWK has more data pending -#if !defined(DISABLE_MCMD_DCAP_REQ) +#if !defined(DISABLE_MCMD_DutyCycleReq) bit_t dutyCapAns; // have to ACK duty cycle settings #endif -#if !defined(DISABLE_MCMD_SNCH_REQ) - u1_t snchAns; // answer set new channel -#endif #if LMIC_ENABLE_TxParamSetupReq - bit_t txParamSetupAns; // transmit setup answer pending. u1_t txParam; // the saved TX param byte. #endif #if LMIC_ENABLE_DeviceTimeReq @@ -486,7 +485,7 @@ struct lmic_t { // 2nd RX window (after up stream) u1_t dn2Dr; -#if !defined(DISABLE_MCMD_DN2P_SET) +#if !defined(DISABLE_MCMD_RXParamSetupReq) u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs #endif @@ -494,9 +493,6 @@ struct lmic_t { #if !defined(DISABLE_BEACONS) u1_t missedBcns; // unable to track last N beacons u1_t bcninfoTries; // how often to try (scan mode only) -#endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - u1_t pingSetAns; // answer set cmd and ACK bits #endif // Public part of MAC state u1_t txCnt; diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index 5050ff79..ad478dd4 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -110,7 +110,6 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { -10, // [5]: MaxEIRP - 10dB -12, // [6]: MaxEIRP - 12dB -14, // [7]: MaxEIRP - 14dB - 0, 0, 0, 0, 0, 0, 0, 0 }; // from LoRaWAN 5.8: mapping from txParam to MaxEIRP @@ -130,15 +129,20 @@ static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { } // translate from an encoded power to an actual power using -// the maxeirp setting. +// the maxeirp setting; return -128 if not legal. int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) { - s1_t const adj = - TABLE_GET_S1( - TXPOWLEVELS, - (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT - ); - - return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(TXPOWLEVELS)) { + s1_t const adj = + TABLE_GET_S1( + TXPOWLEVELS, + pindex + ); + + return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -217,6 +221,15 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { @@ -229,7 +242,10 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c index a6d0fdbc..8cfa3957 100644 --- a/src/lmic/lmic_au921.c +++ b/src/lmic/lmic_au921.c @@ -66,6 +66,13 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr) { return 0xFF; } +int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + return -128; + else + return ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); +} + static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { us2osticksRound(128 << 7), // DR_SF12 us2osticksRound(128 << 6), // DR_SF11 diff --git a/src/lmic/lmic_bandplan_au921.h b/src/lmic/lmic_bandplan_au921.h index 560a1f51..4c439ad5 100644 --- a/src/lmic/lmic_bandplan_au921.h +++ b/src/lmic/lmic_bandplan_au921.h @@ -40,7 +40,8 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr); #define maxFrameLen(dr) LMICau921_maxFrameLen(dr) -#define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))) +int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICau921_pow2dbm(mcmd_ladr_p1) ostime_t LMICau921_dr2hsym(uint8_t dr); #define dr2hsym(dr) LMICau921_dr2hsym(dr) diff --git a/src/lmic/lmic_bandplan_us915.h b/src/lmic/lmic_bandplan_us915.h index f6870959..28ca69bb 100644 --- a/src/lmic/lmic_bandplan_us915.h +++ b/src/lmic/lmic_bandplan_us915.h @@ -40,7 +40,8 @@ uint8_t LMICus915_maxFrameLen(uint8_t dr); #define maxFrameLen(dr) LMICus915_maxFrameLen(dr) -#define pow2dBm(mcmd_ladr_p1) ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))) +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1) ostime_t LMICus915_dr2hsym(uint8_t dr); #define dr2hsym(dr) LMICus915_dr2hsym(dr) diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index e47082b2..f7d81883 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -59,11 +59,16 @@ uint8_t LMICeu868_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 16, 14, 12, 10, 8, 6, 4, 2, 0,0,0,0, 0,0,0,0 + 16, 14, 12, 10, 8, 6, 4, 2 }; int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -130,6 +135,15 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { @@ -146,9 +160,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; - // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 1788ca4d..a4e840f4 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -59,11 +59,16 @@ uint8_t LMICin866_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 0, 0,0,0,0 + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 }; int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -125,17 +130,29 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { - freq |= BAND_MILLI; + freq = (freq&~3) | BAND_MILLI; } else { if (band > BAND_MILLI) return 0; freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(IN866_DR_SF12, IN866_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 468f9773..874b3626 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -57,11 +57,16 @@ uint8_t LMICkr920_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 14, 12, 10, 8, 6, 4, 2, 0, 0,0,0,0, 0,0,0,0 + 14, 12, 10, 8, 6, 4, 2, 0 }; int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -136,17 +141,29 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { - freq |= BAND_MILLI; + freq = (freq&~3) | BAND_MILLI; } else { if (band > BAND_MILLI) return 0; freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index 1d524d47..d911af2e 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -64,6 +64,13 @@ uint8_t LMICus915_maxFrameLen(uint8_t dr) { return 0xFF; } +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + return -128; + else + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); +} + static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { us2osticksRound(128 << 5), // DR_SF10 DR_SF12CR us2osticksRound(128 << 4), // DR_SF9 DR_SF11CR diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 7b1525a6..5a248904 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -485,6 +485,11 @@ enum { // In UP direction: signals class B enabled FCT_CLASSB = FCT_MORE }; + +enum { + LWAN_FCtrl_FOptsLen_MAX = 0x0Fu, // maximum size of embedded MAC commands +}; + enum { NWKID_MASK = (int)0xFE000000, NWKID_BITS = 7 @@ -493,65 +498,69 @@ enum { // MAC uplink commands downwlink too enum { // Class A - MCMD_LCHK_REQ = 0x02, // - LinkCheckReq : - - MCMD_LADR_ANS = 0x03, // - LinkADRAnd : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK - MCMD_DCAP_ANS = 0x04, // - DutyCycleAns : - - MCMD_DN2P_ANS = 0x05, // - RxParamSetupAns : u1:7-2:RFU 1/0:datarate/channel ack - MCMD_DEVS_ANS = 0x06, // - DevStatusAns : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31) - MCMD_SNCH_ANS = 0x07, // - NewChannelAns : u1: 7-2=RFU, 1/0:DR/freq ACK - MCMD_RXTimingSetupAns = 0x08, // : - - MCMD_TxParamSetupAns = 0x09, // : - - MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK - MCMD_DeviceTimeReq = 0x0D, + MCMD_LinkCheckReq = 0x02, // - + MCMD_LinkADRAns = 0x03, // u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK + MCMD_DutyCycleAns = 0x04, // - + MCMD_RXParamSetupAns = 0x05, // u1:7-2:RFU 1/0:datarate/channel ack + MCMD_DevStatusAns = 0x06, // u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31) + MCMD_NewChannelAns = 0x07, // u1: 7-2=RFU, 1/0:DR/freq ACK + MCMD_RXTimingSetupAns = 0x08, // - + MCMD_TxParamSetupAns = 0x09, // - + MCMD_DIChannelAns = 0x0A, // u1: [7-2]:RFU 1:exists 0:OK + MCMD_DeviceTimeReq = 0x0D, // - // Class B - MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate - MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok - MCMD_BCNI_REQ = 0x12, // - next beacon start : - + MCMD_PingSlotInfoReq = 0x10, // u1: 7=RFU, 6-4:interval, 3-0:datarate + MCMD_PingSlotChannelAns = 0x11, // u1: 7-1:RFU, 0:freq ok + MCMD_BeaconTimingReq = 0x12, // - (DEPRECATED) + MCMD_BeaconFreqAns = 0x13, // u1: 7-1:RFU, 0:freq ok }; // MAC downlink commands enum { // Class A - MCMD_LCHK_ANS = 0x02, // LinkCheckAns : u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq - MCMD_LADR_REQ = 0x03, // LinkADRReq : u1:DR/TXPow, u2:chmask, u1:chpage/repeat - MCMD_DCAP_REQ = 0x04, // DutyCycleReq : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k - MCMD_DN2P_SET = 0x05, // RXParamSetupReq : u1:7-4:RFU/3-0:datarate, u3:freq - MCMD_DEVS_REQ = 0x06, // DevStatusReq : - - MCMD_SNCH_REQ = 0x07, // NewChannelReq : u1:chidx, u3:freq, u1:DRrange - MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1) - MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP - MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency - MCMD_DeviceTimeAns = 0x0D, + MCMD_LinkCheckAns = 0x02, // u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq + MCMD_LinkADRReq = 0x03, // u1:DR/TXPow, u2:chmask, u1:chpage/repeat + MCMD_DutyCycleReq = 0x04, // u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k + MCMD_RXParamSetupReq = 0x05, // u1:7-4:RFU/3-0:datarate, u3:freq + MCMD_DevStatusReq = 0x06, // - + MCMD_NewChannelReq = 0x07, // u1:chidx, u3:freq, u1:DRrange + MCMD_RXTimingSetupReq = 0x08, // u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1) + MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP + MCMD_DIChannelReq = 0x0A, // u1: channel, u3: frequency + MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second // Class B - MCMD_PING_SET = 0x11, // set ping freq : u3: freq - MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel + MCMD_PingSlotInfoAns = 0x10, // - + MCMD_PingSlotChannelReq = 0x11, // u3: freq, u1:dr [7-4]:RFU [3:0]:datarate + MCMD_BeaconTimingAns = 0x12, // u2: delay(in TUNIT millis), u1:channel (DEPRECATED) + MCMD_BeaconFreqReq = 0x13, // u3: freq }; enum { - MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis + MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis }; enum { - MCMD_LADR_ANS_RFU = 0xF8, // RFU bits - MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level - MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled + MCMD_LinkADRAns_RFU = 0xF8, // RFU bits + MCMD_LinkADRAns_PowerACK = 0x04, // 0=not supported power level + MCMD_LinkADRAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_LinkADRAns_ChannelACK = 0x01, // 0=unknown channel enabled }; enum { - MCMD_DN2P_ANS_RFU = 0xF8, // RFU bits - MCMD_DN2P_ANS_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed - MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled + MCMD_RXParamSetupAns_RFU = 0xF8, // RFU bits + MCMD_RXParamSetupAns_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed + MCMD_RXParamSetupAns_RX2DataRateACK = 0x02, // 0=unknown data rate + MCMD_RXParamSetupAns_ChannelACK = 0x01, // 0=unknown channel enabled }; enum { - MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits - MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency + MCMD_NewChannelAns_RFU = 0xFC, // RFU bits + MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency }; enum { - MCMD_PING_ANS_RFU = 0xFE, - MCMD_PING_ANS_FQACK = 0x01 + MCMD_PingSlotFreqAns_RFU = 0xFC, + MCMD_PingSlotFreqAns_DataRateACK = 0x02, + MCMD_PingSlotFreqAns_ChannelACK = 0x01, }; enum { @@ -561,7 +570,7 @@ enum { MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level }; -// Bit fields byte#3 of MCMD_LADR_REQ payload +// Bit fields byte#3 of MCMD_LinkADRReq payload enum { MCMD_LADR_CHP_USLIKE_SPECIAL = 0x50, // first special for us-like MCMD_LADR_CHP_BANK = 0x50, // special: bits are banks. @@ -573,7 +582,7 @@ enum { MCMD_LADR_REPEAT_1 = 0x01, MCMD_LADR_CHPAGE_1 = 0x10 }; -// Bit fields byte#0 of MCMD_LADR_REQ payload +// Bit fields byte#0 of MCMD_LinkADRReq payload enum { MCMD_LADR_DR_MASK = 0xF0, MCMD_LADR_POW_MASK = 0x0F, From c8445a04e980f7c1ed87bc03da881afb4bd475b7 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 15 Aug 2019 04:02:54 -0400 Subject: [PATCH 02/27] Remove unused field --- src/lmic/lmic.c | 3 --- src/lmic/lmic.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index fd539b7c..17bd2ece 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -596,9 +596,6 @@ static void stateJustJoined (void) { LMIC.dn2Ans = 0; #endif LMIC.moreData = 0; -#if !defined(DISABLE_MCMD_DutyCycleReq) - LMIC.dutyCapAns = 0; -#endif LMIC.upRepeat = 0; resetJoinParams(); #if !defined(DISABLE_BEACONS) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index f2442579..dacab6f5 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -469,9 +469,6 @@ struct lmic_t { s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command u1_t adrEnabled; u1_t moreData; // NWK has more data pending -#if !defined(DISABLE_MCMD_DutyCycleReq) - bit_t dutyCapAns; // have to ACK duty cycle settings -#endif #if LMIC_ENABLE_TxParamSetupReq u1_t txParam; // the saved TX param byte. #endif From b24514e0c1f21c4dc485a790a6d8c3de1043f047 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 15 Aug 2019 04:03:44 -0400 Subject: [PATCH 03/27] Always reset LMIC.pendMacLen --- src/lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 17bd2ece..d624f479 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -1633,8 +1633,8 @@ static void buildDataFrame (void) { if (LMIC.pendMacLen > 0) { os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); end += LMIC.pendMacLen; - LMIC.pendMacLen = 0; } + LMIC.pendMacLen = 0; #if !defined(DISABLE_MCMD_RXParamSetupReq) if (LMIC.dn2Ans) { if (LMIC.dn2Ans & 0x40) { From 1cf5e1627e1b4f4d153227b390b240b8c4c581e9 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 16 Aug 2019 20:12:59 -0400 Subject: [PATCH 04/27] Passing all but sections 17.12~17.15 of RWC Korean Cert Test --- README.md | 3 +- .../compliance-otaa-halconfig.ino | 329 ++++++++++-------- src/lmic/config.h | 2 + src/lmic/lmic.c | 308 +++++++++++++--- src/lmic/lmic.h | 15 +- src/lmic/lmic_as923.c | 7 +- src/lmic/lmic_bandplan.h | 5 + src/lmic/lmic_eu868.c | 5 + src/lmic/lmic_eu_like.c | 7 + src/lmic/lmic_eu_like.h | 3 + src/lmic/lmic_in866.c | 5 + src/lmic/lmic_kr920.c | 5 + src/lmic/lorabase.h | 13 +- 13 files changed, 499 insertions(+), 208 deletions(-) diff --git a/README.md b/README.md index 324665dc..475dbe08 100644 --- a/README.md +++ b/README.md @@ -314,8 +314,7 @@ If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_star #### Disabling Class A MAC commands -`DISABLE_MCMD_DutyCycleReq`, `DISABLE_MCMD_RXParamSetupReq`, and `DISABLE_MCMD_NewChannelReq` respectively disable code for various Class A MAC -commands. +`DISABLE_MCMD_DutyCycleReq`, `DISABLE_MCMD_RXParamSetupReq`, `DISABLE_MCMD_RXTimingSetupReq`, `DISABLE_MCMD_NewChannelReq`, and `DISABLE_MCMD_DlChannelReq` respectively disable code for various Class A MAC commands. #### Disabling Class B MAC commands diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index 6428e368..c29f0096 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -95,6 +95,8 @@ public: struct eventnode_t { osjob_t job; ev_t event; + const char *pMessage; + uint32_t datum; ostime_t time; ostime_t txend; u4_t freq; @@ -117,7 +119,7 @@ public: return true; } - bool putEvent(ev_t event) { + bool putEvent(ev_t event, const char *pMessage = nullptr, uint32_t datum = 0) { auto i = m_tail + 1; if (i == sizeof(m_queue) / sizeof(m_queue[0])) { i = 0; @@ -127,6 +129,8 @@ public: pn->time = os_getTime(); pn->txend = LMIC.txend; pn->event = event; + pn->pMessage = pMessage; + pn->datum = datum; pn->freq = LMIC.freq; pn->txChnl = LMIC.txChnl; pn->rps = LMIC.rps; @@ -143,12 +147,27 @@ public: private: unsigned m_head, m_tail; - eventnode_t m_queue[16]; + eventnode_t m_queue[32]; osjob_t m_job; }; cEventQueue eventQueue; +extern "C" { + void ArduinoLMIC_putEvent(const char *pMessage); + void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum); +} + +void ArduinoLMIC_putEvent(const char *pMessage) + { + eventQueue.putEvent(ev_t(-1), pMessage); + } + +void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum) + { + eventQueue.putEvent(ev_t(-2), pMessage, datum); + } + uint8_t lastTxChannel; bool lastTxStart; @@ -218,154 +237,178 @@ void printRps(rps_t rps) { Serial.print(F(")")); } +void printOpmode(uint16_t opmode, char sep = ',') { + if (sep != 0) + Serial.print(sep); + Serial.print(F(" opmode=")); Serial.print(opmode, HEX); +} + +void printTxend(ostime_t txend) { + Serial.print(F(", txend=")); Serial.print(txend); +} + void eventPrint(cEventQueue::eventnode_t &e) { ev_t ev = e.event; Serial.print(e.time); - Serial.print(F(": ")); - if (ev < sizeof(evNames) / sizeof(evNames[0])) { - Serial.print(evNames[ev]); + Serial.print(F(" (")); + Serial.print(osticks2ms(e.time)); + Serial.print(F(" ms): ")); + + if (ev == ev_t(-1) || ev == ev_t(-2)) { + Serial.print(e.pMessage); + if (ev == ev_t(-2)) { + Serial.print(F(", datum=0x")); Serial.print(e.datum, HEX); + } + printOpmode(e.opmode, '.'); } else { - Serial.print(F("Unknown event: ")); - Serial.print((unsigned) ev); - } - - switch(ev) { - case EV_SCAN_TIMEOUT: - break; - case EV_BEACON_FOUND: - break; - case EV_BEACON_MISSED: - break; - case EV_BEACON_TRACKED: - break; - case EV_JOINING: - break; - - case EV_JOINED: - Serial.print(F(": ch ")); - Serial.println(unsigned(e.txChnl)); - { - u4_t netid = 0; - devaddr_t devaddr = 0; - u1_t nwkKey[16]; - u1_t artKey[16]; - LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); - Serial.print("netid: "); - Serial.println(netid, DEC); - Serial.print("devaddr: "); - Serial.println(devaddr, HEX); - Serial.print("artKey: "); - for (int i=0; i 0 ) { LMIC.globalDutyAvail = reftime; LMIC.opmode |= OP_RNDTX; @@ -591,9 +594,15 @@ static void resetJoinParams(void) { static void stateJustJoined (void) { LMIC.seqnoDn = LMIC.seqnoUp = 0; LMIC.rejoinCnt = 0; - LMIC.dnConf = LMIC.adrChanged = 0; + LMIC.dnConf = LMIC.lastDnConf = LMIC.adrChanged = 0; #if !defined(DISABLE_MCMD_RXParamSetupReq) LMIC.dn2Ans = 0; +#endif +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + LMIC.macRxTimingSetupAns = 0; +#endif +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + LMIC.macDlChannelAns = 0; #endif LMIC.moreData = 0; LMIC.upRepeat = 0; @@ -644,21 +653,21 @@ static int decodeBeacon (void) { } #endif // !DISABLE_BEACONS -// put a mac response to the current output buffer. +// put a mac response to the current output buffer. Limit according to kind of +// mac data (piggyback vs port 0) static bit_t put_mac_uplink_byte(uint8_t b) { - uint8_t *pEnd; - uint8_t *pBuffer; - - if (LMIC.pendMacLen < 0) { - if (LMIC.pendTxLen < sizeof(LMIC.pendTxData)) { - LMIC.pendTxData[LMIC.pendTxLen++] = b; + if (LMIC.pendMacPiggyback) { + // put in pendMacData + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; return 1; } else { return 0; } } else { - if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { - LMIC.pendMacData[LMIC.pendMacLen++] = b; + // put in pendTxData + if (LMIC.pendMacLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendMacLen++] = b; return 1; } else { return 0; @@ -667,11 +676,25 @@ static bit_t put_mac_uplink_byte(uint8_t b) { } static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t b2) { - return put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2); + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } } static bit_t put_mac_uplink_byte3(u1_t b1, u1_t b2, u1_t b3) { - return put_mac_uplink_byte(b1) && put_mac_uplink_byte2(b2, b3); + u1_t outindex = LMIC.pendMacLen; + + if (put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2) && put_mac_uplink_byte(b3)) { + return 1; + } else { + LMIC.pendMacLen = outindex; + return 0; + } } static CONST_TABLE(u1_t, macCmdSize)[] = { @@ -701,7 +724,7 @@ static u1_t getMacCmdSize(u1_t macCmd) { return TABLE_GET_U1(macCmdSize, macCmd - 2); } -static void +static bit_t applyAdrRequests( const uint8_t *opts, int olen, @@ -712,10 +735,11 @@ applyAdrRequests( int oidx; u1_t p1 = 0; u1_t p4 = 0; + bit_t response_fit = 1; LMICbandplan_saveAdrState(&initialState); - for (oidx = 0; oidx < olen; ) { + for (oidx = 0; oidx < olen && response_fit; ) { if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels @@ -727,7 +751,7 @@ applyAdrRequests( } oidx += kAdrReqSize; - put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); + response_fit = put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); } // all done scanning options @@ -748,12 +772,14 @@ applyAdrRequests( } LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + return response_fit; } - -int scan_mac_cmds_link_adr( +static int +scan_mac_cmds_link_adr( const char *opts, - int olen + int olen, + bit_t *presponse_fit ) { if (olen == 0) @@ -796,14 +822,14 @@ int scan_mac_cmds_link_adr( // go back and apply the ADR changes, if any -- use the effective length, // and process. - applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); + *presponse_fit = applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); return lastOidx; } // scan mac commands starting at opts[] for olen, return count of bytes consumed. -// build response either in pendMacData[] or pendTxData[], depending on whether the -// pointer is before +// build response in pendMacData[], but limit length as needed; simply chop at last +// response that fits. static int scan_mac_cmds( const uint8_t *opts, @@ -814,12 +840,16 @@ scan_mac_cmds( bit_t fSawAdrReq = 0; uint8_t cmd; + LMIC.pendMacLen = 0; if (port == 0) - LMIC.pendMacLen = -1; + LMIC.pendMacPiggyback = 0; else - LMIC.pendMacLen = 0; + LMIC.pendMacPiggyback = 1; while( oidx < olen ) { + bit_t response_fit; + + response_fit = 1; cmd = opts[oidx]; switch( cmd ) { case MCMD_LinkCheckAns: { @@ -839,7 +869,7 @@ scan_mac_cmds( // So we need to process all the contigious commands case MCMD_LinkADRReq: { // skip over all but the last command. - oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx); + oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx, &response_fit); break; } @@ -849,7 +879,7 @@ scan_mac_cmds( // per [1.02] 5.5. the margin is the SNR. LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr)); - put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); + response_fit = put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); break; } @@ -866,7 +896,7 @@ scan_mac_cmds( if (rx1DrOffset <= 3) LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX1DrOffsetAck; - if( LMIC.dn2Ans == (0x80|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { + if( LMIC.dn2Ans == (0xC0|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { LMIC.dn2Dr = dr; LMIC.dn2Freq = freq; LMIC.rx1DrOffset = rx1DrOffset; @@ -875,11 +905,23 @@ scan_mac_cmds( } /* put the first copy of the message */ - put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); + response_fit = put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); #endif // !DISABLE_MCMD_RXParamSetupReq break; } + case MCMD_RXTimingSetupReq: { +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + u1_t delay = opts[oidx+1] & MCMD_RXTimingSetupReq_Delay; + if (delay == 0) + delay = 1; + + LMIC.rxDelay = delay; + LMIC.macRxTimingSetupAns = 2; + response_fit = put_mac_uplink_byte(MCMD_RXTimingSetupAns); +#endif // !DISABLE_MCMD_RXTimingSetupReq + break; + } case MCMD_DutyCycleReq: { #if !defined(DISABLE_MCMD_DutyCycleReq) u1_t cap = opts[oidx+1]; @@ -890,45 +932,75 @@ scan_mac_cmds( LMIC.globalDutyAvail = os_getTime(); DO_DEVDB(cap,dutyCap); - put_mac_uplink_byte(MCMD_DutyCycleAns); + response_fit = put_mac_uplink_byte(MCMD_DutyCycleAns); #endif // !DISABLE_MCMD_DutyCycleReq break; } case MCMD_NewChannelReq: { -#if !defined(DISABLE_MCMD_NewChannelReq) +#if !defined(DISABLE_MCMD_NewChannelReq) && CFG_LMIC_EU_like u1_t chidx = opts[oidx+1]; // channel - u4_t raw_feq0 = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; + u4_t raw_f_not_zero = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq u1_t drs = opts[oidx+5]; // datarate span u1_t ans = MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK; - if (freq == 0 && raw_feq0 != 0) { + if (freq == 0 && raw_f_not_zero) { ans &= ~MCMD_NewChannelAns_ChannelACK; } u1_t MaxDR = drs >> 4; u1_t MinDR = drs & 0xF; - if (MaxDR < MinDR || !validDR(MaxDR) || validDR(MinDR)) + if (MaxDR < MinDR || !validDR(MaxDR) || !validDR(MinDR)) { ans &= ~MCMD_NewChannelAns_DataRateACK; + } if( ans == MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK) { if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { + ArduinoLMIC_putEventDatum("NewChannelReq: setupChannel failed", (MaxDR << 24u) | (MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); ans &= ~MCMD_NewChannelAns_ChannelACK; } } - put_mac_uplink_byte2(MCMD_NewChannelAns, ans); + response_fit = put_mac_uplink_byte2(MCMD_NewChannelAns, ans); #endif // !DISABLE_MCMD_NewChannelReq break; } + case MCMD_DlChannelReq: { +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + u1_t chidx = opts[oidx+1]; // channel + u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq + u1_t ans = MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK; + + if (freq == 0) { + ans &= ~MCMD_DlChannelAns_ChannelACK; + } + if (chidx > MAX_CHANNELS) { + // this is not defined by the 1.0.3 spec + ans = 0; + } else if ((LMIC.channelMap & (1 << chidx)) == 0) { + // the channel is not enabled for downlink. + ans &= ~MCMD_DlChannelAns_FreqACK; + } + + if( ans == MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK) { + LMIC.channelDlFreq[chidx] = freq; + } + + response_fit = put_mac_uplink_byte2(MCMD_DlChannelAns, ans); + // set sticky answer. + LMIC.macDlChannelAns = ans | 0xC0; +#endif // !DISABLE_MCMD_DlChannelReq + break; + } + case MCMD_PingSlotChannelReq: { #if !defined(DISABLE_MCMD_PingSlotChannelReq) && !defined(DISABLE_PING) - u4_t raw_feq0 = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; + u4_t raw_f_not_zero = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); u1_t dr = opts[oidx+4] & 0xF; u1_t ans = MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK; - if (raw_feq0) { + if (! raw_f_not_zero) { freq = FREQ_PING; } else if (freq == 0) { ans &= ~MCMD_PingSlotFreqAns_ChannelACK; @@ -943,7 +1015,7 @@ scan_mac_cmds( DO_DEVDB(LMIC.ping.freq, pingFreq); DO_DEVDB(LMIC.ping.dr, pingDr); } - put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); + response_fit = put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); #endif // !DISABLE_MCMD_PingSlotChannelReq && !DISABLE_PING break; } @@ -987,7 +1059,7 @@ scan_mac_cmds( MCMD_TxParam_TxDWELL_MASK| MCMD_TxParam_MaxEIRP_MASK); LMIC.txParam = txParam; - put_mac_uplink_byte(MCMD_TxParamSetupAns); + response_fit = put_mac_uplink_byte(MCMD_TxParamSetupAns); #endif // LMIC_ENABLE_TxParamSetupReq break; } /* end case */ @@ -1037,6 +1109,8 @@ scan_mac_cmds( // "the first unknown command terminates processing" // force olen to current oidx so we'll exit the while(). olen = oidx; + } else if (! response_fit) { + olen = oidx; } oidx += cmdlen; @@ -1060,6 +1134,8 @@ static bit_t decodeFrame (void) { #if LMIC_DEBUG_LEVEL > 0 const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); #endif + if (dlen > 0) + ArduinoLMIC_putEventDatum("decodeFrame", (dlen << 8) | (hdr << 0)); if( dlen < OFF_DAT_OPTS+4 || (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || @@ -1087,6 +1163,8 @@ static bit_t decodeFrame (void) { int pend = dlen-4; // MIC if( addr != LMIC.devaddr ) { + ArduinoLMIC_putEventDatum("decodeFrame: wrong address", addr); + EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, e_.eui = MAIN::CDEV->getEui(), e_.info = addr, @@ -1094,6 +1172,7 @@ static bit_t decodeFrame (void) { goto norx; } if( poff > pend ) { + ArduinoLMIC_putEventDatum("decodeFrame: corrupted frame", (dlen << 16) | (fct << 8) | (poff - pend)); EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, e_.eui = MAIN::CDEV->getEui(), e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16))); @@ -1106,9 +1185,21 @@ static bit_t decodeFrame (void) { if( pend > poff ) port = d[poff++]; - seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn); + // compute the 32-bit sequence number based on the 16-bit sequence number received + // and the internal 32-bit number. Because the 32-bit number is used in the MIC + // calculation, this must be right. (And if you're curious why a 32-bit seqno matters, + // it's this calculation, plus its use in the MIC calculation.) + // + // we have to be careful to get the right value for replay of last message received. + u2_t seqnoDiff = (u2_t)(seqno - LMIC.seqnoDn); + if (seqnoDiff == 0xFFFFu) { + seqno = LMIC.seqnoDn - 1; + } else { + seqno = LMIC.seqnoDn + seqnoDiff; + } if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { + ArduinoLMIC_putEventDatum("decodeFrame: bad MIC", seqno); EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, e_.eui1 = MAIN::CDEV->getEui(), e_.info1 = Base::lsbf4(&d[pend]), @@ -1122,20 +1213,29 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoDn, e_.info2 = seqno)); + ArduinoLMIC_putEventDatum("decodeFrame: rollover discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); goto norx; } - if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) { + if( seqno != LMIC.seqnoDn-1 || !LMIC.lastDnConf || ftype != HDR_FTYPE_DCDN ) { EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE, e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoDn, e_.info2 = seqno)); + ArduinoLMIC_putEventDatum("decodeFrame: Retransmit confimed discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); goto norx; } // Replay of previous sequence number allowed only if // previous frame and repeated both requested confirmation + // but set a flag, so we don't actually process the message. + ArduinoLMIC_putEventDatum("decodeFrame: Retransmit confimed accepted", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); replayConf = 1; + LMIC.dnConf = FCT_ACK; } else { + if( seqnoDiff > LMICbandplan_MAX_FCNT_GAP) { + ArduinoLMIC_putEventDatum("decodeFrame: gap too big", (seqnoDiff << 16) | (seqno & 0xFFFFu)); + goto norx; + } if( seqno > LMIC.seqnoDn ) { EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP, e_.eui = MAIN::CDEV->getEui(), @@ -1145,7 +1245,18 @@ static bit_t decodeFrame (void) { LMIC.seqnoDn = seqno+1; // next number to be expected DO_DEVDB(LMIC.seqnoDn,seqnoDn); // DN frame requested confirmation - provide ACK once with next UP frame - LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0); + LMIC.dnConf = LMIC.lastDnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0); + if (LMIC.dnConf) + ArduinoLMIC_putEventDatum("decodeFrame: Confirmed downlink", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + } + + if (port == 0 && olen != 0 && pend > poff) { + // we have a port-zero message, and piggyback mac data. + // discard, section 4.3.1.6 line 544-546 +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); +#endif + goto norx; } if( LMIC.dnConf || (fct & FCT_MORE) ) @@ -1155,27 +1266,23 @@ static bit_t decodeFrame (void) { LMIC.adrChanged = LMIC.rejoinCnt = 0; setAdrAckCount(LINK_CHECK_INIT); #if !defined(DISABLE_MCMD_RXParamSetupReq) + // We heard from network "on a Class A downlink" LMIC.dn2Ans = 0; #endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // We heard from network "on a Class A downlink" + LMIC.macRxTimingSetupAns = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + LMIC.macDlChannelAns = 0; +#endif 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. // it's only computed for legacy clients LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; - if (port == 0 && olen != 0) { -#if LMIC_DEBUG_LEVEL > 0 - // discard, section 4.3.1.6 line 544-546 - LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); -#endif - goto norx; - } else { -#if LMIC_DEBUG_LEVEL > 0 - // Process OPTS - LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen); -#endif - } - + // even if it's a replay confirmed, we process the mac options. xref2u1_t opts = &d[OFF_DAT_OPTS]; int oidx = scan_mac_cmds(opts, olen, port); if( oidx != olen ) { @@ -1205,6 +1312,7 @@ static bit_t decodeFrame (void) { ); #endif } + // wait to transmit until txcomplete: above. } } // end decrypt payload EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), @@ -1223,6 +1331,9 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = Base::lsbf4(&d[pend]), e_.info2 = seqno)); + // discard the data + ArduinoLMIC_putEventDatum("decodeFrame: discarding replay", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; } if( // NWK acks but we don't have a frame pending @@ -1236,7 +1347,7 @@ static bit_t decodeFrame (void) { e_.info = seqno, e_.info2 = ackup)); #if LMIC_DEBUG_LEVEL > 1 - LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt); + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt); #endif } @@ -1331,8 +1442,10 @@ static void txDone (ostime_t delay, osjobcb_t func) { } #endif // !DISABLE_PING - // Change RX frequency / rps (US only) before we increment txChnl + // Change RX frequency (can happen even for EU-like if programmed by DlChannelReq) + // change params and rps (US only) before we increment txChnl LMICbandplan_setRx1Params(); + // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.]) // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune // into the middle of the 8 symbols preamble. @@ -1440,6 +1553,7 @@ static bit_t processJoinAccept (void) { #if CFG_region != LMIC_REGION_as923 // TODO(tmm@mcci.com) regionalize // Lower DR every try below current UP DR + // need to check feasibility? join feasability is default. LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt); #else // in the join of AS923 v1.1 or older, only DR2 (SF10) is used. @@ -1630,12 +1744,15 @@ static void buildDataFrame (void) { // highest importance are the ones in the pendMac buffer. int end = OFF_DAT_OPTS; - if (LMIC.pendMacLen > 0) { + if (LMIC.pendTxPort != 0 && LMIC.pendMacPiggyback && LMIC.pendMacLen != 0) { os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); end += LMIC.pendMacLen; } LMIC.pendMacLen = 0; + LMIC.pendMacPiggyback = 0; + #if !defined(DISABLE_MCMD_RXParamSetupReq) + // per 5.4, RxParamSetupAns is sticky. if (LMIC.dn2Ans) { if (LMIC.dn2Ans & 0x40) { LMIC.dn2Ans ^= 0x40; @@ -1646,6 +1763,26 @@ static void buildDataFrame (void) { } } #endif // !DISABLE_MCMD_RXParamSetupReq +#if !defined(DISABLE_MCMD_DlChannelReq) + // per 5.4, DlChannelAns is sticky. + if (LMIC.macDlChannelAns) { + if (LMIC.macDlChannelAns & 0x40) { + LMIC.macDlChannelAns ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_DlChannelAns; + LMIC.frame[end + 1] = LMIC.macDlChannelAns & ~MCMD_DlChannelAns_RFU; + end += 2; + } + } +#endif // !DISABLE_MCMD_DlChannelReq +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + // per 5.7, RXTimingSetupAns is sticky + if (LMIC.macRxTimingSetupAns == 2) { + LMIC.macRxTimingSetupAns = 1; + } else if (LMIC.macRxTimingSetupAns) { + LMIC.frame[end++] = MCMD_RXTimingSetupAns; + } +#endif // !DISABLE_MCMD_RXTimingSetupReq) if( LMIC.adrChanged ) { // if ADR is enabled, and we were just counting down the @@ -1929,15 +2066,26 @@ static void processPingRx (xref2osjob_t osjob) { } #endif // !DISABLE_PING +// process downlink data at close of RX window. Return zero if another RX window +// should be scheduled, non-zero to prevent scheduling of RX2 (if relevant). +// Confusingly, the caller actualyl does some of the calculation, so the answer from +// us is not always totaly right; the rx1 window check ignores our result unless +// LMIC.datalen was non zero before calling. +// +// Inputs: +// LMIC.dataLen number of bytes receieved; 0 --> no message at all received. static bit_t processDnData (void) { ASSERT((LMIC.opmode & OP_TXRXPEND)!=0); if( LMIC.dataLen == 0 ) { + // if this is an RX1 window, shouldn't we return 0 to schedule + // RX2? norx: if( LMIC.txCnt != 0 ) { if( LMIC.txCnt < TXCONF_ATTEMPTS ) { LMIC.txCnt += 1; + // TODO(tmm@mcci.com): check feasibility of lower datarate setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, TABLE_GET_U1(DRADJUST, LMIC.txCnt)), KEEP_TXPOW); // Schedule another retransmission txDelay(LMIC.rxtime, RETRY_PERIOD_secs); @@ -1945,6 +2093,7 @@ static bit_t processDnData (void) { engineUpdate(); return 1; } + // confirmed uplink is complete without an ack: no port and no flag initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); } else { // Nothing received - implies no port @@ -1952,10 +2101,42 @@ static bit_t processDnData (void) { } setAdrAckCount(LMIC.adrAckReq + 1); LMIC.dataBeg = LMIC.dataLen = 0; + + // the transmission that got us here is complete. txcomplete: LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + // if there's pending mac data that's not piggyback, launch it now. + if (LMIC.pendMacLen != 0) { + if (LMIC.pendMacPiggyback) { + ArduinoLMIC_putEvent("piggyback mac message"); + LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. + } else { + // Every mac command on port 0 requires an uplink, if there's data. + // TODO(tmm@mcci.com) -- this is why we need a queueing structure for + // uplinks. + // open code the logic to build this because we don't want to call + // engineUpdate right now. Data is already in the uplink buffer. + LMIC.pendTxConf = 0; // not confirmed + LMIC.pendTxPort = 0; // port 0 + LMIC.pendTxLen = LMIC.pendMacLen; + LMIC.pendMacLen = 0; // discard mac data! + LMIC.opmode |= OP_TXDATA; + ArduinoLMIC_putEvent("port0 mac message"); + } + } + + // half-duplex gateways can have appreciable turn-around times, + // so we force a wait. It might be nice to randomize this a little, + // so that armies of identical devices will not try to talk all + // at once. + txDelay(ms2osticks(500), 0); + #if LMIC_ENABLE_DeviceTimeReq + // + // if the DeviceTimeReq FSM is active, we need to move it to idle, + // completing the callback. + // lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState; if ( requestTimeState != lmic_RequestTimeState_idle ) { lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.client.pNetworkTimeCb; @@ -1986,6 +2167,9 @@ static bit_t processDnData (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.adrAckReq)); dr_t newDr = decDR((dr_t)LMIC.datarate); + // TODO(tmm@mcci.com) newDr must be feasible; there must be at least + // one channel that supports the new datarate. If not, stay + // at current 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. @@ -2022,6 +2206,8 @@ static bit_t processDnData (void) { #endif // !DISABLE_BEACONS return 1; } + // if we get here, LMIC.dataLen != 0, so there is some + // traffic. if( !decodeFrame() ) { if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) return 0; @@ -2187,6 +2373,7 @@ static void engineUpdate (void) { if( (LMIC.opmode & OP_REJOIN) != 0 ) { #if CFG_region != LMIC_REGION_as923 // in AS923 v1.1 or older, no need to change the datarate. + // otherwise we need to check feasibility. txdr = lowerDR(txdr, LMIC.rejoinCnt); #endif } @@ -2381,6 +2568,7 @@ void LMIC_clrTxData (void) { void LMIC_setTxData (void) { + ArduinoLMIC_putEventDatum("LMIC_setTxData", (LMIC.pendTxPort << 24u) | (LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); LMIC.opmode |= OP_TXDATA; if( (LMIC.opmode & OP_JOINING) == 0 ) LMIC.txCnt = 0; // cancel any ongoing TX/RX retries @@ -2390,6 +2578,10 @@ 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 ( LMIC.opmode & OP_TXDATA ) { + // already have a message queued + return -1; + } if( dlen > SIZEOFEXPR(LMIC.pendTxData) ) return -2; if( data != (xref2u1_t)0 ) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index dacab6f5..fd5dc554 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -399,6 +399,9 @@ struct lmic_t { #if CFG_LMIC_EU_like band_t bands[MAX_BANDS]; u4_t channelFreq[MAX_CHANNELS]; +#if !defined(DISABLE_MCMD_DlChannelReq) + u4_t channelDlFreq[MAX_CHANNELS]; +#endif // bit map of enabled datarates for each channel u2_t channelDrMap[MAX_CHANNELS]; u2_t channelMap; @@ -453,14 +456,16 @@ struct lmic_t { u1_t pendTxLen; // count of bytes in pendTxData. u1_t pendTxData[MAX_LEN_PAYLOAD]; - s1_t pendMacLen; // number of bytes of pending Mac response data; -1 - // implies port 0. + u1_t pendMacLen; // number of bytes of pending Mac response data + bit_t pendMacPiggyback; // received on port 0 or piggyback? + // response data if piggybacked u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; u1_t nwkKey[16]; // network session key u1_t artKey[16]; // application router session key u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 + u1_t lastDnConf; // downlink with seqnoDn-1 requested confirmation u1_t adrChanged; u1_t rxDelay; // Rx delay after TX @@ -485,6 +490,12 @@ struct lmic_t { #if !defined(DISABLE_MCMD_RXParamSetupReq) u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs #endif +#if !defined(DISABLE_MCMD_DlChannelReq) + u1_t macDlChannelAns; // 0 ==> no answer pending, 0x80+ACK bits +#endif +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + bit_t macRxTimingSetupAns; // 0 ==> no answer pend, non-zero inserts response. +#endif // Class B state #if !defined(DISABLE_BEACONS) diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index ad478dd4..c6b87078 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -175,6 +175,9 @@ void LMICas923_initDefaultChannels(bit_t join) { LMIC_API_PARAMETER(join); os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); @@ -275,7 +278,9 @@ void LMICas923_setRx1Params(void) { int const txdr = LMIC.dndr; int effective_rx1DrOffset; int candidateDr; - + + LMICeulike_setRx1Freq(); + effective_rx1DrOffset = LMIC.rx1DrOffset; // per section 2.7.7 of regional, lines 1101:1103: switch (effective_rx1DrOffset) { diff --git a/src/lmic/lmic_bandplan.h b/src/lmic/lmic_bandplan.h index 63836c5b..f56257a8 100644 --- a/src/lmic/lmic_bandplan.h +++ b/src/lmic/lmic_bandplan.h @@ -169,6 +169,11 @@ #define PRERX_FSK 1 #define RXLEN_FSK (1+5+2) +// this is regional, but so far all regions are the same +#if !defined(LMICbandplan_MAX_FCNT_GAP) +# define LMICbandplan_MAX_FCNT_GAP 16384 +#endif // !defined LWAN_MAX_FCNT_GAP + #define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) #define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) #define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index f7d81883..beb5a20f 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -98,6 +98,9 @@ static CONST_TABLE(u4_t, iniChannelFreq)[6] = { void LMICeu868_initDefaultChannels(bit_t join) { os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); @@ -243,6 +246,8 @@ void LMICeu868_setRx1Params(void) { s1_t drOffset; s1_t candidateDr; + LMICeulike_setRx1Freq(); + if ( LMIC.rx1DrOffset <= 5) drOffset = (s1_t) LMIC.rx1DrOffset; else diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c index 2f510694..5f6974c6 100644 --- a/src/lmic/lmic_eu_like.c +++ b/src/lmic/lmic_eu_like.c @@ -184,4 +184,11 @@ bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { return pStateBuffer->channelMap != LMIC.channelMap; } +void LMICeulike_setRx1Freq(void) { +#if !defined(DISABLE_MCMD_DlChannelReq) + uint32_t dlFreq = LMIC.channelDlFreq[LMIC.txChnl]; + if (dlFreq != 0) + LMIC.freq = dlFreq; +#endif // !DISABLE_MCMD_DlChannelReq +} #endif // CFG_LMIC_EU_like diff --git a/src/lmic/lmic_eu_like.h b/src/lmic/lmic_eu_like.h index 0f51c855..8d869734 100644 --- a/src/lmic/lmic_eu_like.h +++ b/src/lmic/lmic_eu_like.h @@ -101,4 +101,7 @@ void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); #define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState) +// set Rx1 frequency (might be different than uplink). +void LMICeulike_setRx1Freq(void); + #endif // _lmic_eu_like_h_ diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index a4e840f4..a11906bb 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -103,6 +103,9 @@ void LMICin866_initDefaultChannels(bit_t join) { LMIC_API_PARAMETER(join); os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); @@ -218,6 +221,8 @@ void LMICin866_setRx1Params(void) { s1_t drOffset; s1_t candidateDr; + LMICeulike_setRx1Freq(); + if ( LMIC.rx1DrOffset <= 5) drOffset = (s1_t) LMIC.rx1DrOffset; else diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 874b3626..60ee6965 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -99,6 +99,9 @@ void LMICkr920_initDefaultChannels(bit_t join) { LMIC_API_PARAMETER(join); os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); +#if !defined(DISABLE_MCMD_DlChannelReq) + os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq)); +#endif // !DISABLE_MCMD_DlChannelReq os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); @@ -229,6 +232,8 @@ void LMICkr920_setRx1Params(void) { s1_t drOffset; s1_t candidateDr; + LMICeulike_setRx1Freq(); + if ( LMIC.rx1DrOffset <= 5) drOffset = (s1_t) LMIC.rx1DrOffset; else diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 5a248904..991ffaa5 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -506,7 +506,7 @@ enum { MCMD_NewChannelAns = 0x07, // u1: 7-2=RFU, 1/0:DR/freq ACK MCMD_RXTimingSetupAns = 0x08, // - MCMD_TxParamSetupAns = 0x09, // - - MCMD_DIChannelAns = 0x0A, // u1: [7-2]:RFU 1:exists 0:OK + MCMD_DlChannelAns = 0x0A, // u1: [7-2]:RFU 1:exists 0:OK MCMD_DeviceTimeReq = 0x0D, // - // Class B @@ -527,7 +527,7 @@ enum { MCMD_NewChannelReq = 0x07, // u1:chidx, u3:freq, u1:DRrange MCMD_RXTimingSetupReq = 0x08, // u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1) MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP - MCMD_DIChannelReq = 0x0A, // u1: channel, u3: frequency + MCMD_DlChannelReq = 0x0A, // u1: channel, u3: frequency MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second // Class B @@ -557,6 +557,15 @@ enum { MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency }; +enum { + MCMD_RXTimingSetupReq_RFU = 0xF0, // RFU bits + MCMD_RXTimingSetupReq_Delay = 0x0F, // delay in secs, 1..15; 0 is mapped to 1. +}; +enum { + MCMD_DlChannelAns_RFU = 0xFC, // RFU bits + MCMD_DlChannelAns_FreqACK = 0x02, // 0 = uplink frequency not defined for this channel + MCMD_DlChannelAns_ChannelACK = 0x01, // 0 = rejected channel freq +}; enum { MCMD_PingSlotFreqAns_RFU = 0xFC, MCMD_PingSlotFreqAns_DataRateACK = 0x02, From ebe396a2d6d6122f1effa505773be1996fefd6bf Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 16 Aug 2019 23:07:53 -0400 Subject: [PATCH 05/27] Clean up LinkADRReq processing --- src/lmic/lmic.c | 32 +++++++++++++----- src/lmic/lmic_as923.c | 42 +++++++++++------------ src/lmic/lmic_au921.c | 4 +-- src/lmic/lmic_bandplan.h | 5 +++ src/lmic/lmic_eu868.c | 2 +- src/lmic/lmic_eu_like.c | 56 ++++++++++++++++++++++++------ src/lmic/lmic_in866.c | 2 +- src/lmic/lmic_kr920.c | 2 +- src/lmic/lmic_us915.c | 4 +-- src/lmic/lmic_us_like.c | 29 ++++++++-------- src/lmic/lorabase.h | 73 +++++++++++----------------------------- 11 files changed, 136 insertions(+), 115 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index bb56752a..119e82f8 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -38,8 +38,19 @@ DEFINE_LMIC; +#define LMIC_ENABLE_TRACE 1 + +#ifndef LMIC_ENABLE_TRACE +# define LMIC_ENABLE_TRACE 0 +#endif + +#if LMIC_ENABLE_TRACE extern void ArduinoLMIC_putEvent(const char *pMessage); extern void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum); +#else +# define ArduinoLMIC_putEvent(m) do { ; } while (0) +# define ArduinoLMIC_putEventDatum(m, d) do { ; } while (0) +#endif // Fwd decls. static void reportEventNoUpdate(ev_t); @@ -53,6 +64,7 @@ static bit_t processJoinAccept_nojoinframe(void); static void startScan (void); #endif +// set the txrxFlags, with debugging static inline void initTxrxFlags(const char *func, u1_t mask) { LMIC_DEBUG2_PARAMETER(func); @@ -62,6 +74,7 @@ static inline void initTxrxFlags(const char *func, u1_t mask) { LMIC.txrxFlags = mask; } +// or the txrxFlags, with debugging static inline void orTxrxFlags(const char *func, u1_t mask) { initTxrxFlags(func, LMIC.txrxFlags | mask); } @@ -745,7 +758,7 @@ applyAdrRequests( p1 = opts[oidx+1]; // txpow + DR, in case last p4 = opts[oidx+4]; // ChMaskCtl, NbTrans - u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK; // channel page + u1_t chpage = p4 & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page LMICbandplan_mapChannels(chpage, chmap); } @@ -758,14 +771,14 @@ applyAdrRequests( bit_t changes = LMICbandplan_compareAdrState(&initialState); // handle uplink repeat count - u1_t uprpt = p4 & MCMD_LADR_REPEAT_MASK; // up repeat count + u1_t uprpt = p4 & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count if (LMIC.upRepeat != uprpt) { LMIC.upRepeat = uprpt; changes = 1; } if (adrAns & MCMD_LinkADRAns_DataRateACK) { - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); // handle power changes here, too. changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); @@ -801,9 +814,9 @@ scan_mac_cmds_link_adr( } 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 - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); + u1_t chpage = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page + // u1_t uprpt = opts[oidx+4] & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); if( !LMICbandplan_canMapChannels(chpage, chmap) ) adrAns &= ~MCMD_LinkADRAns_ChannelACK; @@ -2126,11 +2139,12 @@ static bit_t processDnData (void) { } } - // half-duplex gateways can have appreciable turn-around times, + // Half-duplex gateways can have appreciable turn-around times, // so we force a wait. It might be nice to randomize this a little, // so that armies of identical devices will not try to talk all - // at once. - txDelay(ms2osticks(500), 0); + // at once. This is potentially band-specific, so we let it come + // from the band-plan files. + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); #if LMIC_ENABLE_DeviceTimeReq // diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index c6b87078..2005d8b3 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -51,7 +51,7 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { }; // see table in 2.7.6 -- this assumes UplinkDwellTime = 0. -static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { 59+5, // [0] 59+5, // [1] 59+5, // [2] @@ -63,7 +63,7 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { }; // see table in 2.7.6 -- this assumes UplinkDwellTime = 1. -static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { 0, // [0] 0, // [1] 19+5, // [2] @@ -74,18 +74,18 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { 250+5 // [7] }; -static uint8_t +static uint8_t LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) { LMIC_API_PARAMETER(mcmd_txparam); - return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; + return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; } -static uint8_t +static uint8_t LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) { LMIC_API_PARAMETER(mcmd_txparam); - return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0; + return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0; } uint8_t LMICas923_maxFrameLen(uint8_t dr) { @@ -126,19 +126,19 @@ static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> MCMD_TxParam_MaxEIRP_SHIFT ); -} +} -// translate from an encoded power to an actual power using +// translate from an encoded power to an actual power using // the maxeirp setting; return -128 if not legal. int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) { - uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; - if (pindex < sizeof(TXPOWLEVELS)) { - s1_t const adj = + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + s1_t const adj = TABLE_GET_S1( - TXPOWLEVELS, + TXPOWLEVELS, pindex ); - + return LMICas923_getMaxEIRP(LMIC.txParam) + adj; } else { return -128; @@ -166,7 +166,7 @@ ostime_t LMICas923_dr2hsym(uint8_t dr) { enum { NUM_DEFAULT_CHANNELS = 2 }; static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { // Default operational frequencies - AS923_F1 | BAND_CENTI, + AS923_F1 | BAND_CENTI, AS923_F2 | BAND_CENTI, }; @@ -242,8 +242,8 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; - LMIC.channelDrMap[chidx] = - drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) + LMIC.channelDrMap[chidx] = + drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) : drmap; if (fEnable) LMIC.channelMap |= 1 << chidx; // enabled right away @@ -288,22 +288,22 @@ void LMICas923_setRx1Params(void) { case 7: effective_rx1DrOffset = -2; break; default: /* no change */ break; } - + // per regional 2.2.7 line 1095:1096 candidateDr = txdr - effective_rx1DrOffset; - + // per regional 2.2.7 lines 1097:1100 if (LMICas923_getDownlinkDwellBit(LMIC.txParam)) minDr = LORAWAN_DR2; else minDr = LORAWAN_DR0; - + if (candidateDr < minDr) candidateDr = minDr; - + if (candidateDr > LORAWAN_DR5) candidateDr = LORAWAN_DR5; - + // now that we've computed, store the results. LMIC.dndr = (uint8_t) candidateDr; LMIC.rps = dndr2rps(LMIC.dndr); diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c index 8cfa3957..d052af09 100644 --- a/src/lmic/lmic_au921.c +++ b/src/lmic/lmic_au921.c @@ -67,10 +67,10 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr) { } int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1) { - if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) return -128; else - return ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); + return ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); } static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { diff --git a/src/lmic/lmic_bandplan.h b/src/lmic/lmic_bandplan.h index f56257a8..87475e9f 100644 --- a/src/lmic/lmic_bandplan.h +++ b/src/lmic/lmic_bandplan.h @@ -174,6 +174,11 @@ # define LMICbandplan_MAX_FCNT_GAP 16384 #endif // !defined LWAN_MAX_FCNT_GAP +// this is probably regional, but for now default can be the same +#if !defined(LMICbandplan_TX_RECOVERY_ms) +# define LMICbandplan_TX_RECOVERY_ms 100 +#endif + #define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) #define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) #define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index beb5a20f..3b6ee353 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -63,7 +63,7 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { }; int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { - uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; if (pindex < sizeof(constant_table_TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c index 5f6974c6..b25f1f4e 100644 --- a/src/lmic/lmic_eu_like.c +++ b/src/lmic/lmic_eu_like.c @@ -56,21 +56,57 @@ bit_t LMIC_enableChannel(u1_t channel) { return 0; } +// check whether a map operation will work. +// chpage is 0 or 6; 6 turns all on; 0 selects channels 0..15 via mask. +// The spec is unclear as to whether we should veto a channel mask that enables +// a channel that hasn't been configured; we veto it. 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>MCMD_LADR_POW_SHIFT; + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; if (pindex < sizeof(constant_table_TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 60ee6965..4877c20c 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -61,7 +61,7 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { }; int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { - uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; if (pindex < sizeof(constant_table_TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index d911af2e..be1313e4 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -65,10 +65,10 @@ uint8_t LMICus915_maxFrameLen(uint8_t dr) { } int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) { - if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) return -128; else - return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); } static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c index 9ec4e612..f0581d51 100644 --- a/src/lmic/lmic_us_like.c +++ b/src/lmic/lmic_us_like.c @@ -94,29 +94,29 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) { // 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 + || MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_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; - + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { // operate on channels 0..15, 16..31, 32..47, 48..63, 64..71 - if (chpage == (64 >> 4)) { + if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K) { if (chmap & 0xFF00) { // those are reserved bits, fail. return 0; } + } else { + return 1; } - } else if (chpage == MCMD_LADR_CHP_BANK) { + } else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_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; + } else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; // if disabling all 125kHz chans, must have at least one 500kHz chan // don't allow reserved bits to be set in chmap. @@ -132,7 +132,7 @@ bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) { bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { /* - || MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_125OFF are special. The + || MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_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. @@ -140,7 +140,7 @@ bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { u1_t base, top; bit_t result = 0; - if (chpage == MCMD_LADR_CHP_BANK) { + if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) { // each bit enables a bank of channels for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) { if (chmap & 1) { @@ -153,15 +153,16 @@ bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { return result; } - if (chpage < MCMD_LADR_CHP_USLIKE_SPECIAL) { + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { // operate on channels 0..15, 16..31, 32..47, 48..63 base = chpage << 4; top = base + 16; if (base == 64) { top = 72; } - } else /* if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) */ { - u1_t const en125 = chpage == MCMD_LADR_CHP_125ON; + } else /* if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) */ { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; // enable or disable all 125kHz channels for (u1_t chnl = 0; chnl < 64; ++chnl) { diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 991ffaa5..6b052d81 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -581,63 +581,28 @@ enum { // Bit fields byte#3 of MCMD_LinkADRReq payload enum { - MCMD_LADR_CHP_USLIKE_SPECIAL = 0x50, // first special for us-like - MCMD_LADR_CHP_BANK = 0x50, // special: bits are banks. - MCMD_LADR_CHP_125ON = 0x60, // special channel page enable, bits applied to 64..71 - MCMD_LADR_CHP_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71 - MCMD_LADR_N3RFU_MASK = 0x80, - MCMD_LADR_CHPAGE_MASK = 0xF0, - MCMD_LADR_REPEAT_MASK = 0x0F, - MCMD_LADR_REPEAT_1 = 0x01, - MCMD_LADR_CHPAGE_1 = 0x10 + MCMD_LinkADRReq_Redundancy_RFU = 0x80, + MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK= 0x70, + MCMD_LinkADRReq_Redundancy_NbTrans_MASK = 0x0F, + + MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT = 0x00, // direct masking for EU + MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON = 0x60, // EU: enable everything. + + MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K = 0x40, // mask is for the 8 us-like 500 kHz channels + MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL = 0x50, // first special for us-like + MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK = 0x50, // special: bits are banks. + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON = 0x60, // special channel page enable, bits applied to 64..71 + MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71 + + MCMD_LinkADRReq_ChMaskCntl_CN470_ALL_ON = 0x60, // turn all on for China. }; + // Bit fields byte#0 of MCMD_LinkADRReq payload enum { - MCMD_LADR_DR_MASK = 0xF0, - MCMD_LADR_POW_MASK = 0x0F, - MCMD_LADR_DR_SHIFT = 4, - MCMD_LADR_POW_SHIFT = 0, -#if defined(CFG_eu868) // TODO(tmm@mcci.com): complete refactor. - EU868_MCMD_LADR_SF12 = EU868_DR_SF12<<4, - EU868_MCMD_LADR_SF11 = EU868_DR_SF11<<4, - EU868_MCMD_LADR_SF10 = EU868_DR_SF10<<4, - EU868_MCMD_LADR_SF9 = EU868_DR_SF9 <<4, - EU868_MCMD_LADR_SF8 = EU868_DR_SF8 <<4, - EU868_MCMD_LADR_SF7 = EU868_DR_SF7 <<4, - EU868_MCMD_LADR_SF7B = EU868_DR_SF7B<<4, - EU868_MCMD_LADR_FSK = EU868_DR_FSK <<4, - - EU868_MCMD_LADR_20dBm = 0, - EU868_MCMD_LADR_14dBm = 1, - EU868_MCMD_LADR_11dBm = 2, - EU868_MCMD_LADR_8dBm = 3, - EU868_MCMD_LADR_5dBm = 4, - EU868_MCMD_LADR_2dBm = 5, -#elif defined(CFG_us915) - US915_MCMD_LADR_SF10 = US915_DR_SF10<<4, - US915_MCMD_LADR_SF9 = US915_DR_SF9 <<4, - US915_MCMD_LADR_SF8 = US915_DR_SF8 <<4, - US915_MCMD_LADR_SF7 = US915_DR_SF7 <<4, - US915_MCMD_LADR_SF8C = US915_DR_SF8C<<4, - US915_MCMD_LADR_SF12CR = US915_DR_SF12CR<<4, - US915_MCMD_LADR_SF11CR = US915_DR_SF11CR<<4, - US915_MCMD_LADR_SF10CR = US915_DR_SF10CR<<4, - US915_MCMD_LADR_SF9CR = US915_DR_SF9CR<<4, - US915_MCMD_LADR_SF8CR = US915_DR_SF8CR<<4, - US915_MCMD_LADR_SF7CR = US915_DR_SF7CR<<4, - - US915_MCMD_LADR_30dBm = 0, - US915_MCMD_LADR_28dBm = 1, - US915_MCMD_LADR_26dBm = 2, - US915_MCMD_LADR_24dBm = 3, - US915_MCMD_LADR_22dBm = 4, - US915_MCMD_LADR_20dBm = 5, - US915_MCMD_LADR_18dBm = 6, - US915_MCMD_LADR_16dBm = 7, - US915_MCMD_LADR_14dBm = 8, - US915_MCMD_LADR_12dBm = 9, - US915_MCMD_LADR_10dBm = 10 -#endif + MCMD_LinkADRReq_DR_MASK = 0xF0, + MCMD_LinkADRReq_POW_MASK = 0x0F, + MCMD_LinkADRReq_DR_SHIFT = 4, + MCMD_LinkADRReq_POW_SHIFT = 0, }; // bit fields of the TxParam request From 59192e7fde7470bbe6f1d9f17ec5f301b9a543b1 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 16 Aug 2019 23:51:24 -0400 Subject: [PATCH 06/27] Add NbTrans handling --- src/lmic/lmic.c | 25 +++++++++++++++++++++---- src/lmic/lmic.h | 1 + 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 119e82f8..053c2377 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -608,6 +608,7 @@ static void stateJustJoined (void) { LMIC.seqnoDn = LMIC.seqnoUp = 0; LMIC.rejoinCnt = 0; LMIC.dnConf = LMIC.lastDnConf = LMIC.adrChanged = 0; + LMIC.upRepeatCount = LMIC.upRepeat = 0; #if !defined(DISABLE_MCMD_RXParamSetupReq) LMIC.dn2Ans = 0; #endif @@ -1826,7 +1827,7 @@ static void buildDataFrame (void) { | (end-OFF_DAT_OPTS)); os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); - if( LMIC.txCnt == 0 ) { + if( LMIC.txCnt == 0 && LMIC.upRepeatCount == 0 ) { LMIC.seqnoUp += 1; DO_DEVDB(LMIC.seqnoUp,seqnoUp); } else { @@ -1847,6 +1848,11 @@ static void buildDataFrame (void) { // Confirmed only makes sense if we have a payload (or at least a port) LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1; if( LMIC.txCnt == 0 ) LMIC.txCnt = 1; + } else if (LMIC.upRepeat != 0) { + // we are repeating. So we need to count here. + if (LMIC.upRepeatCount == 0) { + LMIC.upRepeatCount = 1; + } } LMIC.frame[end] = LMIC.pendTxPort; os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen); @@ -2108,6 +2114,16 @@ static bit_t processDnData (void) { } // confirmed uplink is complete without an ack: no port and no flag initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); + } else if (LMIC.upRepeatCount != 0) { + if (LMIC.upRepeatCount < LMIC.upRepeat) { + LMIC.upRepeatCount += 1; + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // counted out: nothing received. + initTxrxFlags(__func__, TXRX_NOPORT); } else { // Nothing received - implies no port initTxrxFlags(__func__, TXRX_NOPORT); @@ -2351,7 +2367,6 @@ static void engineUpdate (void) { #endif // !DISABLE_BEACONS if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) { - // Need to TX some data... // Assuming txChnl points to channel which first becomes available again. bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0); // Find next suitable channel and return availability time @@ -2584,8 +2599,10 @@ void LMIC_clrTxData (void) { void LMIC_setTxData (void) { ArduinoLMIC_putEventDatum("LMIC_setTxData", (LMIC.pendTxPort << 24u) | (LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); LMIC.opmode |= OP_TXDATA; - if( (LMIC.opmode & OP_JOINING) == 0 ) - LMIC.txCnt = 0; // cancel any ongoing TX/RX retries + if( (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC.txCnt = 0; // reset the confirmed uplink FSM + LMIC.upRepeatCount = 0; // reset the unconfirmed repeat FSM + } engineUpdate(); } diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index fd5dc554..350457e4 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -449,6 +449,7 @@ struct lmic_t { u1_t errcr; // error coding rate (used for TX only) u1_t rejoinCnt; // adjustment for rejoin datarate + u1_t upRepeatCount; // current up-repeat bit_t initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244 u1_t pendTxPort; From bfe662a0c246a3d3254d42fe21ddca8d458f3f06 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sat, 17 Aug 2019 00:34:37 -0400 Subject: [PATCH 07/27] Fix certification of LinkADRReq: don't set ADRACKReq eagerly --- src/lmic/lmic.c | 6 ++++-- src/lmic/lorabase.h | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 053c2377..067cafd4 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -785,7 +785,9 @@ applyAdrRequests( changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); } - LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + // Certification doesn't like this, but it makes the device happier with TTN. + // LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + return response_fit; } @@ -1823,7 +1825,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 >= LINK_CHECK_CONT ? FCT_ADRARQ : 0) + | (LMIC.adrAckReq >= LINK_CHECK_CONT ? FCT_ADRACKReq : 0) | (end-OFF_DAT_OPTS)); os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 6b052d81..d3b58101 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -475,11 +475,11 @@ enum { }; enum { // Bitfields in frame control octet - FCT_ADREN = 0x80, - FCT_ADRARQ = 0x40, - FCT_ACK = 0x20, - FCT_MORE = 0x10, // also in DN direction: Class B indicator - FCT_OPTLEN = 0x0F, + FCT_ADREN = 0x80, + FCT_ADRACKReq = 0x40, + FCT_ACK = 0x20, + FCT_MORE = 0x10, // also in DN direction: Class B indicator + FCT_OPTLEN = 0x0F, }; enum { // In UP direction: signals class B enabled From 4a1839cbd1a59125f8b902714346710b2a4c302f Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sat, 17 Aug 2019 02:01:29 -0400 Subject: [PATCH 08/27] Pass KR920 tests (individually) --- src/lmic/lmic.c | 75 ++++++++++++++++++++++++++++------------ src/lmic/lmic.h | 2 ++ src/lmic/lmic_bandplan.h | 4 +++ src/lmic/lmic_eu868.c | 2 +- src/lmic/lmic_eu_like.c | 46 ++++++++++++++---------- src/lmic/lmic_eu_like.h | 3 ++ src/lmic/lmic_in866.c | 2 +- src/lmic/lmic_kr920.c | 2 +- src/lmic/lmic_us_like.c | 33 ++++++++++++------ src/lmic/lmic_us_like.h | 3 ++ 10 files changed, 117 insertions(+), 55 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 067cafd4..1e67fe88 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -38,7 +38,7 @@ DEFINE_LMIC; -#define LMIC_ENABLE_TRACE 1 +//#define LMIC_ENABLE_TRACE 1 #ifndef LMIC_ENABLE_TRACE # define LMIC_ENABLE_TRACE 0 @@ -750,37 +750,60 @@ applyAdrRequests( u1_t p1 = 0; u1_t p4 = 0; bit_t response_fit = 1; + bit_t map_ok = 1; LMICbandplan_saveAdrState(&initialState); - for (oidx = 0; oidx < olen && response_fit; ) { - if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { + // compute the changes + if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { + for (oidx = 0; oidx < olen; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } 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]; // ChMaskCtl, NbTrans u1_t chpage = p4 & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page - LMICbandplan_mapChannels(chpage, chmap); + map_ok = LMICbandplan_mapChannels(chpage, chmap); + ArduinoLMIC_putEventDatum("applyAdrRequests: mapChannels", (chpage << 16)|(chmap << 0)); } + } - oidx += kAdrReqSize; + if (! map_ok) { + adrAns &= ~MCMD_LinkADRAns_ChannelACK; + LMICbandplan_restoreAdrState(&initialState); + } + + // now put all the options + for (oidx = 0; oidx < olen && response_fit; oidx += kAdrReqSize) { + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; + } response_fit = put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); } // all done scanning options bit_t changes = LMICbandplan_compareAdrState(&initialState); - // handle uplink repeat count - u1_t uprpt = p4 & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count - if (LMIC.upRepeat != uprpt) { - LMIC.upRepeat = uprpt; - changes = 1; - } + // handle the final options + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { + // handle uplink repeat count + u1_t uprpt = p4 & MCMD_LinkADRReq_Redundancy_NbTrans_MASK; // up repeat count + if (LMIC.upRepeat != uprpt) { + LMIC.upRepeat = uprpt; + changes = 1; + } - if (adrAns & MCMD_LinkADRAns_DataRateACK) { dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); + ArduinoLMIC_putEventDatum("applyAdrRequests: setDrTxPow", (adrAns << 16)|(dr << 8)|(p1 << 0)); + // handle power changes here, too. changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); } @@ -798,6 +821,8 @@ scan_mac_cmds_link_adr( bit_t *presponse_fit ) { + ArduinoLMIC_putEventDatum("scan_mac_cmds_link_adr", olen); + if (olen == 0) return 0; @@ -867,6 +892,15 @@ scan_mac_cmds( response_fit = 1; cmd = opts[oidx]; + + /* compute length, and exit for illegal commands */ + int const cmdlen = getMacCmdSize(cmd); + if (cmdlen > olen - oidx) { + // "the first unknown command terminates processing" + olen = oidx; + break; + } + switch( cmd ) { case MCMD_LinkCheckAns: { // TODO(tmm@mcci.com) capture these, reliably.. @@ -1119,17 +1153,12 @@ scan_mac_cmds( } /* end case */ } /* end switch */ - /* 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; - } else if (! response_fit) { - olen = oidx; - } - - oidx += cmdlen; + /* if we're out of spce for responses, skip to end. */ + if (! response_fit) { + olen = oidx; + } else { + oidx += cmdlen; + } } /* end while */ return oidx; diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 350457e4..1f23a54c 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -168,6 +168,8 @@ enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are imm struct lmic_saved_adr_state_s { u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; }; #endif // ========================================================================== diff --git a/src/lmic/lmic_bandplan.h b/src/lmic/lmic_bandplan.h index 87475e9f..b4c552c6 100644 --- a/src/lmic/lmic_bandplan.h +++ b/src/lmic/lmic_bandplan.h @@ -158,6 +158,10 @@ # error "LMICbandplan_compareAdrState() not defined by bandplan" #endif +#if !defined(LMICbandplan_restoreAdrState) +# error "LMICbandplan_restoreAdrState() not defined by bandplan" +#endif + // // Things common to lmic.c code // diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index 3b6ee353..de700ecb 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -64,7 +64,7 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; - if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { return -128; diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c index b25f1f4e..00acbe92 100644 --- a/src/lmic/lmic_eu_like.c +++ b/src/lmic/lmic_eu_like.c @@ -80,12 +80,11 @@ bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap) { } } -// assumes that LMICeulike_canMapChannels passed. Return true if something changed. -// chpage is 0 or 6; 6 turns all on; 0 selects channels 0..15 via mask. +// assumes that LMICeulike_canMapChannels passed. Return true if this would +// be a valid final configuration. +// chpage is 0 or 0x60; 0x60 turns all on; 0 selects channels 0..15 via mask. // Assumes canMapChannels has already approved this change. bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) { - u2_t const old_chmap = LMIC.channelMap; - switch (chpage) { case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT: LMIC.channelMap = chmap; @@ -106,7 +105,7 @@ bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) { // do nothing. break; } - return old_chmap != chmap; + return LMIC.channelMap != 0; } #if !defined(DISABLE_JOIN) @@ -159,12 +158,12 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { LMIC.txChnl = 0; if ((++LMIC.txCnt % nDefaultChannels) == 0) { // Lower DR every nth try (having all default channels with same DR) - // - // TODO(tmm@mcci.com) add new DR_REGIN_JOIN_MIN instead of LORAWAN_DR0; - // then we can eliminate the LMIC_REGION_as923 below because we'll set - // the failed flag here. This will cause the outer caller to take the - // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() - // + // + // TODO(tmm@mcci.com) add new DR_REGIN_JOIN_MIN instead of LORAWAN_DR0; + // then we can eliminate the LMIC_REGION_as923 below because we'll set + // the failed flag here. This will cause the outer caller to take the + // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() + // if (LMIC.datarate == LORAWAN_DR0) failed = 1; // we have tried all DR - signal EV_JOIN_FAILED else @@ -185,12 +184,12 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { // Duty cycle is based on txend. ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); - // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty // cycle on power up. For testability, add a way to set the join start time // externally (a test API) so we can check this feature. // See https://github.com/mcci-catena/arduino-lmic/issues/2 - // Current code doesn't match LoRaWAN 1.0.2 requirements. + // Current code doesn't match LoRaWAN 1.0.2 requirements. LMIC.txend = time + (isTESTMODE() @@ -206,12 +205,12 @@ 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; + os_copyMem( + pStateBuffer->channelFreq, + LMIC.channelFreq, + sizeof(LMIC.channelFreq) + ); + pStateBuffer->channelMap = LMIC.channelMap; } bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { @@ -220,6 +219,15 @@ bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { return pStateBuffer->channelMap != LMIC.channelMap; } +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelFreq, + pStateBuffer->channelFreq, + sizeof(LMIC.channelFreq) + ); + LMIC.channelMap = pStateBuffer->channelMap; +} + void LMICeulike_setRx1Freq(void) { #if !defined(DISABLE_MCMD_DlChannelReq) uint32_t dlFreq = LMIC.channelDlFreq[LMIC.txChnl]; diff --git a/src/lmic/lmic_eu_like.h b/src/lmic/lmic_eu_like.h index 8d869734..16f2679e 100644 --- a/src/lmic/lmic_eu_like.h +++ b/src/lmic/lmic_eu_like.h @@ -101,6 +101,9 @@ void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); #define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState) +void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICeulike_restoreAdrState(pState) + // set Rx1 frequency (might be different than uplink). void LMICeulike_setRx1Freq(void); diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 8ca14fd2..b99e9686 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -64,7 +64,7 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) { uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; - if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { return -128; diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 4877c20c..cb07b2a8 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -62,7 +62,7 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; - if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { return TABLE_GET_S1(TXPOWLEVELS, pindex); } else { return -128; diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c index f0581d51..90d8c92b 100644 --- a/src/lmic/lmic_us_like.c +++ b/src/lmic/lmic_us_like.c @@ -130,6 +130,7 @@ bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) { return 1; } +// map channels. return true if configuration looks valid. bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { /* || MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The @@ -138,19 +139,18 @@ bit_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_LinkADRReq_ChMaskCntl_USLIKE_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); + LMIC_enableSubBand(subband); } else { - result |= LMIC_disableSubBand(subband); + LMIC_disableSubBand(subband); } } - return result; + return LMIC.activeChannels125khz || LMIC.activeChannels500khz; } if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { @@ -167,9 +167,9 @@ bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { // enable or disable all 125kHz channels for (u1_t chnl = 0; chnl < 64; ++chnl) { if (en125) - result |= LMIC_enableChannel(chnl); + LMIC_enableChannel(chnl); else - result |= LMIC_disableChannel(chnl); + LMIC_disableChannel(chnl); } // then apply mask to top 8 channels. @@ -181,11 +181,11 @@ bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { // Use enable/disable channel to keep activeChannel counts in sync. for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) { if (chmap & 0x0001) - result |= LMIC_enableChannel(chnl); + LMIC_enableChannel(chnl); else - result |= LMIC_disableChannel(chnl); + LMIC_disableChannel(chnl); } - return result; + return LMIC.activeChannels125khz || LMIC.activeChannels500khz; } // US does not have duty cycling - return now as earliest TX time @@ -288,13 +288,26 @@ ostime_t LMICuslike_nextJoinState(void) { #endif void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { - memcpy( + os_copyMem( pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap) ); + pStateBuffer->activeChannels125khz = LMIC.activeChannels125khz; + pStateBuffer->activeChannels500khz = LMIC.activeChannels500khz; } +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelMap, + pStateBuffer->channelMap, + sizeof(LMIC.channelMap) + ); + LMIC.activeChannels125khz = pStateBuffer->activeChannels125khz; + LMIC.activeChannels500khz = pStateBuffer->activeChannels500khz; +} + + bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0; } diff --git a/src/lmic/lmic_us_like.h b/src/lmic/lmic_us_like.h index ba6dc89d..316a12c8 100644 --- a/src/lmic/lmic_us_like.h +++ b/src/lmic/lmic_us_like.h @@ -106,4 +106,7 @@ void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer); bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer); #define LMICbandplan_compareAdrState(pState) LMICuslike_compareAdrState(pState) +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer); +#define LMICbandplan_restoreAdrState(pState) LMICuslike_restoreAdrState(pState) + #endif // _lmic_us_like_h_ From 5a37419b982f0badfa8b503b5599255e546c9e70 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:17:51 -0400 Subject: [PATCH 09/27] Fix #344: Fix #346: improve FSK handling, add docs --- src/lmic/radio.c | 270 ++++++++++++++++++++++++++++------------------- 1 file changed, 163 insertions(+), 107 deletions(-) diff --git a/src/lmic/radio.c b/src/lmic/radio.c index a7f3db21..60344ae1 100644 --- a/src/lmic/radio.c +++ b/src/lmic/radio.c @@ -32,105 +32,106 @@ // ---------------------------------------- // Registers Mapping +// // -type- 1272 vs 1276 #define RegFifo 0x00 // common -#define RegOpMode 0x01 // common -#define FSKRegBitrateMsb 0x02 -#define FSKRegBitrateLsb 0x03 -#define FSKRegFdevMsb 0x04 -#define FSKRegFdevLsb 0x05 -#define RegFrfMsb 0x06 // common -#define RegFrfMid 0x07 // common -#define RegFrfLsb 0x08 // common -#define RegPaConfig 0x09 // common -#define RegPaRamp 0x0A // common -#define RegOcp 0x0B // common -#define RegLna 0x0C // common -#define FSKRegRxConfig 0x0D +#define RegOpMode 0x01 // common see below +#define FSKRegBitrateMsb 0x02 // - +#define FSKRegBitrateLsb 0x03 // - +#define FSKRegFdevMsb 0x04 // - +#define FSKRegFdevLsb 0x05 // - +#define RegFrfMsb 0x06 // common FSK: 1272: 915; 1276: 434 MHz +#define RegFrfMid 0x07 // common ditto +#define RegFrfLsb 0x08 // common ditto +#define RegPaConfig 0x09 // common see below, many diffs +#define RegPaRamp 0x0A // common see below: bits 6..4 are diff +#define RegOcp 0x0B // common - +#define RegLna 0x0C // common bits 4..0 are diff. +#define FSKRegRxConfig 0x0D // - #define LORARegFifoAddrPtr 0x0D -#define FSKRegRssiConfig 0x0E +#define FSKRegRssiConfig 0x0E // - #define LORARegFifoTxBaseAddr 0x0E -#define FSKRegRssiCollision 0x0F +#define FSKRegRssiCollision 0x0F // - #define LORARegFifoRxBaseAddr 0x0F -#define FSKRegRssiThresh 0x10 +#define FSKRegRssiThresh 0x10 // - #define LORARegFifoRxCurrentAddr 0x10 -#define FSKRegRssiValue 0x11 +#define FSKRegRssiValue 0x11 // - #define LORARegIrqFlagsMask 0x11 -#define FSKRegRxBw 0x12 +#define FSKRegRxBw 0x12 // - #define LORARegIrqFlags 0x12 -#define FSKRegAfcBw 0x13 +#define FSKRegAfcBw 0x13 // - #define LORARegRxNbBytes 0x13 -#define FSKRegOokPeak 0x14 +#define FSKRegOokPeak 0x14 // - #define LORARegRxHeaderCntValueMsb 0x14 -#define FSKRegOokFix 0x15 +#define FSKRegOokFix 0x15 // - #define LORARegRxHeaderCntValueLsb 0x15 -#define FSKRegOokAvg 0x16 +#define FSKRegOokAvg 0x16 // - #define LORARegRxPacketCntValueMsb 0x16 #define LORARegRxpacketCntValueLsb 0x17 #define LORARegModemStat 0x18 #define LORARegPktSnrValue 0x19 -#define FSKRegAfcFei 0x1A +#define FSKRegAfcFei 0x1A // - #define LORARegPktRssiValue 0x1A -#define FSKRegAfcMsb 0x1B +#define FSKRegAfcMsb 0x1B // - #define LORARegRssiValue 0x1B -#define FSKRegAfcLsb 0x1C +#define FSKRegAfcLsb 0x1C // - #define LORARegHopChannel 0x1C -#define FSKRegFeiMsb 0x1D +#define FSKRegFeiMsb 0x1D // - #define LORARegModemConfig1 0x1D -#define FSKRegFeiLsb 0x1E +#define FSKRegFeiLsb 0x1E // - #define LORARegModemConfig2 0x1E -#define FSKRegPreambleDetect 0x1F +#define FSKRegPreambleDetect 0x1F // - #define LORARegSymbTimeoutLsb 0x1F -#define FSKRegRxTimeout1 0x20 +#define FSKRegRxTimeout1 0x20 // - #define LORARegPreambleMsb 0x20 -#define FSKRegRxTimeout2 0x21 +#define FSKRegRxTimeout2 0x21 // - #define LORARegPreambleLsb 0x21 -#define FSKRegRxTimeout3 0x22 +#define FSKRegRxTimeout3 0x22 // - #define LORARegPayloadLength 0x22 -#define FSKRegRxDelay 0x23 +#define FSKRegRxDelay 0x23 // - #define LORARegPayloadMaxLength 0x23 -#define FSKRegOsc 0x24 +#define FSKRegOsc 0x24 // - #define LORARegHopPeriod 0x24 -#define FSKRegPreambleMsb 0x25 +#define FSKRegPreambleMsb 0x25 // - #define LORARegFifoRxByteAddr 0x25 +#define FSKRegPreambleLsb 0x26 // - #define LORARegModemConfig3 0x26 -#define FSKRegPreambleLsb 0x26 -#define FSKRegSyncConfig 0x27 +#define FSKRegSyncConfig 0x27 // - #define LORARegFeiMsb 0x28 -#define FSKRegSyncValue1 0x28 +#define FSKRegSyncValue1 0x28 // - #define LORAFeiMib 0x29 -#define FSKRegSyncValue2 0x29 +#define FSKRegSyncValue2 0x29 // - #define LORARegFeiLsb 0x2A -#define FSKRegSyncValue3 0x2A -#define FSKRegSyncValue4 0x2B +#define FSKRegSyncValue3 0x2A // - +#define FSKRegSyncValue4 0x2B // - #define LORARegRssiWideband 0x2C -#define FSKRegSyncValue5 0x2C -#define FSKRegSyncValue6 0x2D -#define FSKRegSyncValue7 0x2E -#define FSKRegSyncValue8 0x2F +#define FSKRegSyncValue5 0x2C // - +#define FSKRegSyncValue6 0x2D // - +#define FSKRegSyncValue7 0x2E // - +#define FSKRegSyncValue8 0x2F // - #define LORARegIffReq1 0x2F -#define FSKRegPacketConfig1 0x30 +#define FSKRegPacketConfig1 0x30 // - #define LORARegIffReq2 0x30 -#define FSKRegPacketConfig2 0x31 +#define FSKRegPacketConfig2 0x31 // - #define LORARegDetectOptimize 0x31 -#define FSKRegPayloadLength 0x32 -#define FSKRegNodeAdrs 0x33 +#define FSKRegPayloadLength 0x32 // - +#define FSKRegNodeAdrs 0x33 // - #define LORARegInvertIQ 0x33 -#define FSKRegBroadcastAdrs 0x34 -#define FSKRegFifoThresh 0x35 -#define FSKRegSeqConfig1 0x36 +#define FSKRegBroadcastAdrs 0x34 // - +#define FSKRegFifoThresh 0x35 // - +#define FSKRegSeqConfig1 0x36 // - #define LORARegHighBwOptimize1 0x36 -#define FSKRegSeqConfig2 0x37 +#define FSKRegSeqConfig2 0x37 // - #define LORARegDetectionThreshold 0x37 -#define FSKRegTimerResol 0x38 -#define FSKRegTimer1Coef 0x39 +#define FSKRegTimerResol 0x38 // - +#define FSKRegTimer1Coef 0x39 // - #define LORARegSyncWord 0x39 -#define FSKRegTimer2Coef 0x3A +#define FSKRegTimer2Coef 0x3A // - #define LORARegHighBwOptimize2 0x3A -#define FSKRegImageCal 0x3B -#define FSKRegTemp 0x3C -#define FSKRegLowBat 0x3D -#define FSKRegIrqFlags1 0x3E -#define FSKRegIrqFlags2 0x3F +#define FSKRegImageCal 0x3B // - +#define FSKRegTemp 0x3C // - +#define FSKRegLowBat 0x3D // - +#define FSKRegIrqFlags1 0x3E // - +#define FSKRegIrqFlags2 0x3F // - #define RegDioMapping1 0x40 // common #define RegDioMapping2 0x41 // common #define RegVersion 0x42 // common @@ -146,8 +147,8 @@ // #define RegBitRateFrac 0x70 // common #if defined(CFG_sx1276_radio) -#define RegTcxo 0x4B // common -#define RegPaDac 0x4D // common +#define RegTcxo 0x4B // common different addresses, same bits +#define RegPaDac 0x4D // common differnet addresses, same bits #elif defined(CFG_sx1272_radio) #define RegTcxo 0x58 // common #define RegPaDac 0x5A // common @@ -241,9 +242,12 @@ // Parameters for RSSI monitoring #define SX127X_FREQ_LF_MAX 525000000 // per datasheet 6.3 -// per datasheet 5.5.3: -#define SX127X_RSSI_ADJUST_LF -164 // add to rssi value to get dB (LF) -#define SX127X_RSSI_ADJUST_HF -157 // add to rssi value to get dB (HF) +// per datasheet 5.5.3 and 5.5.5: +#define SX1272_RSSI_ADJUST -139 // add to rssi value to get dB (LF) + +// per datasheet 5.5.3 and 5.5.5: +#define SX1276_RSSI_ADJUST_LF -164 // add to rssi value to get dB (LF) +#define SX1276_RSSI_ADJUST_HF -157 // add to rssi value to get dB (HF) // per datasheet 2.5.2 (but note that we ought to ask Semtech to confirm, because // datasheet is unclear). @@ -262,6 +266,40 @@ #define OPMODE_RX_SINGLE 0x06 #define OPMODE_CAD 0x07 +// ---------------------------------------- +// FSK opmode bits +// bits 6:5 are the same for 1272 and 1276 +#define OPMODE_FSK_SX127x_ModulationType_FSK (0u << 5) +#define OPMODE_FSK_SX127x_ModulationType_OOK (1u << 5) +#define OPMODE_FSK_SX127x_ModulationType_MASK (3u << 5) + +// bits 4:3 are different for 1272 +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT1_0 (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5 (2u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_3 (3u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_None (0u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_BR (1u << 3) +#define OPMODE_FSK_SX1272_ModulationShaping_OOK_2BR (2u << 3) + +#define OPMODE_FSK_SX1272_ModulationShaping_MASK (3u << 3) + +// SX1276 +#define OPMODE_FSK_SX1276_LowFrequencyModeOn (1u << 3) + +// define the opmode bits apporpriate for the 127x in use. +#if defined(CFG_sx1272_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK | \ + OPMODE_FSK_SX1272_ModulationShaping_FSK_BT0_5) +#elif defined(CFG_sx1276_radio) +# define OPMODE_FSK_SX127X_SETUP (OPMODE_FSK_SX127x_ModulationType_FSK) +#endif + +// ---------------------------------------- +// LoRa opmode bits +#define OPMODE_LORA_SX127x_AccessSharedReg (1u << 6) +#define OPMODE_LORA_SX1276_LowFrequencyModeOn (1u << 3) + // ---------------------------------------- // Bits masking the corresponding IRQs from the radio #define IRQ_LORA_RXTOUT_MASK 0x80 @@ -315,12 +353,8 @@ #define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 #define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default - -// RADIO STATE -// (initialized by radio_init(), used by radio_rand1()) -static u1_t randbuf[16]; - - +// LNA gain constant. Bits 4..0 have different meaning for 1272 and 1276, but +// by chance, the bit patterns we use are the same. #ifdef CFG_sx1276_radio #define LNA_RX_GAIN (0x20|0x3) #elif CFG_sx1272_radio @@ -329,6 +363,10 @@ static u1_t randbuf[16]; #error Missing CFG_sx1272_radio/CFG_sx1276_radio #endif +// RADIO STATE +// (initialized by radio_init(), used by radio_rand1()) +static u1_t randbuf[16]; + static void writeReg (u1_t addr, u1_t data ) { hal_spi_write(addr | 0x80, &data, 1); @@ -371,15 +409,20 @@ static void opmode (u1_t mode) { static void opmodeLora() { u1_t u = OPMODE_LORA; #ifdef CFG_sx1276_radio - u |= 0x8; // TBD: sx1276 high freq + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } #endif writeOpmode(u); } static void opmodeFSK() { - u1_t u = 0; + u1_t u = OPMODE_FSK_SX127X_SETUP; + #ifdef CFG_sx1276_radio - u |= 0x8; // TBD: sx1276 high freq + if (LMIC.freq <= SX127X_FREQ_LF_MAX) { + u |= OPMODE_FSK_SX1276_LowFrequencyModeOn; + } #endif writeOpmode(u); } @@ -659,36 +702,55 @@ static void configPower () { writeReg(RegOcp, rOcp | SX127X_OCP_ENA); } -static void txfsk () { - // select FSK modem (from sleep mode) - writeOpmode(0x10); // FSK, BT=0.5 - ASSERT(readReg(RegOpMode) == 0x10); - // enter standby mode (required for FIFO loading)) - opmode(OPMODE_STANDBY); +static void setupFskRxTx(bit_t fDisableAutoClear) { // set bitrate writeReg(FSKRegBitrateMsb, 0x02); // 50kbps writeReg(FSKRegBitrateLsb, 0x80); // set frequency deviation writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz writeReg(FSKRegFdevLsb, 0x99); - // frame and packet handler settings - writeReg(FSKRegPreambleMsb, 0x00); - writeReg(FSKRegPreambleLsb, 0x05); - writeReg(FSKRegSyncConfig, 0x12); - writeReg(FSKRegPacketConfig1, 0xD0); - writeReg(FSKRegPacketConfig2, 0x40); + + // set sync config + writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync + + // set packet config + writeReg(FSKRegPacketConfig1, fDisableAutoClear ? 0xD8 : 0xD0); // var-length, whitening, crc, no auto-clear, no adr filter + writeReg(FSKRegPacketConfig2, 0x40); // packet mode + + // set sync value writeReg(FSKRegSyncValue1, 0xC1); writeReg(FSKRegSyncValue2, 0x94); writeReg(FSKRegSyncValue3, 0xC1); +} + +static void txfsk () { + // select FSK modem (from sleep mode) + opmodeFSK(); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // set bitrate etc + setupFskRxTx(/* don't autoclear CRC */ 0); + + // frame and packet handler settings + writeReg(FSKRegPreambleMsb, 0x00); + writeReg(FSKRegPreambleLsb, 0x05); + // configure frequency configChannel(); // configure output power configPower(); +#ifdef CFG_sx1276_radio + // select Gausian filter BT=0.5, default ramp. + writeReg(RegPaRamp, 0x29); +#endif + // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP); // initialize the payload size and address pointers + // TODO(tmm@mcci.com): datasheet says this is not used in variable packet length mode writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) // download length byte and buffer to the radio FIFO @@ -715,7 +777,11 @@ static void txlora () { // configure frequency configChannel(); // configure output power +#ifdef CFG_sx1272_radio writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec +#elif defined(CFG_sx1276_radio) + writeReg(RegPaRamp, 0x08); // set PA ramp-up time 50 uSec, clear FSK bits +#endif configPower(); // set sync word writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); @@ -904,33 +970,19 @@ static void rxfsk (u1_t rxmode) { // configure frequency configChannel(); // set LNA gain - //writeReg(RegLna, 0x20|0x03); // max gain, boost enable - writeReg(RegLna, LNA_RX_GAIN); + writeReg(RegLna, LNA_RX_GAIN); // max gain, boost enable. // configure receiver writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? // set receiver bandwidth - writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb + writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb // set AFC bandwidth - writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB + writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB // set preamble detection writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors - // set sync config - writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync - // set packet config - writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter - writeReg(FSKRegPacketConfig2, 0x40); // packet mode - // set sync value - writeReg(FSKRegSyncValue1, 0xC1); - writeReg(FSKRegSyncValue2, 0x94); - writeReg(FSKRegSyncValue3, 0xC1); // set preamble timeout writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2); - // set bitrate - writeReg(FSKRegBitrateMsb, 0x02); // 50kbps - writeReg(FSKRegBitrateLsb, 0x80); - // set frequency deviation - writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz - writeReg(FSKRegFdevLsb, 0x99); + // set bitrate, autoclear CRC + setupFskRxTx(1); // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); @@ -1065,11 +1117,15 @@ void radio_monitor_rssi(ostime_t nTicks, oslmic_radio_rssi_t *pRssi) { // while we're waiting for the PLLs to spin up, determine which // band we're in and choose the base RSSI. +#if defined(CFG_sx1276_radio) if (LMIC.freq > SX127X_FREQ_LF_MAX) { - rssiAdjust = SX127X_RSSI_ADJUST_HF; + rssiAdjust = SX1276_RSSI_ADJUST_HF; } else { - rssiAdjust = SX127X_RSSI_ADJUST_LF; + rssiAdjust = SX1276_RSSI_ADJUST_LF; } +#elif defined(CFG_sx1272_radio) + rssiAdjust = SX1272_RSSI_ADJUST; +#endif rssiAdjust += hal_getRssiCal(); // zero the results From 88af127af08eac670f9e9544983ea8518372af70 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:22:55 -0400 Subject: [PATCH 10/27] touch up comments --- src/lmic/lmic.c | 4 ++-- src/lmic/lmic.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 1e67fe88..0f5ea251 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -924,7 +924,7 @@ scan_mac_cmds( } case MCMD_DevStatusReq: { - // LMIC.snr is SNR time 4, convert to real SNR; rounding towards zero. + // LMIC.snr is SNR times 4, convert to real SNR; rounding towards zero. 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)); @@ -2145,7 +2145,7 @@ static bit_t processDnData (void) { } // confirmed uplink is complete without an ack: no port and no flag initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); - } else if (LMIC.upRepeatCount != 0) { + } else if (LMIC.upRepeatCount != 0) { if (LMIC.upRepeatCount < LMIC.upRepeat) { LMIC.upRepeatCount += 1; txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 1f23a54c..0f0c7734 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -460,7 +460,7 @@ struct lmic_t { u1_t pendTxData[MAX_LEN_PAYLOAD]; u1_t pendMacLen; // number of bytes of pending Mac response data - bit_t pendMacPiggyback; // received on port 0 or piggyback? + bit_t pendMacPiggyback; // received on port 0 or piggyback? // response data if piggybacked u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; From 53e9b9ee9d0bc59f612366da02dfa80b048dd2b5 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:25:50 -0400 Subject: [PATCH 11/27] Fix #375: correct names, add back inadvertent drop of MCMD_BeaconInfoReq --- src/lmic/lmic.c | 10 ++++++++-- src/lmic/lorabase.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 0f5ea251..a95bb94e 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -1844,6 +1844,12 @@ static void buildDataFrame (void) { LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx; } #endif // LMIC_ENABLE_DeviceTimeReq +#if !defined(DISABLE_BEACONS) && defined(ENABLE_MCMD_BeaconTimingAns) + if ( LMIC.bcninfoTries > 0 ) { + LMIC.frame[end+0] = MCMD_BeaconInfoReq; + end += 1; + } +#endif ASSERT(end <= OFF_DAT_OPTS+16); u1_t flen = end + (txdata ? 5+dlen : 4); @@ -1962,7 +1968,7 @@ bit_t LMIC_enableTracking (u1_t tryBcnInfo) { if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 ) return 0; // already in progress or failed to enable // If BCN info requested from NWK then app has to take are - // of sending data up so that MCMD_BeaconTimingReq can be attached. + // of sending data up so that MCMD_BeaconInfoReq can be attached. if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) startScan(); return 1; // enabled @@ -2254,7 +2260,7 @@ static bit_t processDnData (void) { reportEventNoUpdate(EV_LINK_DEAD); // update? } #if !defined(DISABLE_BEACONS) - // If this falls to zero the NWK did not answer our MCMD_BeaconTimingReq commands - try full scan + // If this falls to zero the NWK did not answer our MCMD_BeaconInfoReq commands - try full scan if( LMIC.bcninfoTries > 0 ) { if( (LMIC.opmode & OP_TRACK) != 0 ) { reportEventNoUpdate(EV_BEACON_FOUND); // update? diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index d3b58101..c1ec1cdc 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -512,7 +512,7 @@ enum { // Class B MCMD_PingSlotInfoReq = 0x10, // u1: 7=RFU, 6-4:interval, 3-0:datarate MCMD_PingSlotChannelAns = 0x11, // u1: 7-1:RFU, 0:freq ok - MCMD_BeaconTimingReq = 0x12, // - (DEPRECATED) + MCMD_BeaconInfoReq = 0x12, // - (DEPRECATED) MCMD_BeaconFreqAns = 0x13, // u1: 7-1:RFU, 0:freq ok }; From 97c2366d45b2589c5629f58bbc9793d3322cc810 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:26:36 -0400 Subject: [PATCH 12/27] Move ADR check out of middle of code building MAC uplink --- src/lmic/lmic.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index a95bb94e..c485d3b3 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -1829,14 +1829,6 @@ static void buildDataFrame (void) { } #endif // !DISABLE_MCMD_RXTimingSetupReq) - if( LMIC.adrChanged ) { - // 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 LMIC_ENABLE_DeviceTimeReq if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { LMIC.frame[end+0] = MCMD_DeviceTimeReq; @@ -1852,6 +1844,15 @@ static void buildDataFrame (void) { #endif ASSERT(end <= OFF_DAT_OPTS+16); + if( LMIC.adrChanged ) { + // 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; + } + u1_t flen = end + (txdata ? 5+dlen : 4); if( flen > MAX_LEN_FRAME ) { // Options and payload too big - delay payload From a13b623f106647313074d967fac764a20b11cf7c Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:27:09 -0400 Subject: [PATCH 13/27] Add repeat-count debugging --- src/lmic/lmic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index c485d3b3..51e2d2fe 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -2154,6 +2154,7 @@ static bit_t processDnData (void) { initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); } else if (LMIC.upRepeatCount != 0) { if (LMIC.upRepeatCount < LMIC.upRepeat) { + ArduinoLMIC_putEventDatum("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); LMIC.upRepeatCount += 1; txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); LMIC.opmode &= ~OP_TXRXPEND; @@ -2161,6 +2162,7 @@ static bit_t processDnData (void) { return 1; } // counted out: nothing received. + LMIC.upRepeatCount = 0; initTxrxFlags(__func__, TXRX_NOPORT); } else { // Nothing received - implies no port From ca8853867390be3231be866626179d6802abf2c6 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Aug 2019 20:27:39 -0400 Subject: [PATCH 14/27] Trim whitespace --- .../compliance-otaa-halconfig/compliance-otaa-halconfig.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index c29f0096..02c2f5f4 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -244,7 +244,7 @@ void printOpmode(uint16_t opmode, char sep = ',') { } void printTxend(ostime_t txend) { - Serial.print(F(", txend=")); Serial.print(txend); + Serial.print(F(", txend=")); Serial.print(txend); } void eventPrint(cEventQueue::eventnode_t &e) { From d7d8f35868b5ee7e4a3bdf4db8f0db59d0def023 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 21 Aug 2019 15:51:30 -0400 Subject: [PATCH 15/27] Fix #377: don't repeat uplink seqnos for acks of confs The counter `LMIC.txCnt` was not being reset at the right time; thus we sometimes treated an ACK or a POLL as a retry. With the latest changes, even more so. --- src/lmic/lmic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 51e2d2fe..d46e8f96 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -1869,6 +1869,7 @@ static void buildDataFrame (void) { LMIC.seqnoUp += 1; DO_DEVDB(LMIC.seqnoUp,seqnoUp); } else { + ArduinoLMIC_putEventDatum("retransmit", (LMIC.frame[OFF_DAT_FCT] << 24u) | (LMIC.txCnt << 16u)|(LMIC.upRepeatCount << 8u) | (LMIC.upRepeat<<0u)); EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoUp-1, @@ -2162,7 +2163,6 @@ static bit_t processDnData (void) { return 1; } // counted out: nothing received. - LMIC.upRepeatCount = 0; initTxrxFlags(__func__, TXRX_NOPORT); } else { // Nothing received - implies no port @@ -2174,6 +2174,8 @@ static bit_t processDnData (void) { // the transmission that got us here is complete. txcomplete: LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + // turn off all the repeat stuff. + LMIC.txCnt = LMIC.upRepeatCount = 0; // if there's pending mac data that's not piggyback, launch it now. if (LMIC.pendMacLen != 0) { From 434871d1269c0fb2b251d90f888f6326f4fc09f8 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 21 Aug 2019 20:51:23 -0400 Subject: [PATCH 16/27] Rename the trace functions LMICOS_logEvent() etc. Renaming makes sense, because from the LMIC's point of view, logging functions are provided by the OS/HAL. It might make further sense to move the LOG function from the compliance test script to the OS/HAL, so the logging can be used by any script without makeing them recreate the facility. On the other hand, that would make it more of a headache to override the default in the normal case. --- .../compliance-otaa-halconfig.ino | 8 +-- src/lmic/config.h | 7 +++ src/lmic/lmic.c | 52 +++++++------------ src/lmic/oslmic.h | 12 +++++ 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index 02c2f5f4..08cefa38 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -154,16 +154,16 @@ private: cEventQueue eventQueue; extern "C" { - void ArduinoLMIC_putEvent(const char *pMessage); - void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum); + void LMICOS_logEvent(const char *pMessage); + void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); } -void ArduinoLMIC_putEvent(const char *pMessage) +void LMICOS_logEvent(const char *pMessage) { eventQueue.putEvent(ev_t(-1), pMessage); } -void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum) +void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) { eventQueue.putEvent(ev_t(-2), pMessage, datum); } diff --git a/src/lmic/config.h b/src/lmic/config.h index c5eb1d18..75735de1 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -192,4 +192,11 @@ # define LMIC_ENABLE_long_messages 1 /* PARAM */ #endif +// LMIC_ENABLE_event_logging +// LMIC debugging for certification tests requires this, because debug prints affect +// timing too dramatically. +#if !defined(LMIC_ENABLE_event_logging) +# define LMIC_ENABLE_event_logging 1 /* PARAM */ +#endif + #endif // _lmic_config_h_ diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index d46e8f96..9bba5d8c 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -38,20 +38,6 @@ DEFINE_LMIC; -//#define LMIC_ENABLE_TRACE 1 - -#ifndef LMIC_ENABLE_TRACE -# define LMIC_ENABLE_TRACE 0 -#endif - -#if LMIC_ENABLE_TRACE -extern void ArduinoLMIC_putEvent(const char *pMessage); -extern void ArduinoLMIC_putEventDatum(const char *pMessage, uint32_t datum); -#else -# define ArduinoLMIC_putEvent(m) do { ; } while (0) -# define ArduinoLMIC_putEventDatum(m, d) do { ; } while (0) -#endif - // Fwd decls. static void reportEventNoUpdate(ev_t); static void reportEventAndUpdate(ev_t); @@ -769,7 +755,7 @@ applyAdrRequests( u1_t chpage = p4 & MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK; // channel page map_ok = LMICbandplan_mapChannels(chpage, chmap); - ArduinoLMIC_putEventDatum("applyAdrRequests: mapChannels", (chpage << 16)|(chmap << 0)); + LMICOS_logEventUint32("applyAdrRequests: mapChannels", (chpage << 16)|(chmap << 0)); } } @@ -802,7 +788,7 @@ applyAdrRequests( dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); - ArduinoLMIC_putEventDatum("applyAdrRequests: setDrTxPow", (adrAns << 16)|(dr << 8)|(p1 << 0)); + LMICOS_logEventUint32("applyAdrRequests: setDrTxPow", (adrAns << 16)|(dr << 8)|(p1 << 0)); // handle power changes here, too. changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); @@ -821,7 +807,7 @@ scan_mac_cmds_link_adr( bit_t *presponse_fit ) { - ArduinoLMIC_putEventDatum("scan_mac_cmds_link_adr", olen); + LMICOS_logEventUint32("scan_mac_cmds_link_adr", olen); if (olen == 0) return 0; @@ -1006,7 +992,7 @@ scan_mac_cmds( if( ans == MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK) { if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { - ArduinoLMIC_putEventDatum("NewChannelReq: setupChannel failed", (MaxDR << 24u) | (MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); + LMICOS_logEventUint32("NewChannelReq: setupChannel failed", (MaxDR << 24u) | (MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); ans &= ~MCMD_NewChannelAns_ChannelACK; } } @@ -1180,7 +1166,7 @@ static bit_t decodeFrame (void) { const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); #endif if (dlen > 0) - ArduinoLMIC_putEventDatum("decodeFrame", (dlen << 8) | (hdr << 0)); + LMICOS_logEventUint32("decodeFrame", (dlen << 8) | (hdr << 0)); if( dlen < OFF_DAT_OPTS+4 || (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || @@ -1208,7 +1194,7 @@ static bit_t decodeFrame (void) { int pend = dlen-4; // MIC if( addr != LMIC.devaddr ) { - ArduinoLMIC_putEventDatum("decodeFrame: wrong address", addr); + LMICOS_logEventUint32("decodeFrame: wrong address", addr); EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, e_.eui = MAIN::CDEV->getEui(), @@ -1217,7 +1203,7 @@ static bit_t decodeFrame (void) { goto norx; } if( poff > pend ) { - ArduinoLMIC_putEventDatum("decodeFrame: corrupted frame", (dlen << 16) | (fct << 8) | (poff - pend)); + LMICOS_logEventUint32("decodeFrame: corrupted frame", (dlen << 16) | (fct << 8) | (poff - pend)); EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, e_.eui = MAIN::CDEV->getEui(), e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16))); @@ -1244,7 +1230,7 @@ static bit_t decodeFrame (void) { } if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { - ArduinoLMIC_putEventDatum("decodeFrame: bad MIC", seqno); + LMICOS_logEventUint32("decodeFrame: bad MIC", seqno); EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, e_.eui1 = MAIN::CDEV->getEui(), e_.info1 = Base::lsbf4(&d[pend]), @@ -1258,7 +1244,7 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoDn, e_.info2 = seqno)); - ArduinoLMIC_putEventDatum("decodeFrame: rollover discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + LMICOS_logEventUint32("decodeFrame: rollover discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); goto norx; } if( seqno != LMIC.seqnoDn-1 || !LMIC.lastDnConf || ftype != HDR_FTYPE_DCDN ) { @@ -1266,19 +1252,19 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoDn, e_.info2 = seqno)); - ArduinoLMIC_putEventDatum("decodeFrame: Retransmit confimed discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + LMICOS_logEventUint32("decodeFrame: Retransmit confimed discarded", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); goto norx; } // Replay of previous sequence number allowed only if // previous frame and repeated both requested confirmation // but set a flag, so we don't actually process the message. - ArduinoLMIC_putEventDatum("decodeFrame: Retransmit confimed accepted", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + LMICOS_logEventUint32("decodeFrame: Retransmit confimed accepted", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); replayConf = 1; LMIC.dnConf = FCT_ACK; } else { if( seqnoDiff > LMICbandplan_MAX_FCNT_GAP) { - ArduinoLMIC_putEventDatum("decodeFrame: gap too big", (seqnoDiff << 16) | (seqno & 0xFFFFu)); + LMICOS_logEventUint32("decodeFrame: gap too big", (seqnoDiff << 16) | (seqno & 0xFFFFu)); goto norx; } if( seqno > LMIC.seqnoDn ) { @@ -1292,7 +1278,7 @@ static bit_t decodeFrame (void) { // DN frame requested confirmation - provide ACK once with next UP frame LMIC.dnConf = LMIC.lastDnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0); if (LMIC.dnConf) - ArduinoLMIC_putEventDatum("decodeFrame: Confirmed downlink", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + LMICOS_logEventUint32("decodeFrame: Confirmed downlink", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); } if (port == 0 && olen != 0 && pend > poff) { @@ -1377,7 +1363,7 @@ static bit_t decodeFrame (void) { e_.info = Base::lsbf4(&d[pend]), e_.info2 = seqno)); // discard the data - ArduinoLMIC_putEventDatum("decodeFrame: discarding replay", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + LMICOS_logEventUint32("decodeFrame: discarding replay", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); goto norx; } @@ -1869,7 +1855,7 @@ static void buildDataFrame (void) { LMIC.seqnoUp += 1; DO_DEVDB(LMIC.seqnoUp,seqnoUp); } else { - ArduinoLMIC_putEventDatum("retransmit", (LMIC.frame[OFF_DAT_FCT] << 24u) | (LMIC.txCnt << 16u)|(LMIC.upRepeatCount << 8u) | (LMIC.upRepeat<<0u)); + LMICOS_logEventUint32("retransmit", (LMIC.frame[OFF_DAT_FCT] << 24u) | (LMIC.txCnt << 16u)|(LMIC.upRepeatCount << 8u) | (LMIC.upRepeat<<0u)); EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoUp-1, @@ -2155,7 +2141,7 @@ static bit_t processDnData (void) { initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); } else if (LMIC.upRepeatCount != 0) { if (LMIC.upRepeatCount < LMIC.upRepeat) { - ArduinoLMIC_putEventDatum("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); + LMICOS_logEventUint32("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); LMIC.upRepeatCount += 1; txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); LMIC.opmode &= ~OP_TXRXPEND; @@ -2180,7 +2166,7 @@ static bit_t processDnData (void) { // if there's pending mac data that's not piggyback, launch it now. if (LMIC.pendMacLen != 0) { if (LMIC.pendMacPiggyback) { - ArduinoLMIC_putEvent("piggyback mac message"); + LMICOS_logEvent("piggyback mac message"); LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. } else { // Every mac command on port 0 requires an uplink, if there's data. @@ -2193,7 +2179,7 @@ static bit_t processDnData (void) { LMIC.pendTxLen = LMIC.pendMacLen; LMIC.pendMacLen = 0; // discard mac data! LMIC.opmode |= OP_TXDATA; - ArduinoLMIC_putEvent("port0 mac message"); + LMICOS_logEvent("port0 mac message"); } } @@ -2639,7 +2625,7 @@ void LMIC_clrTxData (void) { void LMIC_setTxData (void) { - ArduinoLMIC_putEventDatum("LMIC_setTxData", (LMIC.pendTxPort << 24u) | (LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); + LMICOS_logEventUint32("LMIC_setTxData", (LMIC.pendTxPort << 24u) | (LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); LMIC.opmode |= OP_TXDATA; if( (LMIC.opmode & OP_JOINING) == 0 ) { LMIC.txCnt = 0; // reset the confirmed uplink FSM diff --git a/src/lmic/oslmic.h b/src/lmic/oslmic.h index 81267042..5ede98c6 100644 --- a/src/lmic/oslmic.h +++ b/src/lmic/oslmic.h @@ -324,6 +324,18 @@ extern xref2u1_t AESaux; u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); #endif +// ====================================================================== +// Simple logging support. Vanishes unless enabled. + +#if LMIC_ENABLE_event_logging +extern void LMICOS_logEvent(const char *pMessage); +extern void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); +#else // ! LMIC_ENABLE_event_logging +# define LMICOS_logEvent(m) do { ; } while (0) +# define LMICOS_logEventUint32(m, d) do { ; } while (0) +#endif // ! LMIC_ENABLE_event_logging + + LMIC_END_DECLS #endif // _oslmic_h_ From 0d6d9513a43034662ab7eb8864fd697fd76241f3 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 21 Aug 2019 22:31:06 -0400 Subject: [PATCH 17/27] Add LMIC event name string table macros --- .../compliance-otaa-halconfig.ino | 10 +------- src/lmic/lmic.h | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index 08cefa38..050ea32d 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -56,15 +56,7 @@ bool g_fTestMode = false; lmic_event_cb_t myEventCb; lmic_rxmessage_cb_t myRxMessageCb; -const char * const evNames[] = { - "<>", - "EV_SCAN_TIMEOUT", "EV_BEACON_FOUND", - "EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING", - "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_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE" -}; +const char * const evNames[] = { LMIC_EVENT_NAME_TABLE__INIT }; /* diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 0f0c7734..4cf6a598 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -256,6 +256,29 @@ enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE }; typedef enum _ev_t ev_t; +// this macro can be used to initalize a normal table of event strings +#define LMIC_EVENT_NAME_TABLE__INIT \ + "<>", \ + "EV_SCAN_TIMEOUT", "EV_BEACON_FOUND", \ + "EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING", \ + "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_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE" + +// if working on an AVR (or worried about it), you can use this multi-zero +// string and put this in a single const F() string. Index through this +// counting up from 0, until you get to the entry you want or to an +// entry that begins with a \0. +#define LMIC_EVENT_NAME_MULTISZ__INIT \ + "<>\0" \ + "EV_SCAN_TIMEOUT\0" "EV_BEACON_FOUND\0" \ + "EV_BEACON_MISSED\0" "EV_BEACON_TRACKED\0" "EV_JOINING\0" \ + "EV_JOINED\0" "EV_RFU1\0" "EV_JOIN_FAILED\0" "EV_REJOIN_FAILED\0" \ + "EV_TXCOMPLETE\0" "EV_LOST_TSYNC\0" "EV_RESET\0" \ + "EV_RXCOMPLETE\0" "EV_LINK_DEAD\0" "EV_LINK_ALIVE\0" "EV_SCAN_FOUND\0" \ + "EV_TXSTART\0" "EV_TXCANCELED\0" "EV_RXSTART\0" "EV_JOIN_TXCOMPLETE\0" + enum { // This value represents 100% error in LMIC.clockError MAX_CLOCK_ERROR = 65536, From aabd00e80f7ac4502635d48857b05885c1249873 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 21 Aug 2019 22:33:08 -0400 Subject: [PATCH 18/27] Trim whitespace --- src/lmic/radio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lmic/radio.c b/src/lmic/radio.c index 60344ae1..0451d88f 100644 --- a/src/lmic/radio.c +++ b/src/lmic/radio.c @@ -123,7 +123,7 @@ #define FSKRegSeqConfig2 0x37 // - #define LORARegDetectionThreshold 0x37 #define FSKRegTimerResol 0x38 // - -#define FSKRegTimer1Coef 0x39 // - +#define FSKRegTimer1Coef 0x39 // - #define LORARegSyncWord 0x39 #define FSKRegTimer2Coef 0x3A // - #define LORARegHighBwOptimize2 0x3A From eeb2bf9627735e0bd50d06227c3d3c1f8b5297c0 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 21 Aug 2019 23:09:03 -0400 Subject: [PATCH 19/27] Revert logging default to 'off'; document logging control --- README.md | 7 +++++++ src/lmic/config.h | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 475dbe08..61eeac27 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ requires C99 mode to be enabled by default. - [Disabling user events](#disabling-user-events) - [Disabling external reference to `onEvent()`](#disabling-external-reference-to-onevent) - [Enabling long messages](#enabling-long-messages) + - [Enabling LMIC event logging calls](#enabling-lmic-event-logging-calls) - [Special purpose](#special-purpose) - [Supported hardware](#supported-hardware) - [Pre-Integrated Boards](#pre-integrated-boards) @@ -334,6 +335,12 @@ In some embedded systems, `onEvent()` may be defined for some other purpose; so To save RAM for simple devices, the LMIC allows message length to be limited to 64 bytes instead of the LoRaWAN standard of 255 bytes max. This saves about 2*192 bytes of RAM. Unfortunately, compliance tests require the full message size. Long messages are enabled by setting `LMIC_ENABLE_long_messages` to 1, or disabled by setting it to zero. This C preprocessor macro is always defined as a post-condition of `#include "config.h"`; if non-zero, the maximum frame size is 255 bytes, and if zero, the maximum frame size is 64 bytes. +#### Enabling LMIC event logging calls + +When debugging the LMIC, debug prints change timing, and can make things not work at all. The LMIC has embedded optional calls to capture debug information that can be printed out later, when the LMIC is not active. Logging is enabled by setting `LMIC_ENABLE_event_logging` to 1. The default is not to log. This C preprocessor macro is always defined as a post-condition of `#include "config.h"`. + +The compliance test script includes a suitable logging implementation; the other example scripts do not. + #### Special purpose `#define DISABLE_INVERT_IQ_ON_RX` disables the inverted Q-I polarity on RX. **Use of this variable is deprecated, see issue [#250](https://github.com/mcci-catena/arduino-lmic/issues/250).** Rather than defining this, set the value of `LMIC.noRXIQinversion`. If set non-zero, receive will be non-inverted. End-devices will be able to receive messages from each other, but will not be able to hear the gateway (other than Class B beacons)aa. If set zero, (the default), end devices will only be able to hear gateways, not each other. diff --git a/src/lmic/config.h b/src/lmic/config.h index 75735de1..29ffec52 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -194,9 +194,9 @@ // LMIC_ENABLE_event_logging // LMIC debugging for certification tests requires this, because debug prints affect -// timing too dramatically. +// timing too dramatically. But normal operation doesn't need this. #if !defined(LMIC_ENABLE_event_logging) -# define LMIC_ENABLE_event_logging 1 /* PARAM */ +# define LMIC_ENABLE_event_logging 0 /* PARAM */ #endif #endif // _lmic_config_h_ From 7f29f6820c3df9f00e05662f861e5cb4bc9dbe8f Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 22 Aug 2019 01:07:34 -0400 Subject: [PATCH 20/27] compile compliance script correctly w/o LMIC logging --- .../compliance-otaa-halconfig/compliance-otaa-halconfig.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index 050ea32d..ec268aa2 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -145,6 +145,7 @@ private: cEventQueue eventQueue; +#if LMIC_ENABLE_event_logging extern "C" { void LMICOS_logEvent(const char *pMessage); void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); @@ -159,6 +160,7 @@ void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) { eventQueue.putEvent(ev_t(-2), pMessage, datum); } +#endif // LMIC_ENABLE_event_logging uint8_t lastTxChannel; bool lastTxStart; From 99481b4d3c497b7f4d23720359646f10df8705d8 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 14:04:05 -0400 Subject: [PATCH 21/27] Fix #382: Fix #238: confirmed uplink/explicit NACK must retry Fix #382: after receiveing a message with `FHDR.FCnt.ACK` == 0, check whether we requested confirmation on the last uplink. If so, treat as a NACK and retransmit. Fix #238: Rewrite `processDnData()` to eliminate `goto`s, which were really only tail recursion and therefore both unneeded and confusing (to me, anyway). --- src/lmic/lmic.c | 310 +++++++++++++++++++++++++++--------------------- 1 file changed, 175 insertions(+), 135 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 9bba5d8c..716deca3 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -2118,163 +2118,203 @@ static void processPingRx (xref2osjob_t osjob) { // // Inputs: // LMIC.dataLen number of bytes receieved; 0 --> no message at all received. +// LMIC.txCnt currnt confirmed uplink count, or 0 for unconfirmed. +// LMIC.txrxflags state of play for the Class A engine and message receipt. +// +// and many other flags in txcomplete(). + +// forward references. +static bit_t processDnData_norx(void); +static bit_t processDnData_txcomplete(void); static bit_t processDnData (void) { ASSERT((LMIC.opmode & OP_TXRXPEND)!=0); if( LMIC.dataLen == 0 ) { // if this is an RX1 window, shouldn't we return 0 to schedule - // RX2? - norx: - if( LMIC.txCnt != 0 ) { - if( LMIC.txCnt < TXCONF_ATTEMPTS ) { - LMIC.txCnt += 1; - // TODO(tmm@mcci.com): check feasibility of lower datarate - setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, TABLE_GET_U1(DRADJUST, LMIC.txCnt)), KEEP_TXPOW); - // Schedule another retransmission - txDelay(LMIC.rxtime, RETRY_PERIOD_secs); - LMIC.opmode &= ~OP_TXRXPEND; - engineUpdate(); - return 1; - } - // confirmed uplink is complete without an ack: no port and no flag - initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); - } else if (LMIC.upRepeatCount != 0) { - if (LMIC.upRepeatCount < LMIC.upRepeat) { - LMICOS_logEventUint32("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); - LMIC.upRepeatCount += 1; - txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); - LMIC.opmode &= ~OP_TXRXPEND; - engineUpdate(); - return 1; - } - // counted out: nothing received. - initTxrxFlags(__func__, TXRX_NOPORT); - } else { - // Nothing received - implies no port - initTxrxFlags(__func__, TXRX_NOPORT); + // RX2? in fact, the rx1 caller ignores what we return, and + // norx() doesn't call txcomplete if this is RX1. + return processDnData_norx(); + } + // if we get here, LMIC.dataLen != 0, so there is some + // traffic. + else if( !decodeFrame() ) { + // if we are in downlink window 1, we need to schedule + // downlink window 2. + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + return 0; + else + // otherwise we are in downlink window 2; we will not + // get any more downlink traffic from this uplink, so we need + // to close the books on this uplink attempt + return processDnData_norx(); + } + // downlink frame was accepted. This means that we're done. Except + // there's one bizarre corner case. If we sent a confirmed message + // and got a downlink that didn't have an ACK, we have to retry. + // It is not clear why the network is permitted to do this; the + // fact that they scheduled a downlink for us during one of the RX + // windows is clear confirmation that the uplink made it to the + // network and was valid. However, compliance checks this, so + // we have to handle it and retransmit. + else if (LMIC.txCnt != 0 && (LMIC.txrxFlags & TXRX_NACK) != 0) + { + // grr. we're confirmed but the network downlink did not + // set the ACK bit. We know txCnt is non-zero, so this + // will immediately fall into the retransmit path. We don't + // want to do this unless it's a confirmed uplink. + return processDnData_norx(); } - setAdrAckCount(LMIC.adrAckReq + 1); - LMIC.dataBeg = LMIC.dataLen = 0; - - // the transmission that got us here is complete. - txcomplete: - LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); - // turn off all the repeat stuff. - LMIC.txCnt = LMIC.upRepeatCount = 0; - - // if there's pending mac data that's not piggyback, launch it now. - if (LMIC.pendMacLen != 0) { - if (LMIC.pendMacPiggyback) { - LMICOS_logEvent("piggyback mac message"); - LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. - } else { - // Every mac command on port 0 requires an uplink, if there's data. - // TODO(tmm@mcci.com) -- this is why we need a queueing structure for - // uplinks. - // open code the logic to build this because we don't want to call - // engineUpdate right now. Data is already in the uplink buffer. - LMIC.pendTxConf = 0; // not confirmed - LMIC.pendTxPort = 0; // port 0 - LMIC.pendTxLen = LMIC.pendMacLen; - LMIC.pendMacLen = 0; // discard mac data! - LMIC.opmode |= OP_TXDATA; - LMICOS_logEvent("port0 mac message"); - } + // the transmit of the uplink is really complete. + else { + return processDnData_txcomplete(); + } +} + +// nothing was received this window. +static bit_t processDnData_norx(void) { + if( LMIC.txCnt != 0 ) { + if( LMIC.txCnt < TXCONF_ATTEMPTS ) { + LMIC.txCnt += 1; + // TODO(tmm@mcci.com): check feasibility of lower datarate + setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, TABLE_GET_U1(DRADJUST, LMIC.txCnt)), KEEP_TXPOW); + // Schedule another retransmission + txDelay(LMIC.rxtime, RETRY_PERIOD_secs); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; } + // confirmed uplink is complete without an ack: no port and no flag + initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); + } else if (LMIC.upRepeatCount != 0) { + if (LMIC.upRepeatCount < LMIC.upRepeat) { + LMICOS_logEventUint32("processDnData: repeat", (LMIC.upRepeat<<8u) | (LMIC.upRepeatCount<<0u)); + LMIC.upRepeatCount += 1; + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + LMIC.opmode &= ~OP_TXRXPEND; + engineUpdate(); + return 1; + } + // counted out: nothing received. + initTxrxFlags(__func__, TXRX_NOPORT); + } else { + // Nothing received - implies no port + initTxrxFlags(__func__, TXRX_NOPORT); + } + setAdrAckCount(LMIC.adrAckReq + 1); + LMIC.dataBeg = LMIC.dataLen = 0; + + return processDnData_txcomplete(); +} + +// this Class-A uplink-and-receive cycle is complete. +static bit_t processDnData_txcomplete(void) { + LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + // turn off all the repeat stuff. + LMIC.txCnt = LMIC.upRepeatCount = 0; + + // if there's pending mac data that's not piggyback, launch it now. + if (LMIC.pendMacLen != 0) { + if (LMIC.pendMacPiggyback) { + LMICOS_logEvent("piggyback mac message"); + LMIC.opmode |= OP_POLL; // send back the mac answers even if there's no data. + } else { + // Every mac command on port 0 requires an uplink, if there's data. + // TODO(tmm@mcci.com) -- this is why we need a queueing structure for + // uplinks. + // open code the logic to build this because we don't want to call + // engineUpdate right now. Data is already in the uplink buffer. + LMIC.pendTxConf = 0; // not confirmed + LMIC.pendTxPort = 0; // port 0 + LMIC.pendTxLen = LMIC.pendMacLen; + LMIC.pendMacLen = 0; // discard mac data! + LMIC.opmode |= OP_TXDATA; + LMICOS_logEvent("port0 mac message"); + } + } - // Half-duplex gateways can have appreciable turn-around times, - // so we force a wait. It might be nice to randomize this a little, - // so that armies of identical devices will not try to talk all - // at once. This is potentially band-specific, so we let it come - // from the band-plan files. - txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); + // Half-duplex gateways can have appreciable turn-around times, + // so we force a wait. It might be nice to randomize this a little, + // so that armies of identical devices will not try to talk all + // at once. This is potentially band-specific, so we let it come + // from the band-plan files. + txDelay(os_getTime() + ms2osticks(LMICbandplan_TX_RECOVERY_ms), 0); #if LMIC_ENABLE_DeviceTimeReq - // - // if the DeviceTimeReq FSM is active, we need to move it to idle, - // completing the callback. - // - lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState; - if ( requestTimeState != lmic_RequestTimeState_idle ) { - 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.client.pNetworkTimeCb = NULL; - - // call the user's notification routine. - (*pNetworkTimeCb)(LMIC.client.pNetworkTimeUserData, flagSuccess); - } + // + // if the DeviceTimeReq FSM is active, we need to move it to idle, + // completing the callback. + // + lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState; + if ( requestTimeState != lmic_RequestTimeState_idle ) { + 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.client.pNetworkTimeCb = NULL; + + // call the user's notification routine. + (*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; - reportEventNoUpdate(EV_LINK_ALIVE); - } - 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 ) { - // We haven't heard from NWK for some time although we - // asked for a response for some time - assume we're disconnected. Lower DR one notch. - EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.adrAckReq)); - dr_t newDr = decDR((dr_t)LMIC.datarate); - // TODO(tmm@mcci.com) newDr must be feasible; there must be at least - // one channel that supports the new datarate. If not, stay - // at current 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. + if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) { + LMIC.opmode &= ~OP_LINKDEAD; + reportEventNoUpdate(EV_LINK_ALIVE); + } + 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 ) { + // We haven't heard from NWK for some time although we + // asked for a response for some time - assume we're disconnected. Lower DR one notch. + EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.adrAckReq)); + dr_t newDr = decDR((dr_t)LMIC.datarate); + // TODO(tmm@mcci.com) newDr must be feasible; there must be at least + // one channel that supports the new datarate. If not, stay + // at current 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. #if !defined(DISABLE_JOIN) - if ( LMIC.adrAckReq > LINK_CHECK_UNJOIN ) { - LMIC.opmode |= OP_UNJOIN; - } -#endif // !defined(DISABLE_JOIN) - } else { - // not in the dead state... let's wait another 32 - // uplinks before panicking. - setAdrAckCount(LINK_CHECK_CONT); + if ( LMIC.adrAckReq > LINK_CHECK_UNJOIN ) { + LMIC.opmode |= OP_UNJOIN; } - // 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? +#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_BeaconInfoReq commands - try full scan - if( LMIC.bcninfoTries > 0 ) { - if( (LMIC.opmode & OP_TRACK) != 0 ) { - reportEventNoUpdate(EV_BEACON_FOUND); // update? - LMIC.bcninfoTries = 0; - } - else if( --LMIC.bcninfoTries == 0 ) { - startScan(); // NWK did not answer - try scan - } + // If this falls to zero the NWK did not answer our MCMD_BeaconInfoReq commands - try full scan + if( LMIC.bcninfoTries > 0 ) { + if( (LMIC.opmode & OP_TRACK) != 0 ) { + reportEventNoUpdate(EV_BEACON_FOUND); // update? + LMIC.bcninfoTries = 0; + } + else if( --LMIC.bcninfoTries == 0 ) { + startScan(); // NWK did not answer - try scan } -#endif // !DISABLE_BEACONS - return 1; - } - // if we get here, LMIC.dataLen != 0, so there is some - // traffic. - if( !decodeFrame() ) { - if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) - return 0; - goto norx; } - goto txcomplete; +#endif // !DISABLE_BEACONS + return 1; } - #if !defined(DISABLE_BEACONS) static void processBeacon (xref2osjob_t osjob) { LMIC_API_PARAMETER(osjob); From 317fc2ef0ab81e75a55a487a8af94ec2ed619c9f Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 20:43:07 -0400 Subject: [PATCH 22/27] Set version to v2.3.2.70 --- src/lmic/lmic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 4cf6a598..cd06d952 100644 --- 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, 60) /* v2.3.2.60 */ +#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 3, 2, 70) /* v2.3.2.70 */ #define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ (((v) >> 24u) & 0xFFu) From 18149ae84e60095c8403f6fa9a023d88609fb9e6 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 20:44:40 -0400 Subject: [PATCH 23/27] Add documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 61eeac27..be5ed6f1 100644 --- a/README.md +++ b/README.md @@ -1098,6 +1098,7 @@ function uflt12f(rawUflt12) - HEAD adds the following changes. + - [#378](https://github.com/mcci-catena/arduino-lmic/pull/378) completely reworks MAC downlink handling. Resulting code passes the LoRaWAN V1.5 EU certification test. (v2.32.2.70) - [#360](https://github.com/mcci-catena/arduino-lmic/pull/360) adds support for the KR-920 regional plan. - v2.3.2 is a patch release. It incorporates two pull requests. From 20b47477230e8acc2905444345d97fc0a3692004 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 20:48:46 -0400 Subject: [PATCH 24/27] Catch asserts in compliance sketch and log instead --- .../compliance-otaa-halconfig.ino | 180 ++++++++++++++---- 1 file changed, 139 insertions(+), 41 deletions(-) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index ec268aa2..c310a57b 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -162,6 +162,16 @@ void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) } #endif // LMIC_ENABLE_event_logging +hal_failure_handler_t log_assertion; + +void log_assertion(const char *pMessage, uint16_t line) { + eventQueue.putEvent(ev_t(-3), pMessage, line); + eventPrintAll(); + Serial.println("***HALTED BY ASSERT***"); + while (true) + yield(); +} + uint8_t lastTxChannel; bool lastTxStart; @@ -216,6 +226,7 @@ const char *getCrcName(rps_t rps) { } void printFreq(u4_t freq) { + Serial.print(F(": freq=")); Serial.print(freq / 1000000); Serial.print(F(".")); Serial.print((freq % 1000000) / 100000); @@ -241,6 +252,54 @@ void printTxend(ostime_t txend) { Serial.print(F(", txend=")); Serial.print(txend); } +void printTxChnl(u1_t txChnl) { + Serial.print(F(": ch=")); + Serial.print(unsigned(txChnl)); +} + +void printDatarate(u1_t datarate) { + Serial.print(F(", datarate=")); Serial.print(unsigned(datarate)); +} + +void printTxrxflags(u2_t txrxFlags) { + Serial.print(F(", txrxFlags=0x")); Serial.print(unsigned(txrxFlags), HEX); + if (txrxFlags & TXRX_ACK) + Serial.print(F("; Received ack")); +} + +void printSaveIrqFlags(u1_t saveIrqFlags) { + Serial.print(F(", saveIrqFlags 0x")); + Serial.print(unsigned(saveIrqFlags), HEX); +} + +// dump all the registers. +void printAllRegisters(void) { + uint8_t regbuf[0x80]; + regbuf[0] = 0; + hal_spi_read(1, regbuf + 1, sizeof(regbuf) - 1); + + for (unsigned i = 0; i < sizeof(regbuf); ++i) { + if (i % 16 == 0) { + printf("\r\n%02x:", i); + } + printf("%s%02x", ((i % 16) == 8) ? " - " : " ", regbuf[i]); + } + + // reset the radio, just in case the register dump caused issues. + hal_pin_rst(0); + delay(2); + hal_pin_rst(2); + delay(6); + + // restore the radio to idle. + const uint8_t opmode = 0x88; // LoRa and sleep. + hal_spi_write(0x81, &opmode, 1); +} + +void printNl(void) { + Serial.println(""); +} + void eventPrint(cEventQueue::eventnode_t &e) { ev_t ev = e.event; @@ -255,6 +314,17 @@ void eventPrint(cEventQueue::eventnode_t &e) { Serial.print(F(", datum=0x")); Serial.print(e.datum, HEX); } printOpmode(e.opmode, '.'); + } else if (ev == ev_t(-3)) { + Serial.print(e.pMessage); + Serial.print(F(", line ")); Serial.print(e.datum); + printFreq(e.freq); + printTxend(e.txend); + printTxChnl(e.txChnl); + printRps(e.rps); + printOpmode(e.opmode); + printTxrxflags(e.opmode); + printSaveIrqFlags(e.saveIrqFlags); + printAllRegisters(); } else { if (ev < sizeof(evNames) / sizeof(evNames[0])) { Serial.print(evNames[ev]); @@ -275,8 +345,8 @@ void eventPrint(cEventQueue::eventnode_t &e) { break; case EV_JOINED: - Serial.print(F(": ch ")); - Serial.println(unsigned(e.txChnl)); + printTxChnl(e.txChnl); + printNl(); do { u4_t netid = 0; devaddr_t devaddr = 0; @@ -312,35 +382,12 @@ void eventPrint(cEventQueue::eventnode_t &e) { */ case EV_JOIN_FAILED: // print out rx info - Serial.print(F(": freq=")); printFreq(e.freq); + printFreq(e.freq); printRps(e.rps); printOpmode(e.opmode); printf(" irqLevel %u", hal_getIrqLevel()); - // dump all the registers. - do { - uint8_t regbuf[0x80]; - regbuf[0] = 0; - hal_spi_read(1, regbuf + 1, sizeof(regbuf) - 1); - - for (unsigned i = 0; i < sizeof(regbuf); ++i) { - if (i % 16 == 0) { - printf("\r\n%02x:", i); - } - printf("%s%02x", ((i % 16) == 8) ? " - " : " ", regbuf[i]); - } - - // reset the radio, just in case the register dump caused issues. - hal_pin_rst(0); - delay(2); - hal_pin_rst(2); - delay(6); - - // restore the radio to idle. - const uint8_t opmode = 0x88; // LoRa and sleep. - hal_spi_write(0x81, &opmode, 1); - - } while (0); + printAllRegisters(); break; case EV_REJOIN_FAILED: @@ -349,12 +396,9 @@ void eventPrint(cEventQueue::eventnode_t &e) { break; case EV_TXCOMPLETE: - Serial.print(F(": ch ")); - Serial.print(e.txChnl); + printTxChnl(e.txChnl); printRps(e.rps); - Serial.print(F(" txrxflags 0x")); Serial.print(e.txrxFlags, HEX); - if (e.txrxFlags & TXRX_ACK) - Serial.print(F("; Received ack")); + printTxrxflags(e.txrxFlags); break; case EV_LOST_TSYNC: break; @@ -377,34 +421,31 @@ void eventPrint(cEventQueue::eventnode_t &e) { case EV_TXSTART: // this event tells us that a transmit is about to start. // but printing here is bad for timing. - Serial.print(F(": ch ")); - Serial.print(unsigned(e.txChnl)); + printTxChnl(e.txChnl); printRps(e.rps); - Serial.print(F(", datarate ")); Serial.print(unsigned(e.datarate)); + printDatarate(e.datarate); printOpmode(e.opmode); printTxend(e.txend); break; case EV_RXSTART: - Serial.print(F(": freq=")); printFreq(e.freq); printRps(e.rps); - Serial.print(F(", datarate ")); Serial.print(unsigned(e.datarate)); + printDatarate(e.datarate); printOpmode(e.opmode); printTxend(e.txend); Serial.print(F(", delta ms ")); Serial.print(osticks2ms(e.time - e.txend)); break; case EV_JOIN_TXCOMPLETE: - Serial.print(F(": saveIrqFlags 0x")); - Serial.print(unsigned(e.saveIrqFlags), HEX); + printSaveIrqFlags(e.saveIrqFlags); break; default: break; } } - Serial.println(""); + printNl(); } /* @@ -534,7 +575,7 @@ void setup() { while (! Serial) ; Serial.begin(115200); - Serial.println(F("Starting")); + setup_printSignOn(); // LMIC init using the computed target const auto pPinMap = Arduino_LMIC::GetPinmap_ThisBoard(); @@ -545,6 +586,7 @@ void setup() { } // now that we have a pinmap, initalize the low levels accordingly. + hal_set_failure_handler(log_assertion); os_init_ex(pPinMap); // LMIC_reset() doesn't affect callbacks, so we can do this first. @@ -565,6 +607,62 @@ void setup() { do_send(&sendjob); } +void setup_printSignOnDashes(void) + { + Serial.print(F("------------------------------------")); + } +void setup_printSignOnDashLine() + { + setup_printSignOnDashes(); + setup_printSignOnDashes(); + printNl(); + } + +static constexpr const char *filebasename2(const char *s, const char *p) { + return p[0] == '\0' ? s : + (p[0] == '/' || p[0] == '\\') ? filebasename2(p + 1, p + 1) : + filebasename2(s, p + 1) ; +} + +static constexpr const char *filebasename(const char *s) + { + return filebasename2(s, s); + } + +void printVersionFragment(char sep, uint8_t v) { + if (sep != 0) { + Serial.print(sep); + } + Serial.print(unsigned(v)); +} + +void printVersion(uint32_t v) { + printVersionFragment(0, uint8_t(v >> 24u)); + printVersionFragment('.', uint8_t(v >> 16u)); + printVersionFragment('.', uint8_t(v >> 8u)); + if (uint8_t(v) != 0) { + printVersionFragment('.', uint8_t(v)); + } +} + +void setup_printSignOn() + { + printNl(); + + setup_printSignOnDashLine(); + + Serial.println(filebasename(__FILE__)); + Serial.print(F("LMIC version ")); + printVersion(ARDUINO_LMIC_VERSION); + Serial.print(F(" configured for region ")); + Serial.print(CFG_region); + Serial.println(F(".")); + Serial.println(F("Remember to select 'Line Ending: Newline' at the bottom of the monitor window.")); + + setup_printSignOnDashLine(); + printNl(); + } + void setupForNetwork(bool preJoin) { #if defined(CFG_us915) LMIC_selectSubBand(1); From 1fe1a9a0b1e721d69b218b364bd55618498dace4 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 20:53:31 -0400 Subject: [PATCH 25/27] Trim whitespace --- doc/release-notes.txt | 4 ++-- .../compliance-otaa-halconfig.ino | 10 ++++----- examples/raw-feather/raw-feather.ino | 2 +- examples/raw/raw.ino | 2 +- .../ttn-abp-feather-us915-dht22.ino | 20 ++++++++--------- examples/ttn-abp/ttn-abp.ino | 6 ++--- .../ttn-otaa-feather-us915-dht22.ino | 14 ++++++------ .../ttn-otaa-feather-us915.ino | 2 +- src/hal/getpinmap_catena4420.cpp | 2 +- src/hal/getpinmap_feather32u4lora.cpp | 2 +- src/hal/getpinmap_featherm0lora.cpp | 2 +- src/hal/getpinmap_ttgo_lora32_v1.cpp | 2 +- src/lmic/lmic_au921.c | 22 +++++++++---------- src/lmic/lmic_compliance.h | 2 +- src/lmic/lmic_env.h | 4 ++-- src/lmic/lmic_in866.c | 8 +++---- src/lmic/lmic_kr920.c | 8 +++---- src/lmic/lmic_us_like.h | 2 +- src/lmic/lorabase_us915.h | 2 +- src/lmic/lorawan_spec_compliance.h | 2 +- 20 files changed, 59 insertions(+), 59 deletions(-) diff --git a/doc/release-notes.txt b/doc/release-notes.txt index 37a4227b..53d2cb90 100644 --- a/doc/release-notes.txt +++ b/doc/release-notes.txt @@ -4,8 +4,8 @@ LMIC VERSION 1.6 (13-July-2015) - License changed to BSD - Modem included, see LMiC-Modem.pdf and examples/modem - - Additional stm32 hardware and Blipper board specific peripheral code - + - Additional stm32 hardware and Blipper board specific peripheral code + ============================================================================== LMIC VERSION 1.5 (8-May-2015) diff --git a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino index c310a57b..31ac7afe 100644 --- a/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino +++ b/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino @@ -38,7 +38,7 @@ static const u1_t PROGMEM DEVEUI[8]= { 1, 0, 0, 0, 0, 0, 0, 0 }; void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} // This key should be in big endian format (or, since it is not really a -// number but a block of memory, endianness does not really apply). +// number but a block of memory, endianness does not really apply). static const u1_t PROGMEM APPKEY[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }; void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} @@ -464,7 +464,7 @@ Definition: uint8_t port, const uint8_t *pMessage, size_t nMessage - ); + ); } Description: @@ -552,7 +552,7 @@ void sendComplete( if (! g_fTestMode) { // Schedule next transmission os_setTimedCallback(j, os_getTime()+sec2osticks(TX_INTERVAL), do_send); - } + } } void myFail(const char *pMessage) { @@ -590,7 +590,7 @@ void setup() { os_init_ex(pPinMap); // LMIC_reset() doesn't affect callbacks, so we can do this first. - if (! (LMIC_registerRxMessageCb(myRxMessageCb, /* userData */ nullptr) && + if (! (LMIC_registerRxMessageCb(myRxMessageCb, /* userData */ nullptr) && LMIC_registerEventCb(myEventCb, /* userData */ nullptr))) { myFail("couldn't register callbacks"); } @@ -676,7 +676,7 @@ void setupForNetwork(bool preJoin) { void loop() { os_runloop_once(); - while ((LMIC.opmode & OP_TXRXPEND) == 0 && + while ((LMIC.opmode & OP_TXRXPEND) == 0 && ! os_queryTimeCriticalJobs(ms2osticks(1000)) && eventPrintOne()) ; diff --git a/examples/raw-feather/raw-feather.ino b/examples/raw-feather/raw-feather.ino index 7f30f042..3522ef18 100644 --- a/examples/raw-feather/raw-feather.ino +++ b/examples/raw-feather/raw-feather.ino @@ -58,7 +58,7 @@ Author: // Pin mapping for Adafruit Feather M0 LoRa, etc. // -// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, // m0 defs ADAFRUIT_FEATHER_M0 // #if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) diff --git a/examples/raw/raw.ino b/examples/raw/raw.ino index c2325345..d4302109 100644 --- a/examples/raw/raw.ino +++ b/examples/raw/raw.ino @@ -33,7 +33,7 @@ // (payload length, frequency, spreading factor), be sure to check if // this interval should not also be increased. // See this spreadsheet for an easy airtime and duty cycle calculator: -// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc +// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc #define TX_INTERVAL 2000 // Pin mapping diff --git a/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino b/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino index 07f95889..3e6468bf 100644 --- a/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino +++ b/examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino @@ -1,18 +1,18 @@ /******************************************************************************* * The Things Network - ABP Feather - * + * * Example of using an Adafruit Feather M0 and DHT22 with a * single-channel TheThingsNetwork gateway. - * + * * This uses ABP (Activation by Personalization), where session keys for * communication would be assigned/generated by TTN and hard-coded on the device. - * + * * Learn Guide: https://learn.adafruit.com/lora-pi - * + * * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman * Copyright (c) 2018 Terry Moore, MCCI * Copyright (c) 2018 Brent Rubell, Adafruit Industries - * + * * Permission is hereby granted, free of charge, to anyone * obtaining a copy of this document and accompanying files, * to do whatever they want with them without any restriction, @@ -181,7 +181,7 @@ void do_send(osjob_t* j){ Serial.print("Temperature: "); Serial.print(temperature); Serial.println(" *C"); // adjust for the f2sflt16 range (-1 to 1) - temperature = temperature / 100; + temperature = temperature / 100; // read the humidity from the DHT22 float rHumidity = dht.readHumidity(); @@ -189,7 +189,7 @@ void do_send(osjob_t* j){ Serial.println(rHumidity); // adjust for the f2sflt16 range (-1 to 1) rHumidity = rHumidity / 100; - + // float -> int // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16) uint16_t payloadTemp = LMIC_f2sflt16(temperature); @@ -239,12 +239,12 @@ void setup() { memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); - + // We'll disable all 72 channels used by TTN for (int c = 0; c < 72; c++){ LMIC_disableChannel(c); } - + // We'll only enable Channel 16 (905.5Mhz) since we're transmitting on a single-channel LMIC_enableChannel(16); @@ -262,5 +262,5 @@ void setup() { } void loop() { - os_runloop_once(); + os_runloop_once(); } diff --git a/examples/ttn-abp/ttn-abp.ino b/examples/ttn-abp/ttn-abp.ino index 1c3d2fa5..83d56267 100644 --- a/examples/ttn-abp/ttn-abp.ino +++ b/examples/ttn-abp/ttn-abp.ino @@ -186,7 +186,7 @@ void do_send(osjob_t* j){ } void setup() { -// pinMode(13, OUTPUT); +// pinMode(13, OUTPUT); while (!Serial); // wait for Serial to be initialized Serial.begin(115200); delay(100); // per sample code on RF_95 test @@ -272,7 +272,7 @@ void loop() { else { digitalWrite(13, LOW); } - + os_runloop_once(); - + } diff --git a/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino b/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino index 7d1a2f50..e38a097e 100644 --- a/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino +++ b/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino @@ -1,15 +1,15 @@ /******************************************************************************* * The Things Network - Sensor Data Example - * + * * Example of sending a valid LoRaWAN packet with DHT22 temperature and * humidity data to The Things Networ using a Feather M0 LoRa. - * + * * Learn Guide: https://learn.adafruit.com/the-things-network-for-feather - * + * * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman * Copyright (c) 2018 Terry Moore, MCCI * Copyright (c) 2018 Brent Rubell, Adafruit Industries - * + * * Permission is hereby granted, free of charge, to anyone * obtaining a copy of this document and accompanying files, * to do whatever they want with them without any restriction, @@ -146,7 +146,7 @@ void onEvent (ev_t ev) { Serial.println(F("EV_REJOIN_FAILED")); break; break; - case EV_TXCOMPLETE: + case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); @@ -202,7 +202,7 @@ void do_send(osjob_t* j){ Serial.print("Temperature: "); Serial.print(temperature); Serial.println(" *C"); // adjust for the f2sflt16 range (-1 to 1) - temperature = temperature / 100; + temperature = temperature / 100; // read the humidity from the DHT22 float rHumidity = dht.readHumidity(); @@ -210,7 +210,7 @@ void do_send(osjob_t* j){ Serial.println(rHumidity); // adjust for the f2sflt16 range (-1 to 1) rHumidity = rHumidity / 100; - + // float -> int // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16) uint16_t payloadTemp = LMIC_f2sflt16(temperature); diff --git a/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino b/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino index 1ee214ad..27949e76 100644 --- a/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino +++ b/examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino @@ -76,7 +76,7 @@ const unsigned TX_INTERVAL = 60; // Pin mapping // -// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, // m0 defs ADAFRUIT_FEATHER_M0 // #if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) diff --git a/src/hal/getpinmap_catena4420.cpp b/src/hal/getpinmap_catena4420.cpp index 9b985864..ae833b00 100644 --- a/src/hal/getpinmap_catena4420.cpp +++ b/src/hal/getpinmap_catena4420.cpp @@ -45,7 +45,7 @@ class HalConfiguration_Catena4420_t : public HalConfiguration_t // virtual void end(void) override // virtual ostime_t setModuleActive(bool state) override - + }; static HalConfiguration_Catena4420_t myConfig; diff --git a/src/hal/getpinmap_feather32u4lora.cpp b/src/hal/getpinmap_feather32u4lora.cpp index 62ef53a5..c852eab8 100644 --- a/src/hal/getpinmap_feather32u4lora.cpp +++ b/src/hal/getpinmap_feather32u4lora.cpp @@ -45,7 +45,7 @@ class HalConfiguration_Feather32U4LoRa_t : public HalConfiguration_t // virtual void end(void) override // virtual ostime_t setModuleActive(bool state) override - + }; static HalConfiguration_Feather32U4LoRa_t myConfig; diff --git a/src/hal/getpinmap_featherm0lora.cpp b/src/hal/getpinmap_featherm0lora.cpp index 2fc31d04..7d221dcd 100644 --- a/src/hal/getpinmap_featherm0lora.cpp +++ b/src/hal/getpinmap_featherm0lora.cpp @@ -45,7 +45,7 @@ class HalConfiguration_FeatherM0LoRa_t : public HalConfiguration_t // virtual void end(void) override // virtual ostime_t setModuleActive(bool state) override - + }; static HalConfiguration_FeatherM0LoRa_t myConfig; diff --git a/src/hal/getpinmap_ttgo_lora32_v1.cpp b/src/hal/getpinmap_ttgo_lora32_v1.cpp index 7a05e7b8..71db3ebf 100644 --- a/src/hal/getpinmap_ttgo_lora32_v1.cpp +++ b/src/hal/getpinmap_ttgo_lora32_v1.cpp @@ -49,7 +49,7 @@ class HalConfiguration_ttgo_lora32_v1 : public HalConfiguration_t // virtual void end(void) override // virtual ostime_t setModuleActive(bool state) override - + }; static HalConfiguration_ttgo_lora32_v1 myConfig; diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c index d052af09..e80afe42 100644 --- a/src/lmic/lmic_au921.c +++ b/src/lmic/lmic_au921.c @@ -52,11 +52,11 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11] MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12] MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13] - ILLEGAL_RPS + ILLEGAL_RPS }; -static CONST_TABLE(u1_t, maxFrameLens)[] = { - 59+5, 59+5, 59+5, 123+5, 230+5, 230+5, 230+5, 255, +static CONST_TABLE(u1_t, maxFrameLens)[] = { + 59+5, 59+5, 59+5, 123+5, 230+5, 230+5, 230+5, 255, 41+5, 117+5, 230+5, 230+5, 230+5, 230+5 }; uint8_t LMICau921_maxFrameLen(uint8_t dr) { @@ -74,23 +74,23 @@ int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1) { } static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { - us2osticksRound(128 << 7), // DR_SF12 + us2osticksRound(128 << 7), // DR_SF12 us2osticksRound(128 << 6), // DR_SF11 us2osticksRound(128 << 5), // DR_SF10 - us2osticksRound(128 << 4), // DR_SF9 - us2osticksRound(128 << 3), // DR_SF8 - us2osticksRound(128 << 2), // DR_SF7 + us2osticksRound(128 << 4), // DR_SF9 + us2osticksRound(128 << 3), // DR_SF8 + us2osticksRound(128 << 2), // DR_SF7 us2osticksRound(128 << 1), // DR_SF8C - us2osticksRound(128 << 0), // ------ + us2osticksRound(128 << 0), // ------ us2osticksRound(128 << 5), // DR_SF12CR - us2osticksRound(128 << 4), // DR_SF11CR - us2osticksRound(128 << 3), // DR_SF10CR + us2osticksRound(128 << 4), // DR_SF11CR + us2osticksRound(128 << 3), // DR_SF10CR us2osticksRound(128 << 2), // DR_SF9CR us2osticksRound(128 << 1), // DR_SF8CR us2osticksRound(128 << 0), // DR_SF7CR }; -// get ostime for symbols based on datarate. This is not like us915, +// get ostime for symbols based on datarate. This is not like us915, // becuase the times don't match between the upper half and lower half // of the table. ostime_t LMICau921_dr2hsym(uint8_t dr) { diff --git a/src/lmic/lmic_compliance.h b/src/lmic/lmic_compliance.h index 14853016..7fceb3e4 100644 --- a/src/lmic/lmic_compliance.h +++ b/src/lmic/lmic_compliance.h @@ -7,7 +7,7 @@ Module: lmic_compliance.h Copyright notice and license info: See LICENSE file accompanying this project. - + Author: Terry Moore, MCCI Corporation March 2019 diff --git a/src/lmic/lmic_env.h b/src/lmic/lmic_env.h index 032f44bf..06793e0f 100644 --- a/src/lmic/lmic_env.h +++ b/src/lmic/lmic_env.h @@ -7,7 +7,7 @@ Module: lmic_env.h Copyright notice and license info: See LICENSE file accompanying this project. - + Author: Terry Moore, MCCI Corporation November 2018 @@ -39,7 +39,7 @@ Macro: LMIC_C_ASSERT() error. The results of using this macro where a declaration is not permitted are unspecified. - This is different from #if !(fErrorIfFalse) / #error in that the + This is different from #if !(fErrorIfFalse) / #error in that the expression is evaluated by the compiler rather than by the pre- processor. Therefore things like sizeof() can be used. diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index b99e9686..455e7dee 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -54,7 +54,7 @@ static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5,59+5,59+5,123+5, 230+5, 230+5 uint8_t LMICin866_maxFrameLen(uint8_t dr) { if (dr < LENOF_TABLE(maxFrameLens)) return TABLE_GET_U1(maxFrameLens, dr); - else + else return 0xFF; } @@ -93,8 +93,8 @@ ostime_t LMICin866_dr2hsym(uint8_t dr) { enum { NUM_DEFAULT_CHANNELS = 3 }; static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { // Default operational frequencies - IN866_F1 | BAND_MILLI, - IN866_F2 | BAND_MILLI, + IN866_F1 | BAND_MILLI, + IN866_F2 | BAND_MILLI, IN866_F3 | BAND_MILLI, }; @@ -189,7 +189,7 @@ ostime_t LMICin866_nextTx(ostime_t now) { } } - // no enabled channel found! just use the last channel. + // no enabled channel found! just use the last channel. return now; } diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index cb07b2a8..c1b5e546 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -52,7 +52,7 @@ static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5,59+5,59+5,123+5, 230+5, 230+5 uint8_t LMICkr920_maxFrameLen(uint8_t dr) { if (dr < LENOF_TABLE(maxFrameLens)) return TABLE_GET_U1(maxFrameLens, dr); - else + else return 0xFF; } @@ -89,8 +89,8 @@ ostime_t LMICkr920_dr2hsym(uint8_t dr) { enum { NUM_DEFAULT_CHANNELS = 3 }; static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { // Default operational frequencies - KR920_F1 | BAND_MILLI, - KR920_F2 | BAND_MILLI, + KR920_F1 | BAND_MILLI, + KR920_F2 | BAND_MILLI, KR920_F3 | BAND_MILLI, }; @@ -200,7 +200,7 @@ ostime_t LMICkr920_nextTx(ostime_t now) { } } - // no enabled channel found! just use the last channel. + // no enabled channel found! just use the last channel. return now; } diff --git a/src/lmic/lmic_us_like.h b/src/lmic/lmic_us_like.h index 316a12c8..b0dc88fe 100644 --- a/src/lmic/lmic_us_like.h +++ b/src/lmic/lmic_us_like.h @@ -69,7 +69,7 @@ LMICuslike_isValidBeacon1(const uint8_t *d) { #define LMICbandplan_advanceBeaconChannel() \ do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) -// TODO(tmm@mcci.com): decide whether we want to do this on every +// TODO(tmm@mcci.com): decide whether we want to do this on every // reset or just restore the last sub-band selected by the user. #define LMICbandplan_resetDefaultChannels() \ LMICbandplan_initDefaultChannels(/* normal */ 0) diff --git a/src/lmic/lorabase_us915.h b/src/lmic/lorabase_us915.h index 0a771849..31bc61b1 100644 --- a/src/lmic/lorabase_us915.h +++ b/src/lmic/lorabase_us915.h @@ -73,7 +73,7 @@ enum { US915_FREQ_MAX = 928000000 }; enum { - US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're + US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're // on an 64-channel bandplan. See code // that computes tx power. }; diff --git a/src/lmic/lorawan_spec_compliance.h b/src/lmic/lorawan_spec_compliance.h index 9e03c49f..e479b240 100644 --- a/src/lmic/lorawan_spec_compliance.h +++ b/src/lmic/lorawan_spec_compliance.h @@ -7,7 +7,7 @@ Module: lorawan_spec_compliance.h Copyright notice and license info: See LICENSE file accompanying this project. - + Author: Terry Moore, MCCI Corporation March 2019 From 3f067608d2c59ce5097aa76e4ebce9659b82c567 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 23:12:12 -0400 Subject: [PATCH 26/27] Fix #384: properly parenthesize mask comparisons --- src/lmic/lmic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index 716deca3..af730591 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -741,7 +741,7 @@ applyAdrRequests( LMICbandplan_saveAdrState(&initialState); // compute the changes - if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { for (oidx = 0; oidx < olen; oidx += kAdrReqSize) { // can we advance? if (olen - oidx < kAdrReqSize) { @@ -990,7 +990,7 @@ scan_mac_cmds( ans &= ~MCMD_NewChannelAns_DataRateACK; } - if( ans == MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK) { + if( ans == (MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK)) { if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { LMICOS_logEventUint32("NewChannelReq: setupChannel failed", (MaxDR << 24u) | (MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); ans &= ~MCMD_NewChannelAns_ChannelACK; @@ -1019,7 +1019,7 @@ scan_mac_cmds( ans &= ~MCMD_DlChannelAns_FreqACK; } - if( ans == MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK) { + if( ans == (MCMD_DlChannelAns_FreqACK|MCMD_DlChannelAns_ChannelACK)) { LMIC.channelDlFreq[chidx] = freq; } @@ -1044,7 +1044,7 @@ scan_mac_cmds( if (! validDR(dr)) ans &= ~MCMD_PingSlotFreqAns_DataRateACK; - if (ans == MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK) { + if (ans == (MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK)) { LMIC.ping.freq = freq; LMIC.ping.dr = dr; DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); From 3388653686f80cfe186a38d0ea46ba2a23c8f38f Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Fri, 23 Aug 2019 23:14:22 -0400 Subject: [PATCH 27/27] Fix #379: add CI checks for compliance sketch --- .travis.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6948ade6..62a96b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,7 @@ before_install: # Put one or more arguments into lmic_project_config.h as `#define $i 1\n` or `#define $i $arg` - function _splitdef { if [ "$1" = "${1/=/}" ]; then echo "$1" 1 ; else echo "${1/=/ }" ; fi ; } - function _projcfg { for i in "$@" ; do printf '#define %s %s\n' $(_splitdef "$i") ; done > $PWD/project_config/lmic_project_config.h ; } + - function _projcfg_class_a { for i in "$@" "DISABLE_PING" "DISABLE_BEACONS"; do printf '#define %s %s\n' $(_splitdef "$i") ; done > $PWD/project_config/lmic_project_config.h ; } # # Handy macro to deal with expected failures. - 'function _expect_failure { if [ $? -eq 0 ]; then echo "Suceeded, but should have failed!" ; echo project_config/lmic_project_config.h ; cat $PWD/project_config/lmic_project_config.h ; return 1 ; else echo "Failed, as expected"; return 0 ; fi ; }' @@ -135,6 +136,14 @@ script: - _notesp32 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/ttn-abp/ttn-abp.ino ; } # make sure debug works - _notesp32 || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio LMIC_DEBUG_LEVEL=2 LMIC_PRINTF_TO=Serial && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + # make sure the compliance sketch compiles on AVR in all regions. + - _notesp32 || { _projcfg CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_au921 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notesp32 || { _projcfg CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_esp32opts '' projcfg) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } # # *** Tests for Feather 32u4 @@ -156,6 +165,15 @@ script: - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw-feather/raw-feather.ino ; } + # make sure the compliance sketch compiles on AVR in all regions. This also requires class-A only + - _notavr || { _projcfg_class_a CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_au921 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_as923 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_as923jp CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_kr920 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + - _notavr || { _projcfg_class_a CFG_in866 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino ; } + # test the raw sketch - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw/raw.ino ; } - _notavr || { _projcfg COMPILE_REGRESSION_TEST CFG_eu868 CFG_sx1276_radio && arduino --verify --board $(_avropts) $PWD/examples/raw/raw.ino ; } @@ -213,8 +231,8 @@ script: - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_au921 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_as923jp CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } - - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } - - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_kr920 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } + - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_in866 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-otaa/ttn-otaa.ino ; } # # test ttn-abp with all regions - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board mcci:samd:mcci_catena_4450:lorawan_region=projcfg $PWD/examples/ttn-abp/ttn-abp.ino ; } @@ -264,6 +282,9 @@ script: - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4460' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } - _notsamd || { _projcfg COMPILE_REGRESSION_TEST CFG_us915 CFG_sx1276_radio && arduino --verify --board $(_samdopts 'mcci_catena_4470' projcfg) $PWD/examples/ttn-otaa-halconfig-us915/ttn-otaa-halconfig-us915.ino ; } + # make sure the compliance sketch compiles on SAMD in EU region. + - _notsamd || arduino --verify --board $(_samdopts 'mcci_catena_4450' eu868 ) $PWD/examples/compliance-otaa-halconfig/compliance-otaa-halconfig.ino + # # some tests that should generate build failures. #