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. # diff --git a/README.md b/README.md index 016e2b61..be5ed6f1 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) @@ -314,14 +315,13 @@ 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 -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 -`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 @@ -335,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. @@ -1092,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. 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 6428e368..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);} @@ -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 }; /* @@ -95,6 +87,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 +111,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 +121,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 +139,39 @@ public: private: unsigned m_head, m_tail; - eventnode_t m_queue[16]; + eventnode_t m_queue[32]; osjob_t m_job; }; cEventQueue eventQueue; +#if LMIC_ENABLE_event_logging +extern "C" { + void LMICOS_logEvent(const char *pMessage); + void LMICOS_logEventUint32(const char *pMessage, uint32_t datum); +} + +void LMICOS_logEvent(const char *pMessage) + { + eventQueue.putEvent(ev_t(-1), pMessage); + } + +void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) + { + eventQueue.putEvent(ev_t(-2), pMessage, 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; @@ -203,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); @@ -218,156 +242,210 @@ void printRps(rps_t rps) { Serial.print(F(")")); } -void eventPrint(cEventQueue::eventnode_t &e) { - ev_t ev = e.event; +void printOpmode(uint16_t opmode, char sep = ',') { + if (sep != 0) + Serial.print(sep); + Serial.print(F(" opmode=")); Serial.print(opmode, HEX); +} - Serial.print(e.time); - Serial.print(F(": ")); - if (ev < sizeof(evNames) / sizeof(evNames[0])) { - Serial.print(evNames[ev]); - } else { - Serial.print(F("Unknown event: ")); - Serial.print((unsigned) ev); - } +void printTxend(ostime_t txend) { + Serial.print(F(", txend=")); Serial.print(txend); +} - 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; +void printTxChnl(u1_t txChnl) { + Serial.print(F(": ch=")); + Serial.print(unsigned(txChnl)); +} - 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> 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); @@ -541,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/config.h b/src/lmic/config.h index 32e30a92..29ffec52 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -116,12 +116,14 @@ // 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 +//#define DISABLE_MCMD_DlChannelReq // set downlink channel for RX1 for given uplink channel. +//#define DISABLE_MCMD_RXTimingSetupReq // delay between TX and RX // 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 @@ -190,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. But normal operation doesn't need this. +#if !defined(LMIC_ENABLE_event_logging) +# define LMIC_ENABLE_event_logging 0 /* PARAM */ +#endif + #endif // _lmic_config_h_ diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index f2f0e690..af730591 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -38,7 +38,6 @@ DEFINE_LMIC; - // Fwd decls. static void reportEventNoUpdate(ev_t); static void reportEventAndUpdate(ev_t); @@ -51,6 +50,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); @@ -60,6 +60,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); } @@ -387,9 +388,10 @@ ostime_t LMICcore_rndDelay (u1_t secSpan) { return delay; } - +// delay reftime ticks, plus a random intervael in [0..secSpan). static void txDelay (ostime_t reftime, u1_t secSpan) { - reftime += LMICcore_rndDelay(secSpan); + if (secSpan != 0) + reftime += LMICcore_rndDelay(secSpan); if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) { LMIC.globalDutyAvail = reftime; LMIC.opmode |= OP_RNDTX; @@ -591,20 +593,18 @@ 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.lastDnConf = LMIC.adrChanged = 0; + LMIC.upRepeatCount = LMIC.upRepeat = 0; +#if !defined(DISABLE_MCMD_RXParamSetupReq) LMIC.dn2Ans = 0; #endif - LMIC.moreData = 0; -#if !defined(DISABLE_MCMD_DCAP_REQ) - LMIC.dutyCapAns = 0; +#if !defined(DISABLE_MCMD_RXTimingSetupReq) + LMIC.macRxTimingSetupAns = 0; #endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - LMIC.pingSetAns = 0; +#if !defined(DISABLE_MCMD_DlChannelReq) && CFG_LMIC_EU_like + LMIC.macDlChannelAns = 0; #endif + LMIC.moreData = 0; LMIC.upRepeat = 0; resetJoinParams(); #if !defined(DISABLE_BEACONS) @@ -653,6 +653,50 @@ static int decodeBeacon (void) { } #endif // !DISABLE_BEACONS +// 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) { + if (LMIC.pendMacPiggyback) { + // put in pendMacData + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } else { + // put in pendTxData + if (LMIC.pendMacLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } +} + +static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t 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) { + 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)[] = { /* 2: LinkCheckAns */ 3, /* 3: LinkADRReq */ 5, @@ -667,7 +711,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 }; @@ -680,141 +724,242 @@ 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 + 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; - - LMICbandplan_saveAdrState(&initialState); - - for (oidx = 0; oidx < olen; ) { - u1_t const cmd = opts[oidx]; - - if (cmd == MCMD_LADR_REQ) { - u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels - - p1 = opts[oidx+1]; // txpow + DR, in case last - p4 = opts[oidx+4]; - u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK; // channel page - - LMICbandplan_mapChannels(chpage, chmap); + lmic_saved_adr_state_t initialState; + int const kAdrReqSize = 5; + int oidx; + u1_t p1 = 0; + u1_t p4 = 0; + bit_t response_fit = 1; + bit_t map_ok = 1; + + LMICbandplan_saveAdrState(&initialState); + + // 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 - int cmdlen = getMacCmdSize(cmd); + 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 - // this really is an assert, we should never here - // unless all the commands are valid. - ASSERT(cmdlen != 0); + map_ok = LMICbandplan_mapChannels(chpage, chmap); + LMICOS_logEventUint32("applyAdrRequests: mapChannels", (chpage << 16)|(chmap << 0)); + } + } - oidx += cmdlen; + 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); + // all done scanning options + bit_t changes = LMICbandplan_compareAdrState(&initialState); + // handle the final options + if (adrAns == (MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK)) { // 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; } - // handle power changes - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + dr_t dr = (dr_t)(p1>>MCMD_LinkADRReq_DR_SHIFT); - LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + LMICOS_logEventUint32("applyAdrRequests: setDrTxPow", (adrAns << 16)|(dr << 8)|(p1 << 0)); + + // handle power changes here, too. + changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); } + + // 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; } +static int +scan_mac_cmds_link_adr( + const char *opts, + int olen, + bit_t *presponse_fit + ) + { + LMICOS_logEventUint32("scan_mac_cmds_link_adr", 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_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; + + if( !validDR(dr) ) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + } + if (pow2dBm(p1) == -128) { + adrAns &= ~MCMD_LinkADRAns_PowerACK; + } + + oidx += kAdrReqSize; + if (opts[oidx] != MCMD_LinkADRReq) + break; + } + + // go back and apply the ADR changes, if any -- use the effective length, + // and process. + *presponse_fit = applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); + + return lastOidx; + } + // scan mac commands starting at opts[] for olen, return count of bytes consumed. +// 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, - 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; + LMIC.pendMacLen = 0; + if (port == 0) + LMIC.pendMacPiggyback = 0; + else + LMIC.pendMacPiggyback = 1; + while( oidx < olen ) { + bit_t response_fit; + + 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_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, &response_fit); break; } - case MCMD_DEVS_REQ: { - LMIC.devsAns = 1; - // LMIC.snr is SNR time 4, convert to real SNR; rounding towards zero. + + case MCMD_DevStatusReq: { + // 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)); + + response_fit = 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 == (0xC0|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 */ + 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_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 +967,97 @@ scan_mac_cmds( LMIC.globalDutyRate = cap & 0xF; LMIC.globalDutyAvail = os_getTime(); DO_DEVDB(cap,dutyCap); - LMIC.dutyCapAns = 1; -#endif // !DISABLE_MCMD_DCAP_REQ + + response_fit = 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) && CFG_LMIC_EU_like u1_t chidx = opts[oidx+1]; // channel + 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 - 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_f_not_zero) { + 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) ) { + LMICOS_logEventUint32("NewChannelReq: setupChannel failed", (MaxDR << 24u) | (MinDR << 16u) | (raw_f_not_zero << 8) | (chidx << 0)); + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + } + + response_fit = 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_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_f_not_zero = 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_f_not_zero) { + 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 + response_fit = 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 +1067,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 +1081,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; + response_fit = 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. @@ -933,21 +1139,14 @@ 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; - } - - oidx += cmdlen; + /* if we're out of spce for responses, skip to end. */ + if (! response_fit) { + olen = oidx; + } else { + oidx += cmdlen; + } } /* end while */ - // go back and apply the ADR changes, if any -- use the effective length - if (fSawAdrReq) - applyAdrRequests(opts, olen); - return oidx; } @@ -966,6 +1165,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) + LMICOS_logEventUint32("decodeFrame", (dlen << 8) | (hdr << 0)); if( dlen < OFF_DAT_OPTS+4 || (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || @@ -993,6 +1194,8 @@ static bit_t decodeFrame (void) { int pend = dlen-4; // MIC if( addr != LMIC.devaddr ) { + LMICOS_logEventUint32("decodeFrame: wrong address", addr); + EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, e_.eui = MAIN::CDEV->getEui(), e_.info = addr, @@ -1000,6 +1203,7 @@ static bit_t decodeFrame (void) { goto norx; } if( 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))); @@ -1012,9 +1216,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) ) { + 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]), @@ -1028,20 +1244,29 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = LMIC.seqnoDn, e_.info2 = seqno)); + LMICOS_logEventUint32("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)); + 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. + 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) { + LMICOS_logEventUint32("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(), @@ -1051,7 +1276,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) + LMICOS_logEventUint32("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) ) @@ -1060,19 +1296,26 @@ static bit_t decodeFrame (void) { // We heard from network 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 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); + 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 +1333,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( @@ -1100,6 +1343,7 @@ static bit_t decodeFrame (void) { ); #endif } + // wait to transmit until txcomplete: above. } } // end decrypt payload EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), @@ -1118,6 +1362,9 @@ static bit_t decodeFrame (void) { e_.eui = MAIN::CDEV->getEui(), e_.info = Base::lsbf4(&d[pend]), e_.info2 = seqno)); + // discard the data + LMICOS_logEventUint32("decodeFrame: discarding replay", (seqno << 16) | (LMIC.lastDnConf << 8) | (ftype << 0)); + goto norx; } if( // NWK acks but we don't have a frame pending @@ -1131,7 +1378,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 } @@ -1226,8 +1473,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. @@ -1335,6 +1584,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. @@ -1522,41 +1772,64 @@ 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; + + if (LMIC.pendTxPort != 0 && LMIC.pendMacPiggyback && LMIC.pendMacLen != 0) { + os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); + end += LMIC.pendMacLen; } -#endif // !DISABLE_PING -#if !defined(DISABLE_MCMD_DCAP_REQ) - if( LMIC.dutyCapAns ) { - LMIC.frame[end] = MCMD_DCAP_ANS; + 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; + } else { + LMIC.frame[end + 0] = MCMD_RXParamSetupAns; + LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU; + end += 2; + } + } +#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_ENABLE_DeviceTimeReq + if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { + LMIC.frame[end+0] = MCMD_DeviceTimeReq; 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; + LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx; } -#if !defined(DISABLE_BEACONS) - if( LMIC.bcninfoTries > 0 ) { - LMIC.frame[end] = MCMD_BCNI_REQ; +#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 // !DISABLE_BEACONS +#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 @@ -1565,45 +1838,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; - end += 1; - LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx; - } -#endif // LMIC_ENABLE_DeviceTimeReq - ASSERT(end <= OFF_DAT_OPTS+16); u1_t flen = end + (txdata ? 5+dlen : 4); if( flen > MAX_LEN_FRAME ) { @@ -1613,14 +1847,15 @@ 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); - if( LMIC.txCnt == 0 ) { + if( LMIC.txCnt == 0 && LMIC.upRepeatCount == 0 ) { LMIC.seqnoUp += 1; DO_DEVDB(LMIC.seqnoUp,seqnoUp); } else { + 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, @@ -1638,6 +1873,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); @@ -1716,7 +1956,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_BeaconInfoReq can be attached. if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) startScan(); return 1; // enabled @@ -1870,108 +2110,211 @@ 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. +// 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 ) { - norx: - if( LMIC.txCnt != 0 ) { - if( LMIC.txCnt < TXCONF_ATTEMPTS ) { - LMIC.txCnt += 1; - 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; - } - initTxrxFlags(__func__, TXRX_NACK | TXRX_NOPORT); + // if this is an RX1 window, shouldn't we return 0 to schedule + // 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(); + } + // 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 { - // Nothing received - implies no port - initTxrxFlags(__func__, TXRX_NOPORT); + // 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"); } - setAdrAckCount(LMIC.adrAckReq + 1); - LMIC.dataBeg = LMIC.dataLen = 0; - txcomplete: - LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + } + + // 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 - 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); - 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_BCNI_REQ 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( !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); @@ -2092,7 +2435,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 @@ -2128,6 +2470,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 } @@ -2322,15 +2665,22 @@ void LMIC_clrTxData (void) { void LMIC_setTxData (void) { + 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; // 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(); } // 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 b9764da4..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) @@ -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 // ========================================================================== @@ -254,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, @@ -399,6 +424,10 @@ 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; #elif CFG_LMIC_US_like @@ -445,35 +474,33 @@ 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; u1_t pendTxConf; // confirmed data - u1_t pendTxLen; // +0x80 = confirmed + u1_t pendTxLen; // count of bytes in pendTxData. 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? + // 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 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) - 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,17 +513,20 @@ 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 +#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) 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..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) { @@ -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 @@ -127,18 +126,23 @@ 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 -// the maxeirp setting. +// 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) { - 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_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(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(). @@ -162,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, }; @@ -171,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)); @@ -217,6 +224,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) { @@ -226,10 +242,13 @@ 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; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } @@ -259,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) { @@ -267,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 a6d0fdbc..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) { @@ -66,24 +66,31 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr) { return 0xFF; } +int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) + return -128; + else + return ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1))); +} + 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_bandplan.h b/src/lmic/lmic_bandplan.h index 63836c5b..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 // @@ -169,6 +173,16 @@ #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 + +// 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_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_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_eu868.c b/src/lmic/lmic_eu868.c index e47082b2..de700ecb 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_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -93,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)); @@ -130,6 +138,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 +163,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; } @@ -227,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..00acbe92 100644 --- a/src/lmic/lmic_eu_like.c +++ b/src/lmic/lmic_eu_like.c @@ -56,21 +56,56 @@ 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; chnlchannelFreq, - 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) { @@ -184,4 +219,20 @@ 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]; + 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..16f2679e 100644 --- a/src/lmic/lmic_eu_like.h +++ b/src/lmic/lmic_eu_like.h @@ -101,4 +101,10 @@ 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); + #endif // _lmic_eu_like_h_ diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 1788ca4d..455e7dee 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -54,16 +54,21 @@ 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; } 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_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -88,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, }; @@ -98,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)); @@ -125,17 +133,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; } @@ -169,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; } @@ -201,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 468f9773..c1b5e546 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -52,16 +52,21 @@ 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; } 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_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT; + if (pindex < LENOF_TABLE(TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -84,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, }; @@ -94,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)); @@ -136,17 +144,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; } @@ -180,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; } @@ -212,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/lmic_us915.c b/src/lmic/lmic_us915.c index 1d524d47..be1313e4 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_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK) + return -128; + else + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LinkADRReq_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/lmic_us_like.c b/src/lmic/lmic_us_like.c index 9ec4e612..90d8c92b 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. @@ -130,45 +130,46 @@ 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_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. */ 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) { - 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_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) { if (en125) - result |= LMIC_enableChannel(chnl); + LMIC_enableChannel(chnl); else - result |= LMIC_disableChannel(chnl); + LMIC_disableChannel(chnl); } // then apply mask to top 8 channels. @@ -180,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 @@ -287,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..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) @@ -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_ diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 7b1525a6..c1ec1cdc 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -475,16 +475,21 @@ 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 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,78 @@ 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_DlChannelAns = 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_BeaconInfoReq = 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_DlChannelReq = 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_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_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_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_NewChannelAns_RFU = 0xFC, // RFU bits + MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency }; 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_RXTimingSetupReq_RFU = 0xF0, // RFU bits + MCMD_RXTimingSetupReq_Delay = 0x0F, // delay in secs, 1..15; 0 is mapped to 1. }; 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_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_PING_ANS_RFU = 0xFE, - MCMD_PING_ANS_FQACK = 0x01 + MCMD_PingSlotFreqAns_RFU = 0xFC, + MCMD_PingSlotFreqAns_DataRateACK = 0x02, + MCMD_PingSlotFreqAns_ChannelACK = 0x01, }; enum { @@ -561,65 +579,30 @@ 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. - 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 -}; -// Bit fields byte#0 of MCMD_LADR_REQ payload + 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 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 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_ diff --git a/src/lmic/radio.c b/src/lmic/radio.c index a7f3db21..0451d88f 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