diff --git a/LICENSE b/LICENSE index 9288a85b..8a3b2470 100644 --- a/LICENSE +++ b/LICENSE @@ -20,9 +20,9 @@ Parts of the source files in this repository are made available under different listed below. Refer to each individual source file for more details. ------------------------------------------------------------------------------------------------ -wifiscan.cpp +wifiscan.cpp and macsniff.cpp: -Prior art was used for wifiscan.cpp and taken from +Parts were derived or taken from * Copyright (c) 2017, Łukasz Marcin Podkalicki * ESP32/016 WiFi Sniffer @@ -208,10 +208,9 @@ under this License: END OF TERMS AND CONDITIONS" ------------------------------------------------------------------------------------------------ -src/lorawan.cpp and /lib/arduino-lmic-1.5.0-<...> +lorawan.cpp -Parts of lorawan.cpp, and the arduino lmic library, which is included in the /lib directory of this -repository, were derived or taken from +Parts of lorawan.cpp, and the arduino lmic library were derived or taken from Arduino-LMIC Library TTN OTAA Example @@ -222,10 +221,32 @@ and it's fork LoraWAN-in-C library, adapted to run under the Arduino environment https://github.com/jpmeijers/arduino-lmic -under this Licence: +under this MIT Licence: + +"MIT License + +Copyright (C) 2014-2016 IBM Corporation +Copyright (c) 2016-2018 MCCI Corporation +Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE." -"License -Most source files in this repository are made available under the Eclipse Public License v1.0. The examples which use a more liberal license. Some of the AES code is available under the LGPL. Refer to each individual source file for more details." ------------------------------------------------------------------------------------------------ blescan.cpp @@ -415,4 +436,4 @@ under this Licence: END OF TERMS AND CONDITIONS" ------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------ \ No newline at end of file diff --git a/README.md b/README.md index 726e35f6..8907c7b7 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,9 @@ Hardware dependent settings (pinout etc.) are stored in board files in /hal dire Before compiling the code, -- **edit paxcounter.conf** and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. +- **edit src/paxcounter.conf** and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. + +- **edit src/lmic_config.h** and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. - **create file loraconf.h in your local /src directory** using the template [loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and populate it with your personal APPEUI und APPKEY for the LoRaWAN network. If you're using popular TheThingsNetwork you can copy&paste the keys from TTN console or output of ttnctl. @@ -336,3 +338,4 @@ Thanks to - [Oliver Brandmüller](https://github.com/spmrider) for idea and initial setup of this project - [Charles Hallard](https://github.com/hallard) for major code contributions to this project - [robbi5](https://github.com/robbi5) for the payload converter +- [terrillmoore](https://github.com/mcci-catena) for maintaining the LMIC for arduino LoRaWAN stack diff --git a/include/globals.h b/include/globals.h index 947b4cfa..78df4a95 100644 --- a/include/globals.h +++ b/include/globals.h @@ -53,7 +53,7 @@ extern std::array beacons; extern TaskHandle_t irqHandlerTask, wifiSwitchTask; #ifdef HAS_GPS -#include "gps.h" +#include "gpsread.h" #endif #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) @@ -67,7 +67,7 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask; #endif #ifdef HAS_SPI -#include "spi.h" +#include "spisend.h" #endif #ifdef HAS_DISPLAY diff --git a/include/gps.h b/include/gpsread.h similarity index 92% rename from include/gps.h rename to include/gpsread.h index 88ca24d3..6f5ed584 100644 --- a/include/gps.h +++ b/include/gpsread.h @@ -1,5 +1,5 @@ -#ifndef _GPS_H -#define _GPS_H +#ifndef _GPSREAD_H +#define _GPSREAD_H #include // library for parsing NMEA data #include diff --git a/include/ota.h b/include/ota.h index 560ee144..14f63eb0 100644 --- a/include/ota.h +++ b/include/ota.h @@ -17,7 +17,7 @@ int version_compare(const String v1, const String v2); void display(const uint8_t row, const std::string status, const std::string msg); #ifdef HAS_DISPLAY -void show_progress(size_t current, size_t size); +void show_progress(unsigned long current, unsigned long size); #endif #endif // USE_OTA diff --git a/include/spi.h b/include/spisend.h similarity index 69% rename from include/spi.h rename to include/spisend.h index b258a2ce..750a3c20 100644 --- a/include/spi.h +++ b/include/spisend.h @@ -1,5 +1,5 @@ -#ifndef _SPI_H -#define _SPI_H +#ifndef _SPISEND_H +#define _SPISEND_H extern TaskHandle_t SpiTask; extern QueueHandle_t SPISendQueue; diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/README.md b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/README.md deleted file mode 100644 index 69d800f5..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/README.md +++ /dev/null @@ -1,365 +0,0 @@ -Arduino-LMIC library -==================== -This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly -modified to run in the Arduino environment, allowing using the SX1272, -SX1276 tranceivers and compatible modules (such as some HopeRF RFM9x -modules). - -This library mostly exposes the functions defined by LMIC, it makes no -attempt to wrap them in a higher level API that is more in the Arduino -style. To find out how to use the library itself, see the examples, or -see the PDF file in the doc subdirectory. - -This library requires Arduino IDE version 1.6.6 or above, since it -requires C99 mode to be enabled by default. - -Installing ----------- -To install this library: - - - install it using the Arduino Library manager ("Sketch" -> "Include - Library" -> "Manage Libraries..."), or - - download a zipfile from github using the "Download ZIP" button and - install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP - Library..." - - clone this git repository into your sketchbook/libraries folder. - -For more info, see https://www.arduino.cc/en/Guide/Libraries - -Features --------- -The LMIC library provides a fairly complete LoRaWAN Class A and Class B -implementation, supporting the EU-868 and US-915 bands. Only a limited -number of features was tested using this port on Arduino hardware, so be -careful when using any of the untested features. - -What certainly works: - - Sending packets uplink, taking into account duty cycling. - - Encryption and message integrity checking. - - Receiving downlink packets in the RX2 window. - - Custom frequencies and datarate settings. - - Over-the-air activation (OTAA / joining). - -What has not been tested: - - Receiving downlink packets in the RX1 window. - - Receiving and processing MAC commands. - - Class B operation. - -If you try one of these untested features and it works, be sure to let -us know (creating a github issue is probably the best way for that). - -Configuration -------------- -A number of features can be configured or disabled by editing the -`config.h` file in the library folder. Unfortunately the Arduino -environment does not offer any way to do this (compile-time) -configuration from the sketch, so be careful to recheck your -configuration when you switch between sketches or update the library. - -At the very least, you should set the right type of transceiver (SX1272 -vs SX1276) in config.h, most other values should be fine at their -defaults. - -Supported hardware ------------------- -This library is intended to be used with plain LoRa transceivers, -connecting to them using SPI. In particular, the SX1272 and SX1276 -families are supported (which should include SX1273, SX1277, SX1278 and -SX1279 which only differ in the available frequencies, bandwidths and -spreading factors). It has been tested with both SX1272 and SX1276 -chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 -and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip -respectively). - -This library contains a full LoRaWAN stack and is intended to drive -these Transceivers directly. It is *not* intended to be used with -full-stack devices like the Microchip RN2483 and the Embit LR1272E. -These contain a transceiver and microcontroller that implements the -LoRaWAN stack and exposes a high-level serial interface instead of the -low-level SPI transceiver interface. - -This library is intended to be used inside the Arduino environment. It -should be architecture-independent, so it should run on "normal" AVR -arduinos, but also on the ARM-based ones, and some success has been seen -running on the ESP8266 board as well. It was tested on the Arduino Uno, -Pinoccio Scout, Teensy LC and 3.x, ESP8266, Arduino 101. - -This library an be quite heavy, especially if the fairly small ATmega -328p (such as in the Arduino Uno) is used. In the default configuration, -the available 32K flash space is nearly filled up (this includes some -debug output overhead, though). By disabling some features in `config.h` -(like beacon tracking and ping slots, which are not typically needed), -some space can be freed up. Some work is underway to replace the AES -encryption implementation, which should free up another 8K or so of -flash in the future, making this library feasible to run on a 328p -microcontroller. - -Connections ------------ -To make this library work, your Arduino (or whatever Arduino-compatible -board you are using) should be connected to the transceiver. The exact -connections are a bit dependent on the transceiver board and Arduino -used, so this section tries to explain what each connection is for and -in what cases it is (not) required. - -Note that the SX1272 module runs at 3.3V and likely does not like 5V on -its pins (though the datasheet is not say anything about this, and my -transceiver did not obviously break after accidentally using 5V I/O for -a few hours). To be safe, make sure to use a level shifter, or an -Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in -series with all data lines that might prevent damage, but I would not -count on that. - -### Power -The SX127x transceivers need a supply voltage between 1.8V and 3.9V. -Using a 3.3V supply is typical. Some modules have a single power pin -(like the HopeRF modules, labeled 3.3V) but others expose multiple power -pins for different parts (like the Semtech evaluation board that has -`VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together. -Any *GND* pins need to be connected to the Arduino *GND* pin(s). - -### SPI -The primary way of communicating with the transceiver is through SPI -(Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and -SS. The former three need to be directly connected: so MOSI to MOSI, -MISO to MISO, SCK to SCK. Where these pins are located on your Arduino -varies, see for example the "Connections" section of the [Arduino SPI -documentation](SPI). - -The SS (slave select) connection is a bit more flexible. On the SPI -slave side (the transceiver), this must be connect to the pin -(typically) labeled *NSS*. On the SPI master (Arduino) side, this pin -can connect to any I/O pin. Most Arduinos also have a pin labeled "SS", -but this is only relevant when the Arduino works as an SPI slave, which -is not the case here. Whatever pin you pick, you need to tell the -library what pin you used through the pin mapping (see below). - -[SPI]: https://www.arduino.cc/en/Reference/SPI - -### DIO pins -The DIO (digitial I/O) pins on the transceiver board can be configured -for various functions. The LMIC library uses them to get instant status -information from the transceiver. For example, when a LoRa transmission -starts, the DIO0 pin is configured as a TxDone output. When the -transmission is complete, the DIO0 pin is made high by the transceiver, -which can be detected by the LMIC library. - -The LMIC library needs only access to DIO0, DIO1 and DIO2, the other -DIOx pins can be left disconnected. On the Arduino side, they can -connect to any I/O pin, since the current implementation does not use -interrupts or other special hardware features (though this might be -added in the feature, see also the "Timing" section). - -In LoRa mode the DIO pins are used as follows: - * DIO0: TxDone and RxDone - * DIO1: RxTimeout - -In FSK mode they are used as follows:: - * DIO0: PayloadReady and PacketSent - * DIO2: TimeOut - -Both modes need only 2 pins, but the tranceiver does not allow mapping -them in such a way that all needed interrupts map to the same 2 pins. -So, if both LoRa and FSK modes are used, all three pins must be -connected. - -The pins used on the Arduino side should be configured in the pin -mapping in your sketch (see below). - -### Reset -The transceiver has a reset pin that can be used to explicitely reset -it. The LMIC library uses this to ensure the chip is in a consistent -state at startup. In practice, this pin can be left disconnected, since -the transceiver will already be in a sane state on power-on, but -connecting it might prevent problems in some cases. - -On the Arduino side, any I/O pin can be used. The pin number used must -be configured in the pin mapping (see below). - -### RXTX -The transceiver contains two separate antenna connections: One for RX -and one for TX. A typical transceiver board contains an antenna switch -chip, that allows switching a single antenna between these RX and TX -connections. Such a antenna switcher can typically be told what -position it should be through an input pin, often labeled *RXTX*. - -The easiest way to control the antenna switch is to use the *RXTX* pin -on the SX127x transceiver. This pin is automatically set high during TX -and low during RX. For example, the HopeRF boards seem to have this -connection in place, so they do not expose any *RXTX* pins and the pin -can be marked as unused in the pin mapping. - -Some boards do expose the antenna switcher pin, and sometimes also the -SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the -former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these -together with a jumper wire is the easiest solution. - -Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be -configured to control the antenna switch. Connect the antenna switch -control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O -pin on the Arduino side, and configure the pin used in the pin map (see -below). It is not entirely clear why would *not* want the transceiver to -control the antenna directly, though. - -### Pin mapping -As described above, most connections can use arbitrary I/O pins on the -Arduino side. To tell the LMIC library about these, a pin mapping struct -is used in the sketch file. - -For example, this could look like this: - - lmic_pinmap lmic_pins = { - .nss = 6, - .rxtx = LMIC_UNUSED_PIN, - .rst = 5, - .dio = {2, 3, 4}, - }; - -The names refer to the pins on the transceiver side, the numbers refer -to the Arduino pin numbers (to use the analog pins, use constants like -`A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2 -respectively. Any pins that are not needed should be specified as -`LMIC_UNUSED_PIN`. The nss and dio0 pin is required, the others can -potentially left out (depending on the environments and requirements, -see the notes above for when a pin can or cannot be left out). - -The name of this struct must always be `lmic_pins`, which is a special name -recognized by the library. - -#### LoRa Nexus by Ideetron -This board uses the following pin mapping: - - const lmic_pinmap lmic_pins = { - .nss = 10, - .rxtx = LMIC_UNUSED_PIN, - .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET - .dio = {4, 5, 7}, - }; - -Examples --------- -This library currently provides three examples: - - - `ttn-abp.ino` shows a basic transmission of a "Hello, world!" message - using the LoRaWAN protocol. It contains some frequency settings and - encryption keys intended for use with The Things Network, but these - also correspond to the default settings of most gateways, so it - should work with other networks and gateways as well. This example - uses activation-by-personalization (ABP, preconfiguring a device - address and encryption keys), and does not employ over-the-air - activation. - - Reception of packets (in response to transmission, using the RX1 and - RX2 receive windows is also supported). - - - `ttn-otaa.ino` also sends a "Hello, world!" message, but uses over - the air activation (OTAA) to first join a network to establish a - session and security keys. This was tested with The Things Network, - but should also work (perhaps with some changes) for other networks. - - - `raw.ino` shows how to access the radio on a somewhat low level, - and allows to send raw (non-LoRaWAN) packets between nodes directly. - This is useful to verify basic connectivity, and when no gateway is - available, but this example also bypasses duty cycle checks, so be - careful when changing the settings. - -Timing ------- -Unfortunately, the SX127x tranceivers do not support accurate -timekeeping themselves (there is a sequencer that is *almost* sufficient -for timing the RX1 and RX2 downlink windows, but that is only available -in FSK mode, not in LoRa mode). This means that the microcontroller is -responsible for keeping track of time. In particular, it should note -when a packet finished transmitting, so it can open up the RX1 and RX2 -receive windows at a fixed time after the end of transmission. - -This timing uses the Arduino `micros()` timer, which has a granularity -of 4μs and is based on the primary microcontroller clock. For timing -events, the tranceiver uses its DIOx pins as interrupt outputs. In the -current implementation, these pins are not handled by an actual -interrupt handler, but they are just polled once every LMIC loop, -resulting in a bit inaccuracy in the timestamping. Also, running -scheduled jobs (such as opening up the receive windows) is done using a -polling approach, which might also result in further delays. - -Fortunately, LoRa is a fairly slow protocol and the timing of the -receive windows is not super critical. To synchronize transmitter and -receiver, a preamble is first transmitted. Using LoRaWAN, this preamble -consists of 8 symbols, of which the receiver needs to see 4 symbols to -lock on. The current implementation tries to enable the receiver for 5 -symbol times at 1.5 symbol after the start of the receive window, -meaning that a inacurracy of plus or minus 2.5 symbol times should be -acceptable. - -At the fastest LoRa setting supported by the tranceiver (SF5BW500) a -single preamble symbol takes 64μs, so the receive window timing should -be accurate within 160μs (for LoRaWAN this is SF7BW250, needing accuracy -within 1280μs). This is certainly within a crystal's accuracy, but using -the internal oscillator is probably not feasible (which is 1% - 10% -accurate, depending on calibration). This accuracy should also be -feasible with the polling approach used, provided that the LMIC loop is -run often enough. - -It would be good to properly review this code at some point, since it -seems that in some places some offsets and corrections are applied that -might not be appropriate for the Arduino environment. So if reception is -not working, the timing is something to have a closer look at. - -The LMIC library was intended to connect the DIO pins to interrupt -lines and run code inside the interrupt handler. However, doing this -opens up an entire can of worms with regard to doing SPI transfers -inside interrupt routines (some of which is solved by the Arduino -`beginTransaction()` API, but possibly not everything). One simpler -alternative could be to use an interrupt handler to just store a -timestamp, and then do the actual handling in the main loop (this -requires modifications of the library to pass a timestamp to the LMIC -`radio_irq_handler()` function). - -An even more accurate solution could be to use a dedicated timer with an -input capture unit, that can store the timestamp of a change on the DIO0 -pin (the only one that is timing-critical) entirely in hardware. -Unfortunately, timer0, as used by Arduino's `millis()` and `micros()` -functions does not seem to have an input capture unit, meaning a -separate timer is needed for this. - -If the main microcontroller does not have a crystal, but uses the -internal oscillator, the clock output of the transceiver (on DIO5) could -be usable to drive this timer instead of the main microcontroller clock, -to ensure the receive window timing is sufficiently accurate. Ideally, -this would use timer2, which supports asynchronous mode (e.g. running -while the microcontroller is sleeping), but that timer does not have an -input capture unit. Timer1 has one, but it seems it will stop running -once the microcontroller sleeps. Running the microcontroller in idle -mode with a slower clock might be feasible, though. Instead of using the -main crystal oscillator of the transceiver, it could be possible to use -the transceiver's internal RC oscillator (which is calibrated against -the transceiver crystal), or to calibrate the microcontroller internal -RC oscillator using the transceiver's clkout. However, that datasheet is -a bit vague on the RC oscillator's accuracy and how to use it exactly -(some registers seem to be FSK-mode only), so this needs some -experiments. - -Downlink datarate ------------------ -Note that the datarate used for downlink packets in the RX2 window -defaults to SF12BW125 according to the specification, but some networks -use different values (iot.semtech.com and The Things Network both use -SF9BW). When using personalized activate (ABP), it is your -responsibility to set the right settings, e.g. by adding this to your -sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does -this. - - LMIC.dn2Dr = DR_SF9; - -When using OTAA, the network communicates the RX2 settings in the -join accept message, but the LMIC library does not currently process -these settings. Until that is solved (see issue #20), you should -manually set the RX2 rate, *after* joining (see the handling of -`EV_JOINED` in the `ttn-otaa.ino` for an example. - -License -------- -Most source files in this repository are made available under the -Eclipse Public License v1.0. The examples which use a more liberal -license. Some of the AES code is available under the LGPL. Refer to each -individual source file for more details. diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/LMiC-v1.5.pdf b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/LMiC-v1.5.pdf deleted file mode 100644 index b14bd693..00000000 Binary files a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/LMiC-v1.5.pdf and /dev/null differ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/library.properties b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/library.properties deleted file mode 100644 index ea924c4e..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=IBM LMIC framework -version=1.5.0+arduino-2 -author=IBM -maintainer=Matthijs Kooijman -sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. -paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers -category=Communication -url=http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html -architectures=* diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.h b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.h deleted file mode 100644 index a7d81d79..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.h +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Matthijs Kooijman - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * This the HAL to run LMIC on top of the Arduino environment. - *******************************************************************************/ -#ifndef _hal_hal_h_ -#define _hal_hal_h_ - -static const int NUM_DIO = 3; - -#if defined(ESP32) || defined(NRF51) -#define LMIC_SPI_PINS_IN_MAPPING - struct lmic_pinmap { - u1_t mosi; - u1_t miso; - u1_t sck; - u1_t nss; - u1_t rxtx; - u1_t rst; - u1_t dio[NUM_DIO]; - }; -#else - struct lmic_pinmap { - u1_t nss; - u1_t rxtx; - u1_t rst; - u1_t dio[NUM_DIO]; - }; -#endif - -// Use this for any unused pins. -const u1_t LMIC_UNUSED_PIN = 0xff; - -// Declared here, to be defined an initialized by the application -extern const lmic_pinmap lmic_pins; - -#endif // _hal_hal_h_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/hal.h b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/hal.h deleted file mode 100644 index 7f2a9f46..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/hal.h +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -#ifndef _hal_hpp_ -#define _hal_hpp_ - -#ifdef __cplusplus -extern "C"{ -#endif - -/* - * initialize hardware (IO, SPI, TIMER, IRQ). - */ -void hal_init (void); - -/* - * drive radio NSS pin (0=low, 1=high). - */ -void hal_pin_nss (u1_t val); - -/* - * drive radio RX/TX pins (0=rx, 1=tx). - */ -void hal_pin_rxtx (u1_t val); - -/* - * control radio RST pin (0=low, 1=high, 2=floating) - */ -void hal_pin_rst (u1_t val); - -/* - * perform 8-bit SPI transaction with radio. - * - write given byte 'outval' - * - read byte and return value - */ -u1_t hal_spi (u1_t outval); - -/* - * disable all CPU interrupts. - * - might be invoked nested - * - will be followed by matching call to hal_enableIRQs() - */ -void hal_disableIRQs (void); - -/* - * enable CPU interrupts. - */ -void hal_enableIRQs (void); - -/* - * put system and CPU in low-power mode, sleep until interrupt. - */ -void hal_sleep (void); - -/* - * return 32-bit system time in ticks. - */ -u4_t hal_ticks (void); - -/* - * busy-wait until specified timestamp (in ticks) is reached. - */ -void hal_waitUntil (u4_t time); - -/* - * check and rewind timer for target time. - * - return 1 if target time is close - * - otherwise rewind timer for target time or full period and return 0 - */ -u1_t hal_checkTimer (u4_t targettime); - -/* - * perform fatal failure action. - * - called by assertions - * - action could be HALT or reboot - */ -void hal_failed (const char *file, u2_t line); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _hal_hpp_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lorabase.h b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lorabase.h deleted file mode 100644 index f95cfd2d..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lorabase.h +++ /dev/null @@ -1,391 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -#ifndef _lorabase_h_ -#define _lorabase_h_ - -#ifdef __cplusplus -extern "C"{ -#endif - -// ================================================================================ -// BEG: Keep in sync with lorabase.hpp -// - -enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; -enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; -enum _bw_t { BW125=0, BW250, BW500, BWrfu }; -typedef u1_t cr_t; -typedef u1_t sf_t; -typedef u1_t bw_t; -typedef u1_t dr_t; -// Radio parameter set (encodes SF/BW/CR/IH/NOCRC) -typedef u2_t rps_t; -TYPEDEF_xref2rps_t; - -enum { ILLEGAL_RPS = 0xFF }; -enum { DR_PAGE_EU868 = 0x00 }; -enum { DR_PAGE_US915 = 0x10 }; - -// Global maximum frame length -enum { STD_PREAMBLE_LEN = 8 }; -enum { MAX_LEN_FRAME = 64 }; -enum { LEN_DEVNONCE = 2 }; -enum { LEN_ARTNONCE = 3 }; -enum { LEN_NETID = 3 }; -enum { DELAY_JACC1 = 5 }; // in secs -enum { DELAY_DNW1 = 1 }; // in secs down window #1 -enum { DELAY_EXTDNW2 = 1 }; // in secs -enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs -enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 -enum { BCN_INTV_exp = 7 }; -enum { BCN_INTV_sec = 1<> 3) & 0x3); } -inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } -inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } -inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } -inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } -inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } -inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } -inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } -inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { - return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); -} -#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) -// Two frames with params r1/r2 would interfere on air: same SFx + BWx -inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } - -extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; -inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } -inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } -inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } -inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } -inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate -inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate -inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR -inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range -inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps - -// -// BEG: Keep in sync with lorabase.hpp -// ================================================================================ - - -// Convert between dBm values and power codes (MCMD_LADR_XdBm) -s1_t pow2dBm (u1_t mcmd_ladr_p1); -// Calculate airtime -ostime_t calcAirTime (rps_t rps, u1_t plen); -// Sensitivity at given SF/BW -int getSensitivity (rps_t rps); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _lorabase_h_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.c b/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.c deleted file mode 100644 index c0883f1a..00000000 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.c +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -#include "lmic.h" -#include - -// RUNTIME STATE -static struct { - osjob_t* scheduledjobs; - osjob_t* runnablejobs; -} OS; - -void os_init () { - memset(&OS, 0x00, sizeof(OS)); - hal_init(); - radio_init(); - LMIC_init(); -} - -ostime_t os_getTime () { - return hal_ticks(); -} - -static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) { - for( ; *pnext; pnext = &((*pnext)->next)) { - if(*pnext == job) { // unlink - *pnext = job->next; - return 1; - } - } - return 0; -} - -// clear scheduled job -void os_clearCallback (osjob_t* job) { - hal_disableIRQs(); - u1_t res = unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job); - hal_enableIRQs(); - #if LMIC_DEBUG_LEVEL > 1 - if (res) - lmic_printf("%lu: Cleared job %p\n", os_getTime(), job); - #endif -} - -// schedule immediately runnable job -void os_setCallback (osjob_t* job, osjobcb_t cb) { - osjob_t** pnext; - hal_disableIRQs(); - // remove if job was already queued - os_clearCallback(job); - // fill-in job - job->func = cb; - job->next = NULL; - // add to end of run queue - for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); - *pnext = job; - hal_enableIRQs(); - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb); - #endif -} - -// schedule timed job -void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { - osjob_t** pnext; - hal_disableIRQs(); - // remove if job was already queued - os_clearCallback(job); - // fill-in job - job->deadline = time; - job->func = cb; - job->next = NULL; - // insert into schedule - for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { - if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) - // enqueue before next element and stop - job->next = *pnext; - break; - } - } - *pnext = job; - hal_enableIRQs(); - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Scheduled job %p, cb %p at %lu\n", os_getTime(), job, cb, time); - #endif -} - -// execute jobs from timer and from run queue -void os_runloop () { - while(1) { - os_runloop_once(); - } -} - -void os_runloop_once() { - #if LMIC_DEBUG_LEVEL > 1 - bool has_deadline = false; - #endif - osjob_t* j = NULL; - hal_disableIRQs(); - // check for runnable jobs - if(OS.runnablejobs) { - j = OS.runnablejobs; - OS.runnablejobs = j->next; - } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs - j = OS.scheduledjobs; - OS.scheduledjobs = j->next; - #if LMIC_DEBUG_LEVEL > 1 - has_deadline = true; - #endif - } else { // nothing pending - hal_sleep(); // wake by irq (timer already restarted) - } - hal_enableIRQs(); - if(j) { // run job callback - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Running job %p, cb %p, deadline %lu\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0); - #endif - j->func(j); - } -} diff --git a/lib/arduino-lmic-master/.gitignore b/lib/arduino-lmic-master/.gitignore new file mode 100644 index 00000000..f8afde11 --- /dev/null +++ b/lib/arduino-lmic-master/.gitignore @@ -0,0 +1,46 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Backup files +*.BAK +*.CKP + +# files from Visual Micro +Release +*.vcxproj +*.vcxproj.filters +*.vcxitems +vs-readme.txt +__vm +.vs +*.sln + +# files from vscode +.vscode diff --git a/lib/arduino-lmic-master/.hgignore b/lib/arduino-lmic-master/.hgignore new file mode 100644 index 00000000..d164264c --- /dev/null +++ b/lib/arduino-lmic-master/.hgignore @@ -0,0 +1,17 @@ +^CVS +.*/CVS +.*/CVS/.* +\.\#.*$ +^\.DS_Store$ +.*\.BAK$ +.*\.bak$ +.*\.CKP$ +^build +^build/.* +^.*\.o$ +^.*\.d$ +^.*\.td$ +\.a$ +^core$ +.*/core$ +.*\.rej$ diff --git a/lib/arduino-lmic-master/HOWTO-ADD-REGION.md b/lib/arduino-lmic-master/HOWTO-ADD-REGION.md new file mode 100644 index 00000000..9e002d18 --- /dev/null +++ b/lib/arduino-lmic-master/HOWTO-ADD-REGION.md @@ -0,0 +1,73 @@ +# Adding a new region to Arduino LMIC +This variant of the Arduino LMIC code supports adding additional regions beyond the eu868 and us915 bands supoprted by the original IBM LMIC 1.6 code. + +This document sketches how to add a new region. + +## Planning + +### Determine the region/region category +Compare the target region (in the LoRaWAN regional specification) to the EU868 and US915 regions. There are three possibilities. +1. the region is like the EU region. There are a limited number of channels (up to 8), and only a small number of channels are used for OTAA join operations. The response masks refer to individual channels, and the JOIN-response can send frequencies of specific channels to be added. + +2. The region is like the US region. There are many channels (the US has 64) with fixed frequences, and the channel masks refer to subsets of the fixed channels. + +3. The region is not really like either the EU or US. At the moment, it seems that CN470-510MHz (section 2.6 of LoRaWAN Regional Parameters spec V1.0.2rB) falls into this category. + +Bandplans in categories (1) and (2) are easily supported. Bandplans in category (3) are not supoprted by the current code. + +### Check whether the region is already listed in `lmic_config_preconditions.h` +Check `src/lmic/lmic_config_preconditions.h` and scan the `LMIC_REGION_...` definitions. The numeric values are assigned based on the subchapter in section 2 of the LoRaWAN 1.0.2 Regional Parmaters document. If your symbol is already there, then the first part of adaptation has already been done. There will already be a corresponding `CFG_...` symbol. But if your region isn't supported, you'll need to add it here. + +- `LMIC_REGION_myregion` must be a distinct integer, and must be less than 32 (so as to fit into a bitmask) + +## Make the appropriate changes in `lmic_config_preconditions.h` + +- `LMIC_REGION_SUPPORTED` is a bit mask of all regions supported by the code. Your new region must appear in this list. +- `CFG_LMIC_REGION_MASK` is a bit mask that, when expanded, returns a bitmask for each defined `CFG_...` variable. You must add your `CFG_myregion` symbol to this list. +- `CFG_region` evaluates to the `LMIC_REGION_...` value for the selected region (as long as only one region is selected). The header files check for this, so you don't have to. +- `CFG_LMIC_EU_like_MASK` is a bitmask of regions that are EU-like, and `CFG_LMIC_US_like_MASK` is a bitmask of regions that are US-like. Add your region to the appropriate one of these two variables. + +## Document your region in `config.h` +You'll see where the regions are listed. Add yours. + +## Document your region in `README.md` +You'll see where the regions are listed. Add yours. + +## Add the definitions for your region in `lorabase.h` +- If your region is EU like, copy the EU block. Document any duty-cycle limitations. +- if your region is US like, copy the US block. +- As appropriate, copy `lorabase_eu868.h` or `lorabase_us915.h` to make your own `lorabase_myregion.h`. Fill in the symbols. + +At time of writing, you need to duplicate some code to copy some settings from `..._CONFIG_SYMBOL` to the corresponding `CONFIG_SYMBOL`; and you need to put some region-specific knowledge into the `lorabase.h` header file. The long-term direction is to put all the regional knowledge into the region-specific header, and then the central code will just copy. The architectural impulse is that we'll want to be able to reuse the regional header files in other contexts. On the other hand, because it's error prone, we don't want to `#include` files that aren't being used; otherwise you could accidentally use EU parameters in US code, etc. + +- Now's a good time to test-compile and clean out errors introduced. You'll still have problems compiling, but they should look like this: + ``` + lmic.c:29: In file included from + + lmic_bandplan.h: 52:3: error: #error "maxFrameLen() not defined by bandplan" + # error "maxFrameLen() not defined by bandplan" + + lmic_bandplan.h: 56:3: error: #error "pow2dBm() not defined by bandplan" + # error "pow2dBm() not defined by bandplan" + ``` + +## Edit `lmic_bandplan.h` +The next step is to add the region-specific interfaces for your region. + +Do this by editing `lmic_bandplan.h` and adding the appropriate call to a (new) region-specific file `lmic_bandplan_myregion.h`, where "myregion" is the abbreviation for your region. + +Then, if your region is eu868-like, copy `lmic_bandplan_eu868.h` to create your new region-specific header file; otherwise copy `lmic_bandplan_us915.h`. + +## Create `lmic_myregion.c` +Once again, you will start by copying either `lmic_eu868.c` or `lmic_us915.c` to create your new file. Then touch it up as necessary. + +## General Discussion +- You'll find it easier to do the test compiles using the example scripts in this directory, rather than trying to get all the Catena framework going too. On the other hand, working with the Catena framework will expose more problems. + +## Addding the region to the Arduino_LoRaWAN library + +In `Arduino_LoRaWAN_ttn.h`: +- Add a new class with name `Arduino_LoRaWAN_ttn_myregion`, copied either from the `Arduino_LoRaWAN_ttn_eu868` class or the `Arduino_LoRaWAN_ttn_us915` class. +- Extend the list of `#if defined(CFG_eu868)` etc to define `Arduino_LoRaWAN_REGION_TAG` to the suffix of your new class if `CFG_myregion` is defined. + +Then copy either `ttn_eu868_netbegin.cpp`/`ttn_eu868_netjoin.cpp` or `ttn_us915_netbegin.cpp`/`ttn_us915_netjoin.cpp` to make your own file(s) for the key functions. \ No newline at end of file diff --git a/lib/arduino-lmic-master/LICENSE b/lib/arduino-lmic-master/LICENSE new file mode 100644 index 00000000..1daf4002 --- /dev/null +++ b/lib/arduino-lmic-master/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (C) 2014-2016 IBM Corporation +Copyright (c) 2016-2018 MCCI Corporation +Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/arduino-lmic-master/README.md b/lib/arduino-lmic-master/README.md new file mode 100644 index 00000000..37848751 --- /dev/null +++ b/lib/arduino-lmic-master/README.md @@ -0,0 +1,1076 @@ +Arduino-LMIC library +==================== + +This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly +modified to run in the Arduino environment, allowing using the SX1272, +SX1276 transceivers and compatible modules (such as some HopeRF RFM9x +modules and the Murata LoRa modules). + +This library mostly exposes the functions defined by LMIC, it makes no +attempt to wrap them in a higher level API that is more in the Arduino +style. To find out how to use the library itself, see the examples, or +see the PDF file in the doc subdirectory. + +The [MCCI arduino-lorawan](https://github.com/mcci-catena/arduino-lorawan) library provides a higher level, more Arduino-like wrapper which may be useful. + +This library requires Arduino IDE version 1.6.6 or above, since it +requires C99 mode to be enabled by default. + +[![GitHub release](https://img.shields.io/github/release/mcci-catena/arduino-lmic.svg)](https://github.com/mcci-catena/arduino-lmic/releases/latest) [![GitHub commits](https://img.shields.io/github/commits-since/mcci-catena/arduino-lmic/latest.svg)](https://github.com/mcci-catena/arduino-lmic/compare/V2.2.2...master) [![Build Status](https://travis-ci.com/mcci-catena/arduino-lmic.svg?branch=master)](https://travis-ci.com/mcci-catena/arduino-lmic) + +**Contents:** + + + + +- [Installing](#installing) +- [Features](#features) +- [Configuration](#configuration) + - [Selecting the LoRaWAN Region Configuration](#selecting-the-lorawan-region-configuration) + - [eu868, as923, in866](#eu868-as923-in866) + - [us915, au921](#us915-au921) + - [Selecting the target radio transceiver](#selecting-the-target-radio-transceiver) + - [Controlling use of interrupts](#controlling-use-of-interrupts) + - [Disabling PING](#disabling-ping) + - [Disabling Beacons](#disabling-beacons) + - [Rarely changed variables](#rarely-changed-variables) + - [Changing debug output](#changing-debug-output) + - [Getting debug from the RF library](#getting-debug-from-the-rf-library) + - [Selecting the AES library](#selecting-the-aes-library) + - [Defining the OS Tick Frequency](#defining-the-os-tick-frequency) + - [Setting the SPI-bus frequency](#setting-the-spi-bus-frequency) + - [Changing handling of runtime assertion failures](#changing-handling-of-runtime-assertion-failures) + - [Disabling JOIN](#disabling-join) + - [Disabling Class A MAC commands](#disabling-class-a-mac-commands) + - [Disabling Class B MAC commands](#disabling-class-b-mac-commands) + - [Special purpose](#special-purpose) +- [Supported hardware](#supported-hardware) +- [Connections](#connections) + - [Power](#power) + - [SPI](#spi) + - [DIO pins](#dio-pins) + - [Reset](#reset) + - [RXTX](#rxtx) + - [RXTX Polarity](#rxtx-polarity) + - [Pin mapping](#pin-mapping) + - [Adafruit Feather M0 LoRa](#adafruit-feather-m0-lora) + - [Adafruit Feather 32u4 LoRa](#adafruit-feather-32u4-lora) + - [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron) + - [MCCI Catena 4450/4460](#mcci-catena-44504460) + - [MCCI Catena 4551](#mcci-catena-4551) +- [Example Sketches](#example-sketches) +- [Timing](#timing) + - [`LMIC_setClockError()`](#lmic_setclockerror) +- [Downlink datarate](#downlink-datarate) +- [Encoding Utilities](#encoding-utilities) + - [sflt16](#sflt16) + - [JavaScript decoder](#javascript-decoder) + - [uflt16](#uflt16) + - [JavaScript decoder](#javascript-decoder-1) + - [sflt12](#sflt12) + - [JavaScript decoder](#javascript-decoder-2) + - [uflt12](#uflt12) + - [JavaScript decoder](#javascript-decoder-3) +- [Release History](#release-history) +- [Contributions](#contributions) +- [Trademark Acknowledgements](#trademark-acknowledgements) +- [License](#license) + + + +## Installing + +To install this library: + + - install it using the Arduino Library manager ("Sketch" -> "Include + Library" -> "Manage Libraries..."), or + - download a zipfile from github using the "Download ZIP" button and + install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP + Library..." + - clone this git repository into your sketchbook/libraries folder. + +For more info, see https://www.arduino.cc/en/Guide/Libraries + +## Features + +The LMIC library provides a fairly complete LoRaWAN Class A and Class B +implementation, supporting the EU-868, US-915, AU-921, AS-923, and IN-866 bands. Only a limited +number of features was tested using this port on Arduino hardware, so be careful when using any of the untested features. + +The library has only been tested with LoRaWAN 1.0.2 networks and does not have the separated key structure defined by LoRaWAN 1.1. + +What certainly works: + + - Sending packets uplink, taking into account duty cycling. + - Encryption and message integrity checking. + - Receiving downlink packets in the RX2 window. + - Custom frequencies and datarate settings. + - Over-the-air activation (OTAA / joining). + - Receiving downlink packets in the RX1 and RX2 windows. + - Some MAC command processing. + +What has not been tested: + + - Receiving and processing all MAC commands. + - Class B operation. + - FSK has not been extensively tested. + +If you try one of these untested features and it works, be sure to let +us know (creating a github issue is probably the best way for that). + +## Configuration + +A number of features can be enabled or disabled at compile time. +This is done by adding the desired settings to the file +`project_settings/lmic_project_config.h`. The `project_settings` +directory is the only directory that contains files that you +should edit to match your project; we organize things this way +so that your local changes are more clearly separated from +the distribution files. The Arduino environment doesn't give +us a better way to do this. + +Unlike other ports of the LMIC code, in this port, you should not edit `src/lmic/config.h` to configure this package. + +The following configuration variables are available. + +### Selecting the LoRaWAN Region Configuration + +The library supports the following regions: + +`-D` variable | CFG region name | CFG region value | LoRa Spec Reference| Frequency +------------|-----------------|:----------------:|:-------------------:|-------- +`-D CFG_eu868` | `LMIC_REGION_eu868` | 1 | 2.1 | EU 863-870 MHz ISM +`-D CFG_us915` | `LMIC_REGION_us915` | 2 | 2.2 | US 902-928 MHz ISM +`-D CFG_au921` | `LMIC_REGION_au921` | 5 | 2.5 | Australia 915-928 MHz ISM +`-D CFG_as923` | `LMIC_REGION_as923` | 7 | 2.7 | Asia 923 MHz ISM +`-D CFG_in866` | `LMIC_REGION_in866` | 9 | 2.9 | India 865-867 MHz ISM + +You should define exactly one of `CFG_...` variables. If you don't, +the library assumes `CFG_eu868`. The library changes configuration pretty substantially +according to the region. Some of the differences are listed below. + +#### eu868, as923, in866 + +If the library is configured for EU868, AS923, or IN866 operation, we make +the following changes: +- Add the API `LMIC_setupBand()`. +- Add the constants `MAX_CHANNELS`, `MAX_BANDS`, `LIMIT_CHANNELS`, `BAND_MILLI`, +`BAND_CENTI`, `BAND_DECI`, and `BAND_AUX`. + +#### us915, au921 + +If the library is configured for US915 operation, we make the following changes: +- Add the APIs `LMIC_enableChannel()`, +`LMIC_enableSubBand()`, `LMIC_disableSubBand()`, and `LMIC_selectSubBand()`. +- Add the constants `MAX_XCHANNELS`. +- Add a number of additional `DR_...` symbols. + +### Selecting the target radio transceiver + +You should define one of the following variables. If you don't, the library assumes +sx1276. There is a runtime check to make sure the actual transceiver matches the library +configuration. + +`#define CFG_sx1272_radio 1` + +Configures the library for use with an sx1272 transceiver. + +`#define CFG_sx1276_radio 1` + +Configures the library for use with an sx1276 transceiver. + +### Controlling use of interrupts + +`#define LMIC_USE_INTERRUPTS` + +If defined, configures the library to use interrupts for detecting events from the transceiver. If left undefined, the library will poll for events from the transceiver. See [Timing](#timing) for more info. + +### Disabling PING + +`#define DISABLE_PING` + +If defined, removes all code needed for PING. Removes the APIs `LMIC_setPingable()` and `LMIC_stopPingable()`. +Class A devices don't support PING, so defining `DISABLE_PING` is often a good idea. + +By default, PING support is included in the library. + +### Disabling Beacons + +`#define DISABLE_BEACONS` + +If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`. +Class A devices don't support beacons, so defining `DISABLE_BEACONS` might be a good idea. + + +### Rarely changed variables ### + +The remaining variables are rarely used, but we list them here for completeness. + +#### Changing debug output + +`#define LMIC_PRINTF_TO SerialLikeObject` + +This variable should be set to the name of a `Serial`-like object, used for printing messages. If not defined, `Serial` +is assumed. + +#### Getting debug from the RF library + +`#define LMIC_DEBUG_LEVEL number /* 0, 1, or 2 */` + +This variable determines the amount of debug output to be produced by the library. The default is `0`. + +If `LMIC_DEBUG_LEVEL` is zero, no output is produced. If `1`, limited output is produced. If `2`, more extensive +output is produced. If non-zero, printf() is used, and the Arduino environment must be configured to support it, +otherwise the sketch will crash at runtime. + +#### Selecting the AES library + +The library comes with two AES implementations. The original implementation is better on +ARM processors becasue it's faster, but it's larger. For smaller AVR8 processors, a +second library ("IDEETRON") is provided that has a smaller code footprint. +You may define one of the following variables to choose the AES implementation. If you don't, +the library uses the IDEETRON version. + +`#define USE_ORIGINAL_AES` + +If defined, the original AES implementation is used. + +`#define USE_IDEETRON_AES` + +If defined, the IDEETRON AES implementation is used. + +#### Defining the OS Tick Frequency + +`#define US_PER_OSTICK_EXPONENT number` + +This variable should be set to the base-2 logarithm of the number of microseconds per OS tick. The default is 4, +which indicates that each tick corresponds to 16 microseconds (because 16 == 2^4). + +#### Setting the SPI-bus frequency + +`#define LMIC_SPI_FREQ floatNumber` + +This variable sets the default frequency for the SPI bus connection to the transceiver. The default is `1E6`, meaning 1 MHz. However, this can be overridden by the contents of the `lmic_pinmap` structure, and we recommend that you use that approach rather than editing the `project_settings/lmic_project_config.h` file. + +#### Changing handling of runtime assertion failures + +The variables `LMIC_FAILURE_TO` and `DISABLE_LMIC_FAILURE_TO` +control the handling of runtime assertion failures. By default, assertion messages are displayed using +the `Serial` object. You can define LMIC_FAILURE_TO to be the name of some other `Print`-like obect. You can +also define `DISABLE_LMIC_FAILURE_TO` to any value, in which case assert failures will silently halt execution. + +#### Disabling JOIN + +`#define DISABLE_JOIN` + +If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_startJoining()` and `LMIC_tryRejoin()`. + +#### 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. + +#### Disabling Class B MAC commands + +`DISABLE_MCMD_PING_SET` 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` + +#### Special purpose + +`#define DISABLE_INVERT_IQ_ON_RX` disables the inverted Q-I polarity on RX. If this is defined, end-devices will be able +to receive messages from each other, but will not be able to hear the gateway. + +## Supported hardware + +This library is intended to be used with plain LoRa transceivers, +connecting to them using SPI. In particular, the SX1272 and SX1276 +families are supported (which should include SX1273, SX1277, SX1278 and +SX1279 which only differ in the available frequencies, bandwidths and +spreading factors). It has been tested with both SX1272 and SX1276 +chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 +and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip +respectively). + +This library contains a full LoRaWAN stack and is intended to drive +these Transceivers directly. It is *not* intended to be used with +full-stack devices like the Microchip RN2483 and the Embit LR1272E. +These contain a transceiver and microcontroller that implements the +LoRaWAN stack and exposes a high-level serial interface instead of the +low-level SPI transceiver interface. + +This library is intended to be used inside the Arduino environment. It +should be architecture-independent, so it should run on "normal" AVR +arduinos, but also on the ARM-based ones, and some success has been seen +running on the ESP8266 board as well. It was tested on the Arduino Uno, +Pinoccio Scout, Teensy LC and 3.x, ESP8266, Arduino 101, Adafruit Feather M0 LoRa 900. It has been tested on the Lattice RISC-V CPU soft core running in an iCE40 UltraPlus, and also on the Murata LoRaWAN module on the MCCI Catena 4551. + +This library an be quite heavy on small systems, especially if the fairly small ATmega +328p (such as in the Arduino Uno) is used. In the default configuration, +the available 32K flash space is nearly filled up (this includes some +debug output overhead, though). By disabling some features in `project_settings/lmic_project_config.h` +(like beacon tracking and ping slots, which are not needed for Class A devices), +some space can be freed up. + +## Connections + +To make this library work, your Arduino (or whatever Arduino-compatible +board you are using) should be connected to the transceiver. In some cases (such as the Adafruit Feather series and Murata-based boards such as the MCCI Catena 4551), the settings are fixed by the board, and you won't have to worry about many of these details. However, you'll need to find the configuration that's suitable for your board. + +> To help you know if you have to worry, we'll call such boards "pre-integrated" and prefix each section with suitable guidance. + +The exact +connections are a bit dependent on the transceiver board and Arduino +used, so this section tries to explain what each connection is for and +in what cases it is (not) required. + +Note that the SX1272 module runs at 3.3V and likely does not like 5V on +its pins (though the datasheet is not say anything about this, and my +transceiver did not obviously break after accidentally using 5V I/O for +a few hours). To be safe, make sure to use a level shifter, or an +Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in +series with all data lines that might prevent damage, but I would not +count on that. + +### Power + +> If you're using a pre-integrated board, you can skip this section. + +The SX127x transceivers need a supply voltage between 1.8V and 3.9V. +Using a 3.3V supply is typical. Some modules have a single power pin +(like the HopeRF modules, labeled 3.3V) but others expose multiple power +pins for different parts (like the Semtech evaluation board that has +`VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together. +Any *GND* pins need to be connected to the Arduino *GND* pin(s). + +### SPI + +> If you're using a pre-integrated board, you can skip this section, and instead refer to your board's documentation on the pins to be used. + +The primary way of communicating with the transceiver is through SPI +(Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and +SS. The former three need to be directly connected: so MOSI to MOSI, +MISO to MISO, SCK to SCK. Where these pins are located on your Arduino +varies, see for example the "Connections" section of the [Arduino SPI +documentation](SPI). + +The SS (slave select) connection is a bit more flexible. On the SPI +slave side (the transceiver), this must be connect to the pin +(typically) labeled *NSS*. On the SPI master (Arduino) side, this pin +can connect to any I/O pin. Most Arduinos also have a pin labeled "SS", +but this is only relevant when the Arduino works as an SPI slave, which +is not the case here. Whatever pin you pick, you need to tell the +library what pin you used through the pin mapping (see [below](#pin-mapping)). + +[SPI]: https://www.arduino.cc/en/Reference/SPI + +### DIO pins + +> If you're using a pre-integrated board, you can ignore this section; refer to your board's documentation for information on what DIO pins need to be used. + +The DIO (digital I/O) pins on the SX127x can be configured +for various functions. The LMIC library uses them to get instant status +information from the transceiver. For example, when a LoRa transmission +starts, the DIO0 pin is configured as a TxDone output. When the +transmission is complete, the DIO0 pin is made high by the transceiver, +which can be detected by the LMIC library. + +The LMIC library needs only access to DIO0, DIO1 and DIO2, the other +DIOx pins can be left disconnected. On the Arduino side, they can +connect to any I/O pin. If interrupts are used, the accuracy of timing +will be improved, particularly the rest of your `loop()` function has +lengthy calculations; but in that case, the enabled DIO pins must all +support rising-edge interrupts. See the [Timing](#timing) section below. + +In LoRa mode the DIO pins are used as follows: + * DIO0: TxDone and RxDone + * DIO1: RxTimeout + +In FSK mode they are used as follows:: + * DIO0: PayloadReady and PacketSent + * DIO2: TimeOut + +Both modes need only 2 pins, but the transceiver does not allow mapping +them in such a way that all needed interrupts map to the same 2 pins. +So, if both LoRa and FSK modes are used, all three pins must be +connected. + +The pins used on the Arduino side should be configured in the pin +mapping in your sketch, by setting the values of `lmic_pinmap::dio[0]`, `[1]`, and `[2]` (see [below](#pin-mapping)). + +### Reset + +> If you're using a pre-configured module, refer to the documentation for your board. + +The transceiver has a reset pin that can be used to explicitly reset +it. The LMIC library uses this to ensure the chip is in a consistent +state at startup. In practice, this pin can be left disconnected, since +the transceiver will already be in a sane state on power-on, but +connecting it might prevent problems in some cases. + +On the Arduino side, any I/O pin can be used. The pin number used must +be configured in the pin mapping `lmic_pinmap::rst` field (see [below](#pin-mapping)). + +### RXTX + +> If you're using a pre-configured module, refer to the documentation for your board. + +The transceiver contains two separate antenna connections: One for RX +and one for TX. A typical transceiver board contains an antenna switch +chip, that allows switching a single antenna between these RX and TX +connections. Such a antenna switcher can typically be told what +position it should be through an input pin, often labeled *RXTX*. + +The easiest way to control the antenna switch is to use the *RXTX* pin +on the SX127x transceiver. This pin is automatically set high during TX +and low during RX. For example, the HopeRF boards seem to have this +connection in place, so they do not expose any *RXTX* pins and the pin +can be marked as unused in the pin mapping. + +Some boards do expose the antenna switcher pin, and sometimes also the +SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the +former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these +together with a jumper wire is the easiest solution. + +Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be +configured to control the antenna switch. Connect the antenna switch +control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O +pin on the Arduino side, and configure the pin used in the pin map (see +[below](#pin-mapping)). + +The configuration entry `lmic_pinmap::rxtx` configures the pin to be used for the *RXTX* control function, in terms of the Arduino `wire.h` digital pin number. If set to `LMIC_UNUSED_PIN`, then the library assumes that software does not need to control the antenna switch. + +### RXTX Polarity + +If an external switch is used, you also must specify the polarity. Some modules want *RXTX* to be high for transmit, low for receive; Others want it to be low for transmit, high for receive. The Murata module, for example, requires that *RXTX* be *high* for receive, *low* for transmit. + +The configuration entry `lmic_pinmap::rxtx_rx_active` should be set to the state to be written to the *RXTX* pin to make the receiver active. The opposite state is written to make the transmitter active. If `lmic_pinmap::rxtx` is `LMIC_UNUSED_PIN`, then the value of `lmic_pinmap::rxtx_rx_active` is ignored. + +### Pin mapping + +> For pre-configured boards, refer to the documentation on your board for the required settings. See the following: +> +> - [Adafruit Feather M0 LoRa](#adafruit-feather-m0-lora) +> - [Adafruit Feather 32u4 LoRa](#adafruit-feather-32u4-lora) +> - [LoRa Nexus by Ideetron](#lora-nexus-by-ideetron) +> - [MCCI Catena 4450/4460](#mcci-catena-44504460) +> - [MCCI Catena 4551](#mcci-catena-4551) + +If you don't have the board documentation, you need to provide your own `lmic_pinmap` values. As described above, a variety of configurations are possible. To tell the LMIC library how your board is configured, you must declare a variable containing a pin mapping struct in the sketch file. + +For example, this could look like this: + +```c++ + lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, + // optional: set polarity of rxtx pin. + .rxtx_rx_active = 0, + // optional: set RSSI cal for listen-before-talk + // this value is in dB, and is added to RSSI + // measured prior to decision. + // Must include noise guardband! Ignored in US, + // EU, IN, other markets where LBT is not required. + .rssi_cal = 0, + // optional: override LMIC_SPI_FREQ if non-zero + .spi_freq = 0, + }; +``` + +The names refer to the pins on the transceiver side, the numbers refer +to the Arduino pin numbers (to use the analog pins, use constants like +`A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2 +respectively. Any pins that are not needed should be specified as +`LMIC_UNUSED_PIN`. The nss and dio0 pin is required, the others can +potentially left out (depending on the environments and requirements, +see the notes above for when a pin can or cannot be left out). + +The name of the variable containing this struct must always be `lmic_pins`, which is a special name recognized by the library. + + +#### Adafruit Feather M0 LoRa + +See Adafruit's [Feather M0 LoRa product page](https://www.adafruit.com/product/3178). +This board uses the following pin mapping, as shown in the various "...-feather" +sketches. + +DIO0 is hard-wired by Adafruit to Arduino D3, but DIO1 is not connected to any Arduino pin (it comes to JP1 pin 1, but is not otherwise connected). This pin table assumes that you have manually wired JP1 pin 1 to Arduino JP3 pin 9 (Arduino D6). + +DIO2 is not connected. + +```c++ +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, +}; +``` + + +#### Adafruit Feather 32u4 LoRa + +See Adafruit's [Feather 32u4 LoRa product page](https://www.adafruit.com/product/3078). This board is supported by the [ttn-otaa-feather-us915.ino](examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino) example sketch. It uses the same pin mapping as the [Feather M0 LoRa](#adafruit-feather-m0-lora). + + +#### LoRa Nexus by Ideetron + +This board uses the following pin mapping: + +```c++ + const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET + .dio = {4, 5, 7}, + }; +``` + + +#### MCCI Catena 4450/4460 + +See [MCCI Catena 4450](https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4450-lorawan-iot-device) and [MCCI Catena 4460](https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4460-sensor-wing-w-bme680). + +These modules are based on the [Feather M0 LoRa](https://www.adafruit.com/product/3178). Since they include an extra Feather wing for the sensors, the Feather wing includes the trace connecting DIO1 to Arduino D6. No user wiring is needed on the Feather M0. + +```c++ +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 6, LMIC_UNUSED_PIN}, +}; +``` + + +#### MCCI Catena 4551 + +See [MCCI Catena 4551](https://store.mcci.com/collections/lorawan-iot-and-the-things-network/products/catena-4551-integrated-lorawan-node). +This board uses a Murata LoRa module and has the following pin mapping: + +```c++ +const lmic_pinmap lmic_pins = { + .nss = 7, + .rxtx = 29, + .rst = 8, + .dio = {25, 26, 27}, + // the Murata module needs D29 high for RX, low for TX. + .rxtx_rx_active = 1, + // the Murata module is direct-wired, we can use 8 MHz for SPI. + .spi_freq = 8000000 +}; +``` + +## Example Sketches + +This library provides several examples. + + - [`ttn-otaa.ino`](examples/ttn-otaa/ttn-otaa.ino) shows a basic transmission of a "Hello, world!" message + using the LoRaWAN protocol. It contains some frequency settings and + encryption keys intended for use with The Things Network, but these + also correspond to the default settings of most gateways, so it + should work with other networks and gateways as well. + The example uses over-the-air activation (OTAA) to first join the network to establish a + session and security keys. This was tested with The Things Network, + but should also work (perhaps with some changes) for other networks. + OTAA is the preferred way to work with production LoRaWAN networks. + + - [`ttn-otaa-feather-us915.ino`](examples/ttn-otaa-feather-us915/ttn-otaa-feather-us915.ino) is a version of `ttn-otaa.ino` that has + been configured for use with the Feather M0 LoRa, on the US915 bandplan, + with The Things Network. Remember that you may also have to change `config.h` + from defaults. This sketch also works with the MCCI Catena family of products + as well as with the Feather 32u4 LoRa. + + - [`ttn-otaa-feather-us915-dht22.ino`](examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino) + is a further refinement of `ttn-otaa-feather-us915.ino`. It measures and + transmits temperature and relative humidity using a DHT22 sensor. It's only + been tested with Feather M0-family products. + + - [`raw.ino`](examples/raw/raw.ino) shows how to access the radio on a somewhat low level, + and allows to send raw (non-LoRaWAN) packets between nodes directly. + This is useful to verify basic connectivity, and when no gateway is + available, but this example also bypasses duty cycle checks, so be + careful when changing the settings. + + - [`raw-feather.ino`](examples/raw-feather/raw-feather.ino) is a version of `raw.ino` that is completely configured + for the Adafruit [Feather M0 LoRa](https://www.adafruit.com/product/3178), and for a variety + of other MCCI products. + + - [`ttn-abp.ino`](examples/ttn-abp/ttn-abp.ino) shows a basic transmission of a "Hello, world!" message + using the LoRaWAN protocol. This example + uses activation-by-personalization (ABP, preconfiguring a device + address and encryption keys), and does not employ over-the-air + activation. + + ABP should not be used if you have access to a production gateway and network; it's + not compliant with LoRaWAN standards, it's not FCC compliant, and it's uses spectrum + in a way that's unfair to other users. However, it's often the most economical way to + get your feet wet with this technology. It's possible to do ABP compliantly with the LMIC + framework, but you need to have FRAM storage and a framework that saves uplink and + downlink counts across reboots and resets. See, for example, + [Catena-Arduino-Platform](https://github.com/mcci-catena/Catena-Arduino-Platform). + + - [`ttn-abp-feather-us915-dht22.ino`](examples/ttn-abp-feather-us915-dht22/ttn-abp-feather-us915-dht22.ino) + refines `ttn-abp.ino` by configuring for use with the Feather M0 LoRa on the US915 bandplan, + with a single-channel gateway on The Things Network; it measures and transmits temperature and relative + humidity using a DHT22 sensor. It's only been tested with Feather M0-family products. + + ABP should not be used if you have access to a production gateway and network; it's + not compliant with LoRaWAN standards, it's not FCC compliant, and it's uses spectrum + in a way that's unfair to other users. However, it's often the most economical way to + get your feet wet with this technology. It's possible to do ABP compliantly with the LMIC + framework, but you need to have FRAM storage and a framework that saves uplink and + downlink counts across reboots and resets. See, for example, + [Catena-Arduino-Platform](https://github.com/mcci-catena/Catena-Arduino-Platform). + + - [`header_test.ino`](examples/header_test/header_test.ino) just tests the header files; it's used for regression testing. + +## Timing + +The library is +responsible for keeping track of time of certain network events, and scheduling +other events relative to those events. In particular, the library must note +when a packet finishes transmitting, so it can open up the RX1 and RX2 +receive windows at a fixed time after the end of transmission. The library does this +by watching for rising edges on the DIO0 output of the SX127x, and noting the time. + +The library observes and processes rising edges on the pins as part of `os_runloop()` processing. +This can be configured in one of two ways (see +[Controlling use of interrupts](#controlling-use-of-interrupts)). + +By default, the routine `hal_io_check()` +polls the enabled pins to determine whether an event has occured. This approach +allows use of any CPU pin to sense the DIOs, and makes no assummptions about +interrupts. However, it means that the end-of-transmit event is not observed +(and time-stamped) until `os_runloop()` is called. + +Optionally, you can configure the LMIC library to use interrupts. The +interrupt handlers capture the time of +the event. Actual processing is done the next time that `os_runloop()` +is called, using the captured time. However, this requires that the +DIO pins be wired to Arduino pins that support rising-edge interrupts. + +Fortunately, LoRa is a fairly slow protocol and the timing of the +receive windows is not super critical. To synchronize transmitter and +receiver, a preamble is first transmitted. Using LoRaWAN, this preamble +consists of 8 symbols, of which the receiver needs to see 4 symbols to +lock on. The current implementation tries to enable the receiver for 5 +symbol times at 1.5 symbol after the start of the receive window, +meaning that a inaccuracy of plus or minus 2.5 symbol times should be +acceptable. + +The HAL bases all timing on the Arduino `micros()` timer, which has a platform-specific +granularity, and is based on the primary microcontroller clock. + +At the fastest LoRa setting supported by the SX127x (SF5BW500) a +single preamble symbol takes 64 microseconds, so the receive window timing should +be accurate within 160 microseconds (for LoRaWAN this is SF7BW250, needing accuracy +within 1280μs). This accuracy should also be +feasible with the polling approach used, provided that the LMIC loop is +run often enough. + +If using an internal oscillator (which is 1% - 10% +accurate, depending on calibration), or if your other `loop()` processing +is time consuming, you may have to use [`LMIC_setClockError()`](#lmic_setclockerror) +to cause the library to leave the radio on longer. + +An even more accurate solution could be to use a dedicated timer with an +input capture unit, that can store the timestamp of a change on the DIO0 +pin (the only one that is timing-critical) entirely in hardware. +Experience shows that this is not normally required, so we leave this as +a customization to be performed on a platform-by-platfom basis. We provide +a special API, `radio_irq_handler_v2(u1_t dio, ostime_t tEvent)`. This +API allows you to supply a hardware-captured time for extra accuracy. + +The practical consequence of inaccurate timing is reduced battery life; +the LMIC must turn on the reciever earlier in order to be sure to capture downlink packets. + +### `LMIC_setClockError()` + +You may call this routine during intialization to infom the LMIC code about the timing accuracy of your system. + +```c++ +enum { MAX_CLOCK_ERROR = 65535 }; + +void LMIC_setClockError( + u2_t error +); +``` + +This function sets the anticipated relative clock error. `MAX_CLOCK_ERROR` +represents +/- 100%, and 0 represents no additional clock compensation. +To allow for an error of 20%, you would call + +```c++ +LMIC_setClockError(MAX_CLOCK_ERROR * 20 / 100); +``` + +Setting a high clock error causes the RX windows to be opened earlier than it otherwise would be. This causes more power to be consumed. For Class A devices, this extra power is not substantial, but for Class B devices, this can be significant. + +This clock error is not reset by `LMIC_reset()`. + +## Downlink datarate + +Note that the datarate used for downlink packets in the RX2 window varies by region. Consult your network's manual for any divergences from the LoRaWAN Regional Parameters. This library assumes that the network follows the regional default. + +Some networks use different values than the specification. For example, in Europe, the specification default is DR0 (SF12, 125 kHz bandwidth). However, iot.semtech.com and The Things Network both used SF9 / 125 kHz or DR3). If using over-the-air activation (OTAA), the network will download RX2 parameters as part of the JoinAccept message; the LMIC will honor the downloaded parameters. + +However, when using personalized activate (ABP), it is your +responsibility to set the right settings, e.g. by adding this to your +sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does +this. + +```c++ +LMIC.dn2Dr = DR_SF9; +``` + +## Encoding Utilities + +It is generally important to make LoRaWAN messages as small as practical. Extra bytes mean extra transmit time, which wastes battery power and interferes with other nodes on the network. + +To simplify coding, the Arduino header file defines some data encoding utility functions to encode floating-point data into `uint16_t` values using `sflt16` or `uflt16` bit layout. For even more efficiency, there are versions that use only the bottom 12 bits of the `uint16_t`, allowing for other bits to be carried in the top 4 bits, or for two values to be crammed into three bytes. + +- `uint16_t LMIC_f2sflt16(float)` converts a floating point number to a [`sflt16`](#sflt16)-encoded `uint16_t`. +- `uint16_t LMIC_f2uflt16(float)` converts a floating-point number to a [`uflt16`](#uflt16)-encoded `uint16_t`. +- `uint16_t LMIC_f2sflt12(float)` converts a floating-point number to a [`sflt12`](#sflt12)-encoded `uint16_t`, leaving the top four bits of the result set to zero. +- `uint16_t LMIC_f2uflt12(float)` converts a floating-point number to a [`uflt12`](#sflt12)-encoded `uint16_t`, leaving the top four bits of the result set to zero. + +JavaScript code for decoding the data can be found in the following sections. + +### sflt16 + +A `sflt16` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +15 | Sign bit +14..11 | binary exponent `b` +10..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/2048 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. However, it is similar to IEEE format in that it uses sign-magnitude rather than twos-complement for negative values. + +For example, if the data value is 0x8D, 0x55, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x8D55. +2. Bit 15 is 1, so this is a negative value. +3. `b` is 1, and `b`-15 is -14. 2^-14 is 1/16384 +4. `f` is 0x555. 0x555/2048 = 1365/2048 is 0.667 +5. `f * 2^(b-15)` is therefore 0.667/16384 or 0.00004068 +6. Since the number is negative, the value is -0.00004068 + +Floating point mavens will immediately recognize: + +* This format uses sign/magnitude representation for negative numbers. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 2048..4095, and then transmit only `f - 2048`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### JavaScript decoder + +```javascript +function sflt162f(rawSflt16) + { + // rawSflt16 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFFF + // bit 15 is the sign bit + // bits 14..11 are the exponent + // bits 10..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the open interval (-1.0, 1.0); + // + + // throw away high bits for repeatability. + rawSflt16 &= 0xFFFF; + + // special case minus zero: + if (rawSflt16 == 0x8000) + return -0.0; + + // extract the sign. + var sSign = ((rawSflt16 & 0x8000) != 0) ? -1 : 1; + + // extract the exponent + var exp1 = (rawSflt16 >> 11) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawSflt16 & 0x7FF) / 2048.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### uflt16 + +A `uflt16` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +15..12 | binary exponent `b` +11..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/4096 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0xEB, 0xF7, and the transmitted byte order is big endian, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0xEBF7. +2. `b` is therefore 0xE, and `b`-15 is -1. 2^-1 is 1/2 +3. `f` is 0xBF7. 0xBF7/4096 is 3063/4096 == 0.74780... +4. `f * 2^(b-15)` is therefore 0.74780/2 or 0.37390 + +Floating point mavens will immediately recognize: + +* There is no sign bit; all numbers are positive. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 4096..8191, and then transmit only `f - 4096`, saving a bit. However, this complicated the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### JavaScript decoder + +```javascript +function uflt162f(rawUflt16) + { + // rawUflt16 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFFF + // bits 15..12 are the exponent + // bits 11..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the half-open interval [0, 1.0); + // + + // throw away high bits for repeatability. + rawUflt16 &= 0xFFFF; + + // extract the exponent + var exp1 = (rawUflt16 >> 12) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawUflt16 & 0xFFF) / 4096.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### sflt12 + +A `sflt12` datum represents an signed floating point number in the range [0, 1.0), transmitted as a 12-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +11 | sign bit +11..8 | binary exponent `b` +7..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/128 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0x8, 0xD5, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x8D5. +2. The number is negative. +3. `b` is 0x1, and `b`-15 is -14. 2^-14 is 1/16384 +4. `f` is 0x55. 0x55/128 is 85/128, or 0.66 +5. `f * 2^(b-15)` is therefore 0.66/16384 or 0.000041 (to two significant digits) +6. The decoded number is therefore -0.000041. + +Floating point mavens will immediately recognize: + +* This format uses sign/magnitude representation for negative numbers. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 128 .. 256, and then transmit only `f - 128`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. +* It can be strongly argued that dropping the sign bit would be worth the effort, as this would get us 14% more resolution for a minor amount of work. + +#### JavaScript decoder + +```javascript +function sflt122f(rawSflt12) + { + // rawSflt12 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFF (12 bits). For safety, we mask + // on entry and discard the high-order bits. + // bit 11 is the sign bit + // bits 10..7 are the exponent + // bits 6..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the open interval (-1.0, 1.0); + // + + // throw away high bits for repeatability. + rawSflt12 &= 0xFFF; + + // special case minus zero: + if (rawSflt12 == 0x800) + return -0.0; + + // extract the sign. + var sSign = ((rawSflt12 & 0x800) != 0) ? -1 : 1; + + // extract the exponent + var exp1 = (rawSflt12 >> 7) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawSflt12 & 0x7F) / 128.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +### uflt12 + +A `uflt12` datum represents an unsigned floating point number in the range [0, 1.0), transmitted as a 16-bit field. The encoded field is interpreted as follows: + +bits | description +:---:|:--- +11..8 | binary exponent `b` +7..0 | fraction `f` + +The corresponding floating point value is computed by computing `f`/256 * 2^(`b`-15). Note that this format is deliberately not IEEE-compliant; it's intended to be easy to decode by hand and not overwhelmingly sophisticated. + +For example, if the transmitted message contains 0x1, 0xAB, the equivalent floating point number is found as follows. + +1. The full 16-bit number is 0x1AB. +2. `b` is therefore 0x1, and `b`-15 is -14. 2^-14 is 1/16384 +3. `f` is 0xAB. 0xAB/256 is 0.67 +4. `f * 2^(b-15)` is therefore 0.67/16384 or 0.0000408 (to three significant digits) + +Floating point mavens will immediately recognize: + +* There is no sign bit; all numbers are positive. +* Numbers do not need to be normalized (although in practice they always are). +* The format is somewhat wasteful, because it explicitly transmits the most-significant bit of the fraction. (Most binary floating-point formats assume that `f` is is normalized, which means by definition that the exponent `b` is adjusted and `f` is shifted left until the most-significant bit of `f` is one. Most formats then choose to delete the most-significant bit from the encoding. If we were to do that, we would insist that the actual value of `f` be in the range 256 .. 512, and then transmit only `f - 256`, saving a bit. However, this complicates the handling of gradual underflow; see next point.) +* Gradual underflow at the bottom of the range is automatic and simple with this encoding; the more sophisticated schemes need extra logic (and extra testing) in order to provide the same feature. + +#### JavaScript decoder + +```javascript +function uflt122f(rawUflt12) + { + // rawUflt12 is the 2-byte number decoded from wherever; + // it's in range 0..0xFFF (12 bits). For safety, we mask + // on entry and discard the high-order bits. + // bits 11..8 are the exponent + // bits 7..0 are the the mantissa. Unlike IEEE format, + // the msb is explicit; this means that numbers + // might not be normalized, but makes coding for + // underflow easier. + // As with IEEE format, negative zero is possible, so + // we special-case that in hopes that JavaScript will + // also cooperate. + // + // The result is a number in the half-open interval [0, 1.0); + // + + // throw away high bits for repeatability. + rawUflt12 &= 0xFFF; + + // extract the exponent + var exp1 = (rawUflt12 >> 8) & 0xF; + + // extract the "mantissa" (the fractional part) + var mant1 = (rawUflt12 & 0xFF) / 256.0; + + // convert back to a floating point number. We hope + // that Math.pow(2, k) is handled efficiently by + // the JS interpreter! If this is time critical code, + // you can replace by a suitable shift and divide. + var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15); + + return f_unscaled; + } +``` + +## Release History + +- V2.2.2 adds `ttn-abp-feather-us915-dht22.ino` example, and fixes some documentation typos. It also fixes encoding of the `Margin` field of the `DevStatusAns` MAC message ([#130](https://github.com/mcci-catena/arduino-lmic/issues/130)). This makes Arduino LMIC work with newtorks implemented with [LoraServer](https://www.loraserver.io/). + +- V2.2.1 corrects the value of `ARDUINO_LMIC_VERSION` ([#123](https://github.com/mcci-catena/arduino-lmic/issues/123)), allows ttn-otaa-feather-us915 example to compile for the Feather 32u4 LoRa ([#116](https://github.com/mcci-catena/arduino-lmic/issues/116)), and addresses documentation issues ([#122](https://github.com/mcci-catena/arduino-lmic/issues/122), [#120](https://github.com/mcci-catena/arduino-lmic/issues/120)). + +- V2.2.0 adds encoding functions and `tn-otaa-feather-us915-dht22.ino` example. Plus a large number of issues: [#59](https://github.com/mcci-catena/arduino-lmic/issues/59), [#60](https://github.com/mcci-catena/arduino-lmic/issues/60), [#63](https://github.com/mcci-catena/arduino-lmic/issues/63), [#64](https://github.com/mcci-catena/arduino-lmic/issues/47) (listen-before-talk for Japan), [#65](https://github.com/mcci-catena/arduino-lmic/issues/65), [#68](https://github.com/mcci-catena/arduino-lmic/issues/68), [#75](https://github.com/mcci-catena/arduino-lmic/issues/75), [#78](https://github.com/mcci-catena/arduino-lmic/issues/78), [#80](https://github.com/mcci-catena/arduino-lmic/issues/80), [#91](https://github.com/mcci-catena/arduino-lmic/issues/91), [#98](https://github.com/mcci-catena/arduino-lmic/issues/98), [#101](https://github.com/mcci-catena/arduino-lmic/issues/101). Added full Travis CI testing, switched to travis-ci.com as the CI service. Prepared to publish library in the offical Arduino library list. + +- V2.1.5 fixes issue [#56](https://github.com/mcci-catena/arduino-lmic/issues/56) (a documentation bug). Documentation was quickly reviewed and other issues were corrected. The OTAA examples were also updated slightly. + +- V2.1.4 fixes issues [#47](https://github.com/mcci-catena/arduino-lmic/issues/47) and [#50](https://github.com/mcci-catena/arduino-lmic/issues/50) in the radio driver for the SX1276 (both related to handling of output power control bits). + +- V2.1.3 has a fix for issue [#43](https://github.com/mcci-catena/arduino-lmic/issues/43): handling of `LinkAdrRequest` was incorrect for US915 and AU921; when TTN added ADR support on US and AU, the deficiency was revealed (and caused an ASSERT). + +- V2.1.2 has a fix for issue [#39](https://github.com/mcci-catena/arduino-lmic/issues/39) (adding a prototype for `LMIC_DEBUG_PRINTF` if needed). Fully upward compatible, so just a patch. + +- V2.1.1 has the same content as V2.1.2, but was accidentally released without updating `library.properties`. + +- V2.1.0 adds support for the Murata LoRaWAN module. + +- V2.0.2 adds support for the extended bandplans. + +## Contributions + +This library started from the IBM V1.5 open-source code. + +- Thomas Telkamp and Matthijs Kooijman ported V1.5 to Arduino and did a lot of bug fixing. + +- Terry Moore, LeRoy Leslie, Frank Rose, and ChaeHee Won did a lot of work on US support. + +- Terry Moore added the AU921, AS923 and IN866 bandplans, and created the regionalization framework. + +- [@tanupoo](https://github.com/tanupoo) of the WIDE Project debugged AS923JP and LBT support. + +## Trademark Acknowledgements + +LoRa is a registered trademark of the LoRa Alliance. LoRaWAN is a trademark of the LoRa Alliance. + +MCCI and MCCI Catena are registered trademarks of MCCI Corporation. + +All other trademarks are the properties of their respective owners. + +## License + +The upstream files from IBM v1.6 are based on the Berkeley license, +and the merge which synchronized this repository therefore migrated +the core files to the Berkeley license. However, modifications made +in the Arduino branch were done under the Eclipse license, so the +overall license of this repository is still Eclipse Public License +v1.0. The examples which use a more liberal +license. Some of the AES code is available under the LGPL. Refer to each +individual source file for more details, but bear in mind that until +the upstream developers look into this issue, it is safest to assume +the Eclipse license applies. diff --git a/lib/arduino-lmic-master/doc/LMiC-v1.6.docx b/lib/arduino-lmic-master/doc/LMiC-v1.6.docx new file mode 100644 index 00000000..cb00af74 Binary files /dev/null and b/lib/arduino-lmic-master/doc/LMiC-v1.6.docx differ diff --git a/lib/arduino-lmic-master/doc/LMiC-v1.6.pdf b/lib/arduino-lmic-master/doc/LMiC-v1.6.pdf new file mode 100644 index 00000000..66821e13 Binary files /dev/null and b/lib/arduino-lmic-master/doc/LMiC-v1.6.pdf differ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/README.txt b/lib/arduino-lmic-master/doc/README.txt similarity index 97% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/README.txt rename to lib/arduino-lmic-master/doc/README.txt index 237b1632..42027c31 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/README.txt +++ b/lib/arduino-lmic-master/doc/README.txt @@ -1,4 +1,4 @@ -DISCLAIMER: -Please note that the software is provided AS IS and we cannot -provide support for optimizations, adaptations, integration, -ports to other platforms or device drivers! +DISCLAIMER: +Please note that the software is provided AS IS and we cannot +provide support for optimizations, adaptations, integration, +ports to other platforms or device drivers! diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/release-notes.txt b/lib/arduino-lmic-master/doc/release-notes.txt similarity index 73% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/release-notes.txt rename to lib/arduino-lmic-master/doc/release-notes.txt index 835154d1..37a4227b 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/doc/release-notes.txt +++ b/lib/arduino-lmic-master/doc/release-notes.txt @@ -1,28 +1,38 @@ -============================================================================== -LMIC VERSION 1.4 (17-Mar-2015) -------------------------------- - - - changed API: inverted port indicator flag in LMIC.txrxFlags - (now TXRX_PORT, previously TXRX_NOPORT) - - - fixed offset OFF_CFLIST constant - - - changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial - - - fixed radio driver (low data rate optimization for SF11+SF12 only for BW125) - - - fixed timer rollover handling in job queue - -============================================================================== -LMIC VERSION 1.5 (8-May-2015) ------------------------------- - - - fixed condition in convFreq() - - - fixed freq*100 bug and freq==0 bug for CFList - - - fixed TX scheduling bug - - - better support for GNU compiler toolchain - -============================================================================== +============================================================================== +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 + + +============================================================================== +LMIC VERSION 1.5 (8-May-2015) +------------------------------ + + - fixed condition in convFreq() + + - fixed freq*100 bug and freq==0 bug for CFList + + - fixed TX scheduling bug + + - better support for GNU compiler toolchain + + +============================================================================== +LMIC VERSION 1.4 (17-Mar-2015) +------------------------------- + + - changed API: inverted port indicator flag in LMIC.txrxFlags + (now TXRX_PORT, previously TXRX_NOPORT) + + - fixed offset OFF_CFLIST constant + + - changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial + + - fixed radio driver (low data rate optimization for SF11+SF12 only for BW125) + + - fixed timer rollover handling in job queue + +============================================================================== diff --git a/lib/arduino-lmic-master/library.properties b/lib/arduino-lmic-master/library.properties new file mode 100644 index 00000000..76ecaf6f --- /dev/null +++ b/lib/arduino-lmic-master/library.properties @@ -0,0 +1,9 @@ +name=MCCI LoRaWAN LMIC library +version=2.2.2 +author=IBM, Matthis Kooijman, Terry Moore, ChaeHee Won, Frank Rose +maintainer=Terry Moore +sentence=Arduino port of the LMIC (LoraWAN-MAC-in-C) framework provided by IBM. +paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers. Refactored to support multiple bandplans beyond the original two supported by the IBM LMIC code. Various enhancements and bug fixes from MCCI and The Things Network New York. Original IBM URL http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html. +category=Communication +url=https://github.com/mcci-catena/arduino-lmic +architectures=* diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/ideetron/AES-128_V10.cpp b/lib/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp similarity index 96% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/ideetron/AES-128_V10.cpp rename to lib/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp index f067ebd4..96bb3d07 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/ideetron/AES-128_V10.cpp +++ b/lib/arduino-lmic-master/src/aes/ideetron/AES-128_V10.cpp @@ -1,342 +1,349 @@ -/****************************************************************************************** -#if defined(USE_IDEETRON_AES) -* Copyright 2015, 2016 Ideetron B.V. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -******************************************************************************************/ -/****************************************************************************************** -* -* File: AES-128_V10.cpp -* Author: Gerben den Hartog -* Compagny: Ideetron B.V. -* Website: http://www.ideetron.nl/LoRa -* E-mail: info@ideetron.nl -******************************************************************************************/ -/**************************************************************************************** -* -* Created on: 20-10-2015 -* Supported Hardware: ID150119-02 Nexus board with RFM95 -* -* Firmware Version 1.0 -* First version -****************************************************************************************/ - -// This file was taken from -// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for -// use with LMIC. It was only cosmetically modified: -// - AES_Encrypt was renamed to lmic_aes_encrypt. -// - All other functions and variables were made static -// - Tabs were converted to 2 spaces -// - An #include and #if guard was added -// - S_Table is now stored in PROGMEM - -#include "../../lmic/oslmic.h" - -#if defined(USE_IDEETRON_AES) - -/* -******************************************************************************************** -* Global Variables -******************************************************************************************** -*/ - -static unsigned char State[4][4]; - -static CONST_TABLE(unsigned char, S_Table)[16][16] = { - {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, - {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, - {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, - {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, - {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, - {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, - {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, - {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, - {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, - {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, - {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, - {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, - {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, - {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, - {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, - {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} -}; - -extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); -static void AES_Add_Round_Key(unsigned char *Round_Key); -static unsigned char AES_Sub_Byte(unsigned char Byte); -static void AES_Shift_Rows(); -static void AES_Mix_Collums(); -static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); -static void Send_State(); - -/* -***************************************************************************************** -* Description : Function for encrypting data using AES-128 -* -* Arguments : *Data Data to encrypt is a 16 byte long arry -* *Key Key to encrypt data with is a 16 byte long arry -***************************************************************************************** -*/ -void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) -{ - unsigned char i; - unsigned char Row,Collum; - unsigned char Round = 0x00; - unsigned char Round_Key[16]; - - //Copy input to State arry - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = Data[Row + (4*Collum)]; - } - } - - //Copy key to round key - for(i = 0; i < 16; i++) - { - Round_Key[i] = Key[i]; - } - - //Add round key - AES_Add_Round_Key(Round_Key); - - //Preform 9 full rounds - for(Round = 1; Round < 10; Round++) - { - //Preform Byte substitution with S table - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); - } - } - - //Preform Row Shift - AES_Shift_Rows(); - - //Mix Collums - AES_Mix_Collums(); - - //Calculate new round key - AES_Calculate_Round_Key(Round,Round_Key); - - //Add round key - AES_Add_Round_Key(Round_Key); - } - - //Last round whitout mix collums - //Preform Byte substitution with S table - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); - } - } - - //Shift rows - AES_Shift_Rows(); - - //Calculate new round key - AES_Calculate_Round_Key(Round,Round_Key); - - //Add round Key - AES_Add_Round_Key(Round_Key); - - //Copy the State into the data array - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - Data[Row + (4*Collum)] = State[Row][Collum]; - } - } - -} - -/* -***************************************************************************************** -* Description : Function that add's the round key for the current round -* -* Arguments : *Round_Key 16 byte long array holding the Round Key -***************************************************************************************** -*/ -static void AES_Add_Round_Key(unsigned char *Round_Key) -{ - unsigned char Row,Collum; - - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; - } - } -} - -/* -***************************************************************************************** -* Description : Function that substitutes a byte with a byte from the S_Table -* -* Arguments : Byte The byte that will be substituted -* -* Return : The return is the found byte in the S_Table -***************************************************************************************** -*/ -static unsigned char AES_Sub_Byte(unsigned char Byte) -{ - unsigned char S_Row,S_Collum; - unsigned char S_Byte; - - //Split byte up in Row and Collum - S_Row = ((Byte >> 4) & 0x0F); - S_Collum = (Byte & 0x0F); - - //Find the correct byte in the S_Table - S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); - - return S_Byte; -} - -/* -***************************************************************************************** -* Description : Function that preforms the shift row operation described in the AES standard -***************************************************************************************** -*/ -static void AES_Shift_Rows() -{ - unsigned char Buffer; - - //Row 0 doesn't change - - //Shift Row 1 one left - //Store firt byte in buffer - Buffer = State[1][0]; - //Shift all bytes - State[1][0] = State[1][1]; - State[1][1] = State[1][2]; - State[1][2] = State[1][3]; - State[1][3] = Buffer; - - //Shift row 2 two left - Buffer = State[2][0]; - State[2][0] = State[2][2]; - State[2][2] = Buffer; - Buffer = State[2][1]; - State[2][1] = State[2][3]; - State[2][3] = Buffer; - - //Shift row 3 three left - Buffer = State[3][3]; - State[3][3] = State[3][2]; - State[3][2] = State[3][1]; - State[3][1] = State[3][0]; - State[3][0] = Buffer; -} - -/* -***************************************************************************************** -* Description : Function that preforms the Mix Collums operation described in the AES standard -***************************************************************************************** -*/ -static void AES_Mix_Collums() -{ - unsigned char Row,Collum; - unsigned char a[4], b[4]; - for(Collum = 0; Collum < 4; Collum++) - { - for(Row = 0; Row < 4; Row++) - { - a[Row] = State[Row][Collum]; - b[Row] = (State[Row][Collum] << 1); - - if((State[Row][Collum] & 0x80) == 0x80) - { - b[Row] = b[Row] ^ 0x1B; - } - } - State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; - State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; - State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; - State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; - } -} - -/* -***************************************************************************************** -* Description : Function that calculaties the round key for the current round -* -* Arguments : Round Number of current Round -* *Round_Key 16 byte long array holding the Round Key -***************************************************************************************** -*/ -static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) -{ - unsigned char i,j; - unsigned char b; - unsigned char Temp[4]; - unsigned char Buffer; - unsigned char Rcon; - - //Calculate first Temp - //Copy laste byte from previous key - for(i = 0; i < 4; i++) - { - Temp[i] = Round_Key[i+12]; - } - - //Rotate Temp - Buffer = Temp[0]; - Temp[0] = Temp[1]; - Temp[1] = Temp[2]; - Temp[2] = Temp[3]; - Temp[3] = Buffer; - - //Substitute Temp - for(i = 0; i < 4; i++) - { - Temp[i] = AES_Sub_Byte(Temp[i]); - } - - //Calculate Rcon - Rcon = 0x01; - while(Round != 1) - { - b = Rcon & 0x80; - Rcon = Rcon << 1; - if(b == 0x80) - { - Rcon = Rcon ^ 0x1b; - } - Round--; - } - - //XOR Rcon - Temp[0] = Temp[0] ^ Rcon; - - //Calculate new key - for(i = 0; i < 4; i++) - { - for(j = 0; j < 4; j++) - { - Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; - Temp[j] = Round_Key[j + (4*i)]; - } - } -} - -#endif // defined(USE_IDEETRON_AES) +/****************************************************************************************** +#if defined(USE_IDEETRON_AES) +* Copyright 2015, 2016 Ideetron B.V. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +******************************************************************************************/ +/****************************************************************************************** +* +* File: AES-128_V10.cpp +* Author: Gerben den Hartog +* Compagny: Ideetron B.V. +* Website: http://www.ideetron.nl/LoRa +* E-mail: info@ideetron.nl +******************************************************************************************/ +/**************************************************************************************** +* +* Created on: 20-10-2015 +* Supported Hardware: ID150119-02 Nexus board with RFM95 +* +* Firmware Version 1.0 +* First version +****************************************************************************************/ + +// This file was taken from +// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for +// use with LMIC. It was only cosmetically modified: +// - AES_Encrypt was renamed to lmic_aes_encrypt. +// - All other functions and variables were made static +// - Tabs were converted to 2 spaces +// - An #include and #if guard was added +// - S_Table is now stored in PROGMEM + +#include "../../lmic/oslmic.h" + +#if defined(USE_IDEETRON_AES) + +/* +******************************************************************************************** +* Global Variables +******************************************************************************************** +*/ + +static unsigned char State[4][4]; + +static CONST_TABLE(unsigned char, S_Table)[16][16] = { + {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, + {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, + {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, + {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, + {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, + {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, + {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, + {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, + {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, + {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, + {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, + {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, + {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, + {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, + {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, + {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} +}; + +#ifdef __cplusplus +extern "C" { +#endif + void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); +#ifdef __cplusplus +} +#endif + +static void AES_Add_Round_Key(unsigned char *Round_Key); +static unsigned char AES_Sub_Byte(unsigned char Byte); +static void AES_Shift_Rows(); +static void AES_Mix_Collums(); +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); +static void Send_State(); + +/* +***************************************************************************************** +* Description : Function for encrypting data using AES-128 +* +* Arguments : *Data Data to encrypt is a 16 byte long arry +* *Key Key to encrypt data with is a 16 byte long arry +***************************************************************************************** +*/ +void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) +{ + unsigned char i; + unsigned char Row,Collum; + unsigned char Round = 0x00; + unsigned char Round_Key[16]; + + //Copy input to State arry + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = Data[Row + (4*Collum)]; + } + } + + //Copy key to round key + for(i = 0; i < 16; i++) + { + Round_Key[i] = Key[i]; + } + + //Add round key + AES_Add_Round_Key(Round_Key); + + //Preform 9 full rounds + for(Round = 1; Round < 10; Round++) + { + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Preform Row Shift + AES_Shift_Rows(); + + //Mix Collums + AES_Mix_Collums(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round key + AES_Add_Round_Key(Round_Key); + } + + //Last round whitout mix collums + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Shift rows + AES_Shift_Rows(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round Key + AES_Add_Round_Key(Round_Key); + + //Copy the State into the data array + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + Data[Row + (4*Collum)] = State[Row][Collum]; + } + } + +} + +/* +***************************************************************************************** +* Description : Function that add's the round key for the current round +* +* Arguments : *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Add_Round_Key(unsigned char *Round_Key) +{ + unsigned char Row,Collum; + + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; + } + } +} + +/* +***************************************************************************************** +* Description : Function that substitutes a byte with a byte from the S_Table +* +* Arguments : Byte The byte that will be substituted +* +* Return : The return is the found byte in the S_Table +***************************************************************************************** +*/ +static unsigned char AES_Sub_Byte(unsigned char Byte) +{ + unsigned char S_Row,S_Collum; + unsigned char S_Byte; + + //Split byte up in Row and Collum + S_Row = ((Byte >> 4) & 0x0F); + S_Collum = (Byte & 0x0F); + + //Find the correct byte in the S_Table + S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); + + return S_Byte; +} + +/* +***************************************************************************************** +* Description : Function that preforms the shift row operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Shift_Rows() +{ + unsigned char Buffer; + + //Row 0 doesn't change + + //Shift Row 1 one left + //Store firt byte in buffer + Buffer = State[1][0]; + //Shift all bytes + State[1][0] = State[1][1]; + State[1][1] = State[1][2]; + State[1][2] = State[1][3]; + State[1][3] = Buffer; + + //Shift row 2 two left + Buffer = State[2][0]; + State[2][0] = State[2][2]; + State[2][2] = Buffer; + Buffer = State[2][1]; + State[2][1] = State[2][3]; + State[2][3] = Buffer; + + //Shift row 3 three left + Buffer = State[3][3]; + State[3][3] = State[3][2]; + State[3][2] = State[3][1]; + State[3][1] = State[3][0]; + State[3][0] = Buffer; +} + +/* +***************************************************************************************** +* Description : Function that preforms the Mix Collums operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Mix_Collums() +{ + unsigned char Row,Collum; + unsigned char a[4], b[4]; + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + a[Row] = State[Row][Collum]; + b[Row] = (State[Row][Collum] << 1); + + if((State[Row][Collum] & 0x80) == 0x80) + { + b[Row] = b[Row] ^ 0x1B; + } + } + State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; + State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; + State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; + State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; + } +} + +/* +***************************************************************************************** +* Description : Function that calculaties the round key for the current round +* +* Arguments : Round Number of current Round +* *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) +{ + unsigned char i,j; + unsigned char b; + unsigned char Temp[4]; + unsigned char Buffer; + unsigned char Rcon; + + //Calculate first Temp + //Copy laste byte from previous key + for(i = 0; i < 4; i++) + { + Temp[i] = Round_Key[i+12]; + } + + //Rotate Temp + Buffer = Temp[0]; + Temp[0] = Temp[1]; + Temp[1] = Temp[2]; + Temp[2] = Temp[3]; + Temp[3] = Buffer; + + //Substitute Temp + for(i = 0; i < 4; i++) + { + Temp[i] = AES_Sub_Byte(Temp[i]); + } + + //Calculate Rcon + Rcon = 0x01; + while(Round != 1) + { + b = Rcon & 0x80; + Rcon = Rcon << 1; + if(b == 0x80) + { + Rcon = Rcon ^ 0x1b; + } + Round--; + } + + //XOR Rcon + Temp[0] = Temp[0] ^ Rcon; + + //Calculate new key + for(i = 0; i < 4; i++) + { + for(j = 0; j < 4; j++) + { + Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; + Temp[j] = Round_Key[j + (4*i)]; + } + } +} + +#endif // defined(USE_IDEETRON_AES) diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/lmic.c b/lib/arduino-lmic-master/src/aes/lmic.c similarity index 93% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/lmic.c rename to lib/arduino-lmic-master/src/aes/lmic.c index 55a68769..2a5bca35 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/lmic.c +++ b/lib/arduino-lmic-master/src/aes/lmic.c @@ -1,370 +1,386 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -#include "../lmic/oslmic.h" - -#if defined(USE_ORIGINAL_AES) - -#define AES_MICSUB 0x30 // internal use only - -static CONST_TABLE(u4_t, AES_RCON)[10] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, - 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 -}; - -static CONST_TABLE(u1_t, AES_S)[256] = { - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, -}; - -static CONST_TABLE(u4_t, AES_E1)[256] = { - 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, - 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, - 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, - 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, - 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, - 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, - 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, - 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, - 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, - 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, - 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, - 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, - 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, - 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, - 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, - 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, - 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, - 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, - 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, - 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, - 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, - 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, - 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, - 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, - 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, - 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, - 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, - 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, - 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, - 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, - 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, - 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, -}; - -static CONST_TABLE(u4_t, AES_E2)[256] = { - 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, - 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, - 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, - 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, - 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, - 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, - 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, - 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, - 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, - 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, - 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, - 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, - 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, - 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, - 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, - 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, - 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, - 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, - 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, - 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, - 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, - 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, - 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, - 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, - 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, - 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, - 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, - 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, - 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, - 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, - 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, - 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, -}; - -static CONST_TABLE(u4_t, AES_E3)[256] = { - 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, - 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, - 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, - 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, - 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, - 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, - 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, - 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, - 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, - 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, - 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, - 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, - 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, - 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, - 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, - 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, - 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, - 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, - 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, - 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, - 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, - 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, - 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, - 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, - 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, - 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, - 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, - 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, - 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, - 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, - 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, - 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, -}; - -static CONST_TABLE(u4_t, AES_E4)[256] = { - 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, - 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, - 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, - 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, - 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, - 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, - 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, - 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, - 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, - 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, - 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, - 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, - 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, - 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, - 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, - 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, - 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, - 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, - 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, - 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, - 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, - 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, - 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, - 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, - 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, - 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, - 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, - 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, - 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, - 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, - 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, - 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, -}; - -#define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3]) -#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v) -#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) ) - -#define u1(v) ((u1_t)(v)) - -#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \ - r2 = ki[i+2]; \ - r3 = ki[i+3]; \ - r0 = ki[i] - -#define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \ - r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \ - r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \ - r0 ^= TABLE_GET_U4(AES_E1, (i>>24)) - -#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ - a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \ - a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \ - a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \ - a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) ) - -// global area for passing parameters (aux, key) and for storing round keys -u4_t AESAUX[16/sizeof(u4_t)]; -u4_t AESKEY[11*16/sizeof(u4_t)]; - -// generate 1+10 roundkeys for encryption with 128-bit key -// read 128-bit key from AESKEY in MSBF, generate roundkey words in place -static void aesroundkeys () { - int i; - u4_t b; - - for( i=0; i<4; i++) { - AESKEY[i] = swapmsbf(AESKEY[i]); - } - - b = AESKEY[3]; - for( ; i<44; i++ ) { - if( i%4==0 ) { - // b = SubWord(RotWord(b)) xor Rcon[i/4] - b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^ - ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^ - ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^ - ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^ - TABLE_GET_U4(AES_RCON, (i-4)/4); - } - AESKEY[i] = b ^= AESKEY[i-4]; - } -} - -u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { - - aesroundkeys(); - - if( mode & AES_MICNOAUX ) { - AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0; - } else { - AESAUX[0] = swapmsbf(AESAUX[0]); - AESAUX[1] = swapmsbf(AESAUX[1]); - AESAUX[2] = swapmsbf(AESAUX[2]); - AESAUX[3] = swapmsbf(AESAUX[3]); - } - - while( (signed char)len > 0 ) { - u4_t a0, a1, a2, a3; - u4_t t0, t1, t2, t3; - u4_t *ki, *ke; - - // load input block - if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block - a0 = AESAUX[0]; - a1 = AESAUX[1]; - a2 = AESAUX[2]; - a3 = AESAUX[3]; - } - else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block - a0 = a1 = a2 = a3 = 0; // load null block - mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2 - } else - LOADDATA: { // load data block (partially) - for(t0=0; t0<16; t0++) { - t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block - do { - // compute CMAC subkey K1 and K2 - t0 = a0 >> 31; // save MSB - a0 = (a0 << 1) | (a1 >> 31); - a1 = (a1 << 1) | (a2 >> 31); - a2 = (a2 << 1) | (a3 >> 31); - a3 = (a3 << 1); - if( t0 ) a3 ^= 0x87; - } while( --t1 ); - - AESAUX[0] ^= a0; - AESAUX[1] ^= a1; - AESAUX[2] ^= a2; - AESAUX[3] ^= a3; - mode &= ~AES_MICSUB; - goto LOADDATA; - } else { - // save cipher block as new iv - AESAUX[0] = a0; - AESAUX[1] = a1; - AESAUX[2] = a2; - AESAUX[3] = a3; - } - } else { // CIPHER - if( mode & AES_CTR ) { // xor block (partially) - t0 = (len > 16) ? 16: len; - for(t1=0; t1>24); - a0 <<= 8; - if((t1&3)==3) { - a0 = a1; - a1 = a2; - a2 = a3; - } - } - // update counter - AESAUX[3]++; - } else { // ECB - // store block - msbf4_write(buf+0, a0); - msbf4_write(buf+4, a1); - msbf4_write(buf+8, a2); - msbf4_write(buf+12, a3); - } - } - - // update block state - if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) { - buf += 16; - len -= 16; - } - mode |= AES_MICNOAUX; - } - return AESAUX[0]; -} - -#endif +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../lmic/oslmic.h" + +#if defined(USE_ORIGINAL_AES) + +#define AES_MICSUB 0x30 // internal use only + +static CONST_TABLE(u4_t, AES_RCON)[10] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 +}; + +static CONST_TABLE(u1_t, AES_S)[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static CONST_TABLE(u4_t, AES_E1)[256] = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, + 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, + 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, + 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, + 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, + 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, + 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, + 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, + 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, + 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, + 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, + 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, + 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, + 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, + 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, + 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, + 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, + 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, +}; + +static CONST_TABLE(u4_t, AES_E2)[256] = { + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, + 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, + 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, + 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, + 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, + 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, + 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, + 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, + 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, + 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, + 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, + 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, + 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, + 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, + 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, + 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, + 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, + 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, + 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, + 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, + 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, + 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, + 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, + 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, + 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, + 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, +}; + +static CONST_TABLE(u4_t, AES_E3)[256] = { + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, + 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, + 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, + 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, + 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, + 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, + 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, + 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, + 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, + 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, + 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, + 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, + 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, + 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, + 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, + 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, + 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, + 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, + 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, + 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, + 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, + 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, + 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, + 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, + 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, + 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, +}; + +static CONST_TABLE(u4_t, AES_E4)[256] = { + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, +}; + +#define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3]) +#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v) +#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) ) + +#define u1(v) ((u1_t)(v)) + +#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \ + r2 = ki[i+2]; \ + r3 = ki[i+3]; \ + r0 = ki[i] + +#define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \ + r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \ + r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \ + r0 ^= TABLE_GET_U4(AES_E1, (i>>24)) + +#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \ + a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \ + a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) ) + +// global area for passing parameters (aux, key) and for storing round keys +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[11*16/sizeof(u4_t)]; + +// generate 1+10 roundkeys for encryption with 128-bit key +// read 128-bit key from AESKEY in MSBF, generate roundkey words in place +static void aesroundkeys () { + int i; + u4_t b; + + for( i=0; i<4; i++) { + AESKEY[i] = swapmsbf(AESKEY[i]); + } + + b = AESKEY[3]; + for( ; i<44; i++ ) { + if( i%4==0 ) { + // b = SubWord(RotWord(b)) xor Rcon[i/4] + b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^ + ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^ + ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^ + TABLE_GET_U4(AES_RCON, (i-4)/4); + } + AESKEY[i] = b ^= AESKEY[i-4]; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + + aesroundkeys(); + + if( mode & AES_MICNOAUX ) { + AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0; + } else { + AESAUX[0] = swapmsbf(AESAUX[0]); + AESAUX[1] = swapmsbf(AESAUX[1]); + AESAUX[2] = swapmsbf(AESAUX[2]); + AESAUX[3] = swapmsbf(AESAUX[3]); + } + + while( (signed char)len > 0 ) { + u4_t a0, a1, a2, a3; + u4_t t0, t1, t2, t3; + u4_t *ki, *ke; + + // load input block + if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block + a0 = AESAUX[0]; + a1 = AESAUX[1]; + a2 = AESAUX[2]; + a3 = AESAUX[3]; + } + else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block + a0 = a1 = a2 = a3 = 0; // load null block + mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2 + } else + LOADDATA: { // load data block (partially) + for(t0=0; t0<16; t0++) { + t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block + do { + // compute CMAC subkey K1 and K2 + t0 = a0 >> 31; // save MSB + a0 = (a0 << 1) | (a1 >> 31); + a1 = (a1 << 1) | (a2 >> 31); + a2 = (a2 << 1) | (a3 >> 31); + a3 = (a3 << 1); + if( t0 ) a3 ^= 0x87; + } while( --t1 ); + + AESAUX[0] ^= a0; + AESAUX[1] ^= a1; + AESAUX[2] ^= a2; + AESAUX[3] ^= a3; + mode &= ~AES_MICSUB; + goto LOADDATA; + } else { + // save cipher block as new iv + AESAUX[0] = a0; + AESAUX[1] = a1; + AESAUX[2] = a2; + AESAUX[3] = a3; + } + } else { // CIPHER + if( mode & AES_CTR ) { // xor block (partially) + t0 = (len > 16) ? 16: len; + for(t1=0; t1>24); + a0 <<= 8; + if((t1&3)==3) { + a0 = a1; + a1 = a2; + a2 = a3; + } + } + // update counter + AESAUX[3]++; + } else { // ECB + // store block + msbf4_write(buf+0, a0); + msbf4_write(buf+4, a1); + msbf4_write(buf+8, a2); + msbf4_write(buf+12, a3); + } + } + + // update block state + if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) { + buf += 16; + len -= 16; + } + mode |= AES_MICNOAUX; + } + return AESAUX[0]; +} + +#endif diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/other.c b/lib/arduino-lmic-master/src/aes/other.c similarity index 95% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/other.c rename to lib/arduino-lmic-master/src/aes/other.c index d0156222..52febdb0 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/aes/other.c +++ b/lib/arduino-lmic-master/src/aes/other.c @@ -1,145 +1,145 @@ -/******************************************************************************* - * Copyright (c) 2016 Matthijs Kooijman - * - * LICENSE - * - * 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, - * including, but not limited to, copying, modification and - * redistribution. - * - * NO WARRANTY OF ANY KIND IS PROVIDED. - *******************************************************************************/ - -/* - * The original LMIC AES implementation integrates raw AES encryption - * with CMAC and AES-CTR in a single piece of code. Most other AES - * implementations (only) offer raw single block AES encryption, so this - * file contains an implementation of CMAC and AES-CTR, and offers the - * same API through the os_aes() function as the original AES - * implementation. This file assumes that there is an encryption - * function available with this signature: - * - * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); - * - * That takes a single 16-byte buffer and encrypts it wit the given - * 16-byte key. - */ - -#include "../lmic/oslmic.h" - -#if !defined(USE_ORIGINAL_AES) - -// This should be defined elsewhere -void lmic_aes_encrypt(u1_t *data, u1_t *key); - -// global area for passing parameters (aux, key) -u4_t AESAUX[16/sizeof(u4_t)]; -u4_t AESKEY[16/sizeof(u4_t)]; - -// Shift the given buffer left one bit -static void shift_left(xref2u1_t buf, u1_t len) { - while (len--) { - u1_t next = len ? buf[1] : 0; - - u1_t val = (*buf << 1); - if (next & 0x80) - val |= 1; - *buf++ = val; - } -} - -// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, -// AESAUX is prepended to the message. AESAUX is used as working memory -// in any case. The CMAC result is returned in AESAUX as well. -static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { - if (prepend_aux) - lmic_aes_encrypt(AESaux, AESkey); - else - memset (AESaux, 0, 16); - - while (len > 0) { - u1_t need_padding = 0; - for (u1_t i = 0; i < 16; ++i, ++buf, --len) { - if (len == 0) { - // The message is padded with 0x80 and then zeroes. - // Since zeroes are no-op for xor, we can just skip them - // and leave AESAUX unchanged for them. - AESaux[i] ^= 0x80; - need_padding = 1; - break; - } - AESaux[i] ^= *buf; - } - - if (len == 0) { - // Final block, xor with K1 or K2. K1 and K2 are calculated - // by encrypting the all-zeroes block and then applying some - // shifts and xor on that. - u1_t final_key[16]; - memset(final_key, 0, sizeof(final_key)); - lmic_aes_encrypt(final_key, AESkey); - - // Calculate K1 - u1_t msb = final_key[0] & 0x80; - shift_left(final_key, sizeof(final_key)); - if (msb) - final_key[sizeof(final_key)-1] ^= 0x87; - - // If the final block was not complete, calculate K2 from K1 - if (need_padding) { - msb = final_key[0] & 0x80; - shift_left(final_key, sizeof(final_key)); - if (msb) - final_key[sizeof(final_key)-1] ^= 0x87; - } - - // Xor with K1 or K2 - for (u1_t i = 0; i < sizeof(final_key); ++i) - AESaux[i] ^= final_key[i]; - } - - lmic_aes_encrypt(AESaux, AESkey); - } -} - -// Run AES-CTR using the key in AESKEY and using AESAUX as the -// counter block. The last byte of the counter block will be incremented -// for every block. The given buffer will be encrypted in place. -static void os_aes_ctr (xref2u1_t buf, u2_t len) { - u1_t ctr[16]; - while (len) { - // Encrypt the counter block with the selected key - memcpy(ctr, AESaux, sizeof(ctr)); - lmic_aes_encrypt(ctr, AESkey); - - // Xor the payload with the resulting ciphertext - for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) - *buf ^= ctr[i]; - - // Increment the block index byte - AESaux[15]++; - } -} - -u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { - switch (mode & ~AES_MICNOAUX) { - case AES_MIC: - os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); - return os_rmsbf4(AESaux); - - case AES_ENC: - // TODO: Check / handle when len is not a multiple of 16 - for (u1_t i = 0; i < len; i += 16) - lmic_aes_encrypt(buf+i, AESkey); - break; - - case AES_CTR: - os_aes_ctr(buf, len); - break; - } - return 0; -} - -#endif // !defined(USE_ORIGINAL_AES) +/******************************************************************************* + * Copyright (c) 2016 Matthijs Kooijman + * + * LICENSE + * + * 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, + * including, but not limited to, copying, modification and + * redistribution. + * + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ + +/* + * The original LMIC AES implementation integrates raw AES encryption + * with CMAC and AES-CTR in a single piece of code. Most other AES + * implementations (only) offer raw single block AES encryption, so this + * file contains an implementation of CMAC and AES-CTR, and offers the + * same API through the os_aes() function as the original AES + * implementation. This file assumes that there is an encryption + * function available with this signature: + * + * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); + * + * That takes a single 16-byte buffer and encrypts it wit the given + * 16-byte key. + */ + +#include "../lmic/oslmic.h" + +#if !defined(USE_ORIGINAL_AES) + +// This should be defined elsewhere +void lmic_aes_encrypt(u1_t *data, u1_t *key); + +// global area for passing parameters (aux, key) and for storing round keys +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[11*16/sizeof(u4_t)]; + +// Shift the given buffer left one bit +static void shift_left(xref2u1_t buf, u1_t len) { + while (len--) { + u1_t next = len ? buf[1] : 0; + + u1_t val = (*buf << 1); + if (next & 0x80) + val |= 1; + *buf++ = val; + } +} + +// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, +// AESAUX is prepended to the message. AESAUX is used as working memory +// in any case. The CMAC result is returned in AESAUX as well. +static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { + if (prepend_aux) + lmic_aes_encrypt(AESaux, AESkey); + else + memset (AESaux, 0, 16); + + while (len > 0) { + u1_t need_padding = 0; + for (u1_t i = 0; i < 16; ++i, ++buf, --len) { + if (len == 0) { + // The message is padded with 0x80 and then zeroes. + // Since zeroes are no-op for xor, we can just skip them + // and leave AESAUX unchanged for them. + AESaux[i] ^= 0x80; + need_padding = 1; + break; + } + AESaux[i] ^= *buf; + } + + if (len == 0) { + // Final block, xor with K1 or K2. K1 and K2 are calculated + // by encrypting the all-zeroes block and then applying some + // shifts and xor on that. + u1_t final_key[16]; + memset(final_key, 0, sizeof(final_key)); + lmic_aes_encrypt(final_key, AESkey); + + // Calculate K1 + u1_t msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + + // If the final block was not complete, calculate K2 from K1 + if (need_padding) { + msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + } + + // Xor with K1 or K2 + for (u1_t i = 0; i < sizeof(final_key); ++i) + AESaux[i] ^= final_key[i]; + } + + lmic_aes_encrypt(AESaux, AESkey); + } +} + +// Run AES-CTR using the key in AESKEY and using AESAUX as the +// counter block. The last byte of the counter block will be incremented +// for every block. The given buffer will be encrypted in place. +static void os_aes_ctr (xref2u1_t buf, u2_t len) { + u1_t ctr[16]; + while (len) { + // Encrypt the counter block with the selected key + memcpy(ctr, AESaux, sizeof(ctr)); + lmic_aes_encrypt(ctr, AESkey); + + // Xor the payload with the resulting ciphertext + for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) + *buf ^= ctr[i]; + + // Increment the block index byte + AESaux[15]++; + } +} + +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { + switch (mode & ~AES_MICNOAUX) { + case AES_MIC: + os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); + return os_rmsbf4(AESaux); + + case AES_ENC: + // TODO: Check / handle when len is not a multiple of 16 + for (u1_t i = 0; i < len; i += 16) + lmic_aes_encrypt(buf+i, AESkey); + break; + + case AES_CTR: + os_aes_ctr(buf, len); + break; + } + return 0; +} + +#endif // !defined(USE_ORIGINAL_AES) diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.cpp b/lib/arduino-lmic-master/src/hal/hal.cpp similarity index 65% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.cpp rename to lib/arduino-lmic-master/src/hal/hal.cpp index 6ee413fa..a003a80e 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/hal/hal.cpp +++ b/lib/arduino-lmic-master/src/hal/hal.cpp @@ -1,269 +1,349 @@ -/******************************************************************************* - * Copyright (c) 2015 Matthijs Kooijman - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * This the HAL to run LMIC on top of the Arduino environment. - *******************************************************************************/ - -#include -#include -#include "../lmic.h" -#include "hal.h" -#include - -// ----------------------------------------------------------------------------- -// I/O - -static void hal_io_init () { - // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK - ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); - ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); - ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); - - #ifdef LMIC_SPI_PINS_IN_MAPPING - ASSERT(lmic_pins.mosi != LMIC_UNUSED_PIN - || lmic_pins.miso != LMIC_UNUSED_PIN - || lmic_pins.sck != LMIC_UNUSED_PIN); - #endif - - pinMode(lmic_pins.nss, OUTPUT); - if (lmic_pins.rxtx != LMIC_UNUSED_PIN) - pinMode(lmic_pins.rxtx, OUTPUT); - if (lmic_pins.rst != LMIC_UNUSED_PIN) - pinMode(lmic_pins.rst, OUTPUT); - - pinMode(lmic_pins.dio[0], INPUT); - if (lmic_pins.dio[1] != LMIC_UNUSED_PIN) - pinMode(lmic_pins.dio[1], INPUT); - if (lmic_pins.dio[2] != LMIC_UNUSED_PIN) - pinMode(lmic_pins.dio[2], INPUT); -} - -// val == 1 => tx 1 -void hal_pin_rxtx (u1_t val) { - if (lmic_pins.rxtx != LMIC_UNUSED_PIN) - digitalWrite(lmic_pins.rxtx, val); -} - -// set radio RST pin to given value (or keep floating!) -void hal_pin_rst (u1_t val) { - if (lmic_pins.rst == LMIC_UNUSED_PIN) - return; - - if(val == 0 || val == 1) { // drive pin - pinMode(lmic_pins.rst, OUTPUT); - digitalWrite(lmic_pins.rst, val); - } else { // keep pin floating - pinMode(lmic_pins.rst, INPUT); - } -} - -static bool dio_states[NUM_DIO] = {0}; - -static void hal_io_check() { - uint8_t i; - for (i = 0; i < NUM_DIO; ++i) { - if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) - continue; - - if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { - dio_states[i] = !dio_states[i]; - if (dio_states[i]) - radio_irq_handler(i); - } - } -} - -// ----------------------------------------------------------------------------- -// SPI - -static const SPISettings settings(10E6, MSBFIRST, SPI_MODE0); - -// Initialize SPI, allowing override of default SPI pins on certain boards. -static void hal_spi_init () { - #if defined(ESP32) - // On the ESP32 the default is _use_hw_ss(false), - // so we can set the last parameter to anything. - SPI.begin(lmic_pins.sck, lmic_pins.miso, lmic_pins.mosi, 0x00); - #elif defined(NRF51) - SPI.begin(lmic_pins.sck, lmic_pins.mosi, lmic_pins.miso); - #else - //unknown board, or board without SPI pin select ability - SPI.begin(); - #endif -} - -void hal_pin_nss (u1_t val) { - if (!val) - SPI.beginTransaction(settings); - else - SPI.endTransaction(); - - //Serial.println(val?">>":"<<"); - digitalWrite(lmic_pins.nss, val); -} - -// perform SPI transaction with radio -u1_t hal_spi (u1_t out) { - u1_t res = SPI.transfer(out); -/* - Serial.print(">"); - Serial.print(out, HEX); - Serial.print("<"); - Serial.println(res, HEX); - */ - return res; -} - -// ----------------------------------------------------------------------------- -// TIME - -static void hal_time_init () { - // Nothing to do -} - -u4_t hal_ticks () { - // Because micros() is scaled down in this function, micros() will - // overflow before the tick timer should, causing the tick timer to - // miss a significant part of its values if not corrected. To fix - // this, the "overflow" serves as an overflow area for the micros() - // counter. It consists of three parts: - // - The US_PER_OSTICK upper bits are effectively an extension for - // the micros() counter and are added to the result of this - // function. - // - The next bit overlaps with the most significant bit of - // micros(). This is used to detect micros() overflows. - // - The remaining bits are always zero. - // - // By comparing the overlapping bit with the corresponding bit in - // the micros() return value, overflows can be detected and the - // upper bits are incremented. This is done using some clever - // bitwise operations, to remove the need for comparisons and a - // jumps, which should result in efficient code. By avoiding shifts - // other than by multiples of 8 as much as possible, this is also - // efficient on AVR (which only has 1-bit shifts). - static uint8_t overflow = 0; - - // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, - // the others will be the lower bits of our return value. - uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; - // Most significant byte of scaled - uint8_t msb = scaled >> 24; - // Mask pointing to the overlapping bit in msb and overflow. - const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); - // Update overflow. If the overlapping bit is different - // between overflow and msb, it is added to the stored value, - // so the overlapping bit becomes equal again and, if it changed - // from 1 to 0, the upper bits are incremented. - overflow += (msb ^ overflow) & mask; - - // Return the scaled value with the upper bits of stored added. The - // overlapping bit will be equal and the lower bits will be 0, so - // bitwise or is a no-op for them. - return scaled | ((uint32_t)overflow << 24); - - // 0 leads to correct, but overly complex code (it could just return - // micros() unmodified), 8 leaves no room for the overlapping bit. - static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); -} - -// Returns the number of ticks until time. Negative values indicate that -// time has already passed. -static s4_t delta_time(u4_t time) { - return (s4_t)(time - hal_ticks()); -} - -void hal_waitUntil (u4_t time) { - s4_t delta = delta_time(time); - // From delayMicroseconds docs: Currently, the largest value that - // will produce an accurate delay is 16383. - while (delta > (16000 / US_PER_OSTICK)) { - delay(16); - delta -= (16000 / US_PER_OSTICK); - } - if (delta > 0) - delayMicroseconds(delta * US_PER_OSTICK); -} - -// check and rewind for target time -u1_t hal_checkTimer (u4_t time) { - // No need to schedule wakeup, since we're not sleeping - return delta_time(time) <= 0; -} - -static uint8_t irqlevel = 0; - -void hal_disableIRQs () { - noInterrupts(); - irqlevel++; -} - -void hal_enableIRQs () { - if(--irqlevel == 0) { - interrupts(); - - // Instead of using proper interrupts (which are a bit tricky - // and/or not available on all pins on AVR), just poll the pin - // values. Since os_runloop disables and re-enables interrupts, - // putting this here makes sure we check at least once every - // loop. - // - // As an additional bonus, this prevents the can of worms that - // we would otherwise get for running SPI transfers inside ISRs - hal_io_check(); - } -} - -void hal_sleep () { - // Not implemented -} - -// ----------------------------------------------------------------------------- - -#if defined(LMIC_PRINTF_TO) -static int uart_putchar (char c, FILE *) -{ - LMIC_PRINTF_TO.write(c) ; - return 0 ; -} - -void hal_printf_init() { - // create a FILE structure to reference our UART output function - static FILE uartout; - memset(&uartout, 0, sizeof(uartout)); - - // fill in the UART file descriptor with pointer to writer. - fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); - - // The uart is the standard output device STDOUT. - stdout = &uartout ; -} -#endif // defined(LMIC_PRINTF_TO) - -void hal_init () { - // configure radio I/O and interrupt handler - hal_io_init(); - // configure radio SPI - hal_spi_init(); - // configure timer and interrupt handler - hal_time_init(); -#if defined(LMIC_PRINTF_TO) - // printf support - hal_printf_init(); -#endif -} - -void hal_failed (const char *file, u2_t line) { -#if defined(LMIC_FAILURE_TO) - LMIC_FAILURE_TO.println("FAILURE "); - LMIC_FAILURE_TO.print(file); - LMIC_FAILURE_TO.print(':'); - LMIC_FAILURE_TO.println(line); - LMIC_FAILURE_TO.flush(); -#endif - hal_disableIRQs(); - while(1); -} +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ + +#include +#include +#include "../lmic.h" +#include "hal.h" +#include + +// ----------------------------------------------------------------------------- +// I/O + +static const lmic_pinmap *plmic_pins; + +static void hal_interrupt_init(); // Fwd declaration + +static void hal_io_init () { + // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK + ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN); + ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN); + +// Serial.print("nss: "); Serial.println(plmic_pins->nss); +// Serial.print("rst: "); Serial.println(plmic_pins->rst); +// Serial.print("dio[0]: "); Serial.println(plmic_pins->dio[0]); +// Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]); +// Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]); + + pinMode(plmic_pins->nss, OUTPUT); + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) + pinMode(plmic_pins->rxtx, OUTPUT); + if (plmic_pins->rst != LMIC_UNUSED_PIN) + pinMode(plmic_pins->rst, OUTPUT); + + hal_interrupt_init(); +} + +// val == 1 => tx +void hal_pin_rxtx (u1_t val) { + if (plmic_pins->rxtx != LMIC_UNUSED_PIN) + digitalWrite(plmic_pins->rxtx, val != plmic_pins->rxtx_rx_active); +} + +// set radio RST pin to given value (or keep floating!) +void hal_pin_rst (u1_t val) { + if (plmic_pins->rst == LMIC_UNUSED_PIN) + return; + + if(val == 0 || val == 1) { // drive pin + pinMode(plmic_pins->rst, OUTPUT); + digitalWrite(plmic_pins->rst, val); + } else { // keep pin floating + pinMode(plmic_pins->rst, INPUT); + } +} + +s1_t hal_getRssiCal (void) { + return plmic_pins->rssi_cal; +} + +#if !defined(LMIC_USE_INTERRUPTS) +static void hal_interrupt_init() { + pinMode(plmic_pins->dio[0], INPUT); + if (plmic_pins->dio[1] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[1], INPUT); + if (plmic_pins->dio[2] != LMIC_UNUSED_PIN) + pinMode(plmic_pins->dio[2], INPUT); +} + +static bool dio_states[NUM_DIO] = {0}; +static void hal_io_check() { + uint8_t i; + for (i = 0; i < NUM_DIO; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + if (dio_states[i] != digitalRead(plmic_pins->dio[i])) { + dio_states[i] = !dio_states[i]; + if (dio_states[i]) + radio_irq_handler(i); + } + } +} + +#else +// Interrupt handlers +static ostime_t interrupt_time[NUM_DIO] = {0}; + +static void hal_isrPin0() { + ostime_t now = os_getTime(); + interrupt_time[0] = now ? now : 1; +} +static void hal_isrPin1() { + ostime_t now = os_getTime(); + interrupt_time[1] = now ? now : 1; +} +static void hal_isrPin2() { + ostime_t now = os_getTime(); + interrupt_time[2] = now ? now : 1; +} + +typedef void (*isr_t)(); +static isr_t interrupt_fns[NUM_DIO] = {hal_isrPin0, hal_isrPin1, hal_isrPin2}; + +static void hal_interrupt_init() { + for (uint8_t i = 0; i < NUM_DIO; ++i) { + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + attachInterrupt(digitalPinToInterrupt(plmic_pins->dio[i]), interrupt_fns[i], RISING); + } +} + +static void hal_io_check() { + uint8_t i; + for (i = 0; i < NUM_DIO; ++i) { + ostime_t iTime; + if (plmic_pins->dio[i] == LMIC_UNUSED_PIN) + continue; + + iTime = interrupt_time[i]; + if (iTime) { + interrupt_time[i] = 0; + radio_irq_handler_v2(i, iTime); + } + } +} +#endif // LMIC_USE_INTERRUPTS + +// ----------------------------------------------------------------------------- +// SPI + +static void hal_spi_init () { + SPI.begin(plmic_pins->sck, plmic_pins->miso, plmic_pins->mosi, plmic_pins->nss); +} + +void hal_pin_nss (u1_t val) { + if (!val) { + uint32_t spi_freq; + + if ((spi_freq = plmic_pins->spi_freq) == 0) + spi_freq = LMIC_SPI_FREQ; + + SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0); + SPI.beginTransaction(settings); + } else { + SPI.endTransaction(); + } + + //Serial.println(val?">>":"<<"); + digitalWrite(plmic_pins->nss, val); +} + +// perform SPI transaction with radio +u1_t hal_spi (u1_t out) { + u1_t res = SPI.transfer(out); +/* + Serial.print(">"); + Serial.print(out, HEX); + Serial.print("<"); + Serial.println(res, HEX); + */ + return res; +} + +// ----------------------------------------------------------------------------- +// TIME + +static void hal_time_init () { + // Nothing to do +} + +u4_t hal_ticks () { + // Because micros() is scaled down in this function, micros() will + // overflow before the tick timer should, causing the tick timer to + // miss a significant part of its values if not corrected. To fix + // this, the "overflow" serves as an overflow area for the micros() + // counter. It consists of three parts: + // - The US_PER_OSTICK upper bits are effectively an extension for + // the micros() counter and are added to the result of this + // function. + // - The next bit overlaps with the most significant bit of + // micros(). This is used to detect micros() overflows. + // - The remaining bits are always zero. + // + // By comparing the overlapping bit with the corresponding bit in + // the micros() return value, overflows can be detected and the + // upper bits are incremented. This is done using some clever + // bitwise operations, to remove the need for comparisons and a + // jumps, which should result in efficient code. By avoiding shifts + // other than by multiples of 8 as much as possible, this is also + // efficient on AVR (which only has 1-bit shifts). + static uint8_t overflow = 0; + + // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, + // the others will be the lower bits of our return value. + uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; + // Most significant byte of scaled + uint8_t msb = scaled >> 24; + // Mask pointing to the overlapping bit in msb and overflow. + const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); + // Update overflow. If the overlapping bit is different + // between overflow and msb, it is added to the stored value, + // so the overlapping bit becomes equal again and, if it changed + // from 1 to 0, the upper bits are incremented. + overflow += (msb ^ overflow) & mask; + + // Return the scaled value with the upper bits of stored added. The + // overlapping bit will be equal and the lower bits will be 0, so + // bitwise or is a no-op for them. + return scaled | ((uint32_t)overflow << 24); + + // 0 leads to correct, but overly complex code (it could just return + // micros() unmodified), 8 leaves no room for the overlapping bit. + static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); +} + +// Returns the number of ticks until time. Negative values indicate that +// time has already passed. +static s4_t delta_time(u4_t time) { + return (s4_t)(time - hal_ticks()); +} + +void hal_waitUntil (u4_t time) { + s4_t delta = delta_time(time); + // From delayMicroseconds docs: Currently, the largest value that + // will produce an accurate delay is 16383. + while (delta > (16000 / US_PER_OSTICK)) { + delay(16); + delta -= (16000 / US_PER_OSTICK); + } + if (delta > 0) + delayMicroseconds(delta * US_PER_OSTICK); +} + +// check and rewind for target time +u1_t hal_checkTimer (u4_t time) { + // No need to schedule wakeup, since we're not sleeping + return delta_time(time) <= 0; +} + +static uint8_t irqlevel = 0; + +void hal_disableIRQs () { + noInterrupts(); + irqlevel++; +} + +void hal_enableIRQs () { + if(--irqlevel == 0) { + interrupts(); + + // Instead of using proper interrupts (which are a bit tricky + // and/or not available on all pins on AVR), just poll the pin + // values. Since os_runloop disables and re-enables interrupts, + // putting this here makes sure we check at least once every + // loop. + // + // As an additional bonus, this prevents the can of worms that + // we would otherwise get for running SPI transfers inside ISRs + hal_io_check(); + } +} + +void hal_sleep () { + // Not implemented +} + +// ----------------------------------------------------------------------------- + +#if defined(LMIC_PRINTF_TO) +#if !defined(__AVR) +static ssize_t uart_putchar (void *, const char *buf, size_t len) { + return LMIC_PRINTF_TO.write((const uint8_t *)buf, len); +} + +static cookie_io_functions_t functions = + { + .read = NULL, + .write = uart_putchar, + .seek = NULL, + .close = NULL + }; + +void hal_printf_init() { + stdout = fopencookie(NULL, "w", functions); + if (stdout != nullptr) { + setvbuf(stdout, NULL, _IONBF, 0); + } +} +#else // defined(__AVR) +static int uart_putchar (char c, FILE *) +{ + LMIC_PRINTF_TO.write(c) ; + return 0 ; +} + +void hal_printf_init() { + // create a FILE structure to reference our UART output function + static FILE uartout; + memset(&uartout, 0, sizeof(uartout)); + + // fill in the UART file descriptor with pointer to writer. + fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); + + // The uart is the standard output device STDOUT. + stdout = &uartout ; +} + +#endif // !defined(ESP8266) || defined(ESP31B) || defined(ESP32) +#endif // defined(LMIC_PRINTF_TO) + +void hal_init (void) { + hal_init_ex(&lmic_pins); +} + +void hal_init_ex (const void *pContext) { + plmic_pins = (const lmic_pinmap *)pContext; + + // configure radio I/O and interrupt handler + hal_io_init(); + // configure radio SPI + hal_spi_init(); + // configure timer and interrupt handler + hal_time_init(); +#if defined(LMIC_PRINTF_TO) + // printf support + hal_printf_init(); +#endif +} + +void hal_failed (const char *file, u2_t line) { +#if defined(LMIC_FAILURE_TO) + LMIC_FAILURE_TO.println("FAILURE "); + LMIC_FAILURE_TO.print(file); + LMIC_FAILURE_TO.print(':'); + LMIC_FAILURE_TO.println(line); + LMIC_FAILURE_TO.flush(); +#endif + hal_disableIRQs(); + while(1); +} diff --git a/lib/arduino-lmic-master/src/hal/hal.h b/lib/arduino-lmic-master/src/hal/hal.h new file mode 100644 index 00000000..6a612327 --- /dev/null +++ b/lib/arduino-lmic-master/src/hal/hal.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2015-2016 Matthijs Kooijman + * Copyright (c) 2016-2018 MCCI Corporation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ +#ifndef _hal_hal_h_ +#define _hal_hal_h_ + +static const int NUM_DIO = 3; + +// be careful of alignment below. +struct lmic_pinmap { + u1_t nss; // byte 0: pin for select + u1_t rxtx; // byte 1: pin for rx/tx control + u1_t rst; // byte 2: pin for reset + u1_t dio[NUM_DIO]; // bytes 3..5: pins for DIO0, DOI1, DIO2 + u1_t mosi; // byte 9: pin for master out / slave in (write to LORA chip) + u1_t miso; // byte 10: pin for master in / slave out (read from LORA chip) + u1_t sck; // byte 11: pin for serial clock by master + // true if we must set rxtx for rx_active, false for tx_active + u1_t rxtx_rx_active; // byte 6: polarity of rxtx active + s1_t rssi_cal; // byte 7: cal in dB -- added to RSSI + // measured prior to decision. + // Must include noise guardband! + u4_t spi_freq; // bytes 8..11: SPI freq in Hz. +}; + +// Use this for any unused pins. +const u1_t LMIC_UNUSED_PIN = 0xff; + +// Declared here, to be defined and initialized by the application +// use os_init_ex() if you want not to use a const table. +extern const lmic_pinmap lmic_pins; + +#endif // _hal_hal_h_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic.h b/lib/arduino-lmic-master/src/lmic.h similarity index 60% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic.h rename to lib/arduino-lmic-master/src/lmic.h index f081e82d..b74c651c 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic.h +++ b/lib/arduino-lmic-master/src/lmic.h @@ -1,9 +1,11 @@ -#ifdef __cplusplus -extern "C"{ -#endif - -#include "lmic/lmic.h" - -#ifdef __cplusplus -} -#endif +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lmic.h" +#include "lmic/lmic_bandplan.h" +#include "lmic/lmic_util.h" + +#ifdef __cplusplus +} +#endif diff --git a/lib/arduino-lmic-master/src/lmic/config.h b/lib/arduino-lmic-master/src/lmic/config.h new file mode 100644 index 00000000..17509078 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/config.h @@ -0,0 +1,174 @@ +#ifndef _lmic_config_h_ +#define _lmic_config_h_ + +// In the original LMIC code, these config values were defined on the +// gcc commandline. Since Arduino does not allow easily modifying the +// compiler commandline unless you modify the BSP, you have two choices: +// +// - edit {libraries}/arduino-lmic/project_config/lmic_project_config.h; +// - use a BSP like the MCCI Arduino BSPs, which get the configuration +// from the boards.txt file through a menu option. +// +// You definitely should not edit this file. + +// set up preconditions, and load configuration if needed. +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +// check post-conditions. + +// make sure that we have exactly one target region defined. +#if CFG_LMIC_REGION_MASK == 0 +# define CFG_eu868 1 +#elif (CFG_LMIC_REGION_MASK & (-CFG_LMIC_REGION_MASK)) != CFG_LMIC_REGION_MASK +# error You can define at most one of CFG_... variables +#elif (CFG_LMIC_REGION_MASK & LMIC_REGIONS_SUPPORTED) == 0 +# error The selected CFG_... region is not supported yet. +#endif + +// make sure that LMIC_COUNTRY_CODE is defined. +#ifndef LMIC_COUNTRY_CODE +# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_NONE +#endif + +// if the country code is Japan, then the region must be AS923 +#if LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP && CFG_region != LMIC_REGION_as923 +# error "If country code is JP, then region must be AS923" +#endif + +// check for internal consistency +#if !(CFG_LMIC_EU_like || CFG_LMIC_US_like) +# error "Internal error: Neither EU-like nor US-like!" +#endif + +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +//#define CFG_sx1276_radio 1 + +// ensure that a radio is defined. +#if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio)) +# warning Target radio not defined, assuming CFG_sx1276_radio +#define CFG_sx1276_radio 1 +#elif defined(CFG_sx1272_radio) && defined(CFG_sx1276_radio) +# error You can define at most one of CFG_sx1272_radio and CF_sx1276_radio +#endif + +// LMIC requires ticks to be 15.5μs - 100 μs long +#ifndef OSTICKS_PER_SEC +// 16 μs per tick +# ifndef US_PER_OSTICK_EXPONENT +# define US_PER_OSTICK_EXPONENT 4 +# endif +# define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +# define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) +#endif /* OSTICKS_PER_SEC */ + +#if ! (10000 <= OSTICKS_PER_SEC && OSTICKS_PER_SEC < 64516) +# error LMIC requires ticks to be 15.5 us to 100 us long +#endif + +// Change the SPI clock speed if you encounter errors +// communicating with the radio. +// The standard range is 125kHz-8MHz, but some boards can go faster. +#ifndef LMIC_SPI_FREQ +#define LMIC_SPI_FREQ 1E6 +#endif + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +#ifndef LMIC_DEBUG_LEVEL +#define LMIC_DEBUG_LEVEL 0 +#endif + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Enable this to use interrupt handler routines listening for RISING signals. +// Otherwise, the library polls digital input lines for changes. +//#define LMIC_USE_INTERRUPTS + +// If DISABLE_LMIC_FAILURE_TO is defined, runtime assertion failures +// silently halt execution. Otherwise, LMIC_FAILURE_TO should be defined +// as the name of an object derived from Print, which will be used for +// displaying runtime assertion failures. If you say nothing in your +// lmic_project_config.h, runtime assertion failures are displayed +// using the Serial object. +#if ! defined(DISABLE_LMIC_FAILURE_TO) && ! defined(LMIC_FAILURE_TO) +#define LMIC_FAILURE_TO Serial +#endif + +// define this in lmic_project_config.h to disable all code related to joining +//#define DISABLE_JOIN +// define this in lmic_project_config.h to disable all code related to ping +//#define DISABLE_PING +// define this in lmic_project_config.h to disable all code related to beacon tracking. +// Requires ping to be disabled too +//#define DISABLE_BEACONS + +// 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 +// 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 + +// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the +// same on RX. This ensures that gateways can talk to nodes and vice +// versa, but gateways will not hear other gateways and nodes will not +// hear other nodes. By defining this macro in lmic_project_config.h, +// this inversion is disabled and this node can hear other nodes. If +// two nodes both have this macro set, they can talk to each other +// (but they can no longer hear gateways). This should probably only +// be used when debugging and/or when talking to the radio directly +// (e.g. like in the "raw" example). +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +// #define USE_IDEETRON_AES + +#if ! (defined(USE_ORIGINAL_AES) || defined(USE_IDEETRON_AES)) +# define USE_IDEETRON_AES +#endif + +#if defined(USE_ORIGINAL_AES) && defined(USE_IDEETRON_AES) +# error "You may define at most one of USE_ORIGINAL_AES and USE_IDEETRON_AES" +#endif + +// LMIC_DISABLE_DR_LEGACY +// turn off legacy DR_* symbols that vary by bandplan. +// Older code uses these for configuration. EU868_DR_*, US915_DR_* +// etc symbols are prefered, but breaking older code is inconvenient for +// everybody. We don't want to use DR_* in the LMIC itself, so we provide +// this #define to allow them to be removed. +#if !defined(LMIC_DR_LEGACY) +# if !defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 1 +# else // defined(LMIC_DISABLE_DR_LEGACY) +# define LMIC_DR_LEGACY 0 +# endif // defined(LMIC_DISABLE_DR_LEGACY) +#endif // LMIC_DR_LEGACY + +#endif // _lmic_config_h_ diff --git a/lib/arduino-lmic-master/src/lmic/hal.h b/lib/arduino-lmic-master/src/lmic/hal.h new file mode 100644 index 00000000..51866d9a --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/hal.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016, 2018 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _hal_hpp_ +#define _hal_hpp_ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* + * initialize hardware (IO, SPI, TIMER, IRQ). + */ +void hal_init (void); + +/* + * initialize hardware, passing in platform-specific context + */ +void hal_init_ex (const void *pContext); + +/* + * drive radio NSS pin (0=low, 1=high). + */ +void hal_pin_nss (u1_t val); + +/* + * drive radio RX/TX pins (0=rx, 1=tx). + */ +void hal_pin_rxtx (u1_t val); + +/* + * control radio RST pin (0=low, 1=high, 2=floating) + */ +void hal_pin_rst (u1_t val); + +/* + * perform 8-bit SPI transaction with radio. + * - write given byte 'outval' + * - read byte and return value + */ +u1_t hal_spi (u1_t outval); + +/* + * disable all CPU interrupts. + * - might be invoked nested + * - will be followed by matching call to hal_enableIRQs() + */ +void hal_disableIRQs (void); + +/* + * enable CPU interrupts. + */ +void hal_enableIRQs (void); + +/* + * put system and CPU in low-power mode, sleep until interrupt. + */ +void hal_sleep (void); + +/* + * return 32-bit system time in ticks. + */ +u4_t hal_ticks (void); + +/* + * busy-wait until specified timestamp (in ticks) is reached. + */ +void hal_waitUntil (u4_t time); + +/* + * check and rewind timer for target time. + * - return 1 if target time is close + * - otherwise rewind timer for target time or full period and return 0 + */ +u1_t hal_checkTimer (u4_t targettime); + +/* + * perform fatal failure action. + * - called by assertions + * - action could be HALT or reboot + */ +void hal_failed (const char *file, u2_t line); + +/* + * get the calibration value for radio_rssi + */ +s1_t hal_getRssiCal (void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _hal_hpp_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.c b/lib/arduino-lmic-master/src/lmic/lmic.c similarity index 71% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.c rename to lib/arduino-lmic-master/src/lmic/lmic.c index a6e42933..c6c0a867 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.c +++ b/lib/arduino-lmic-master/src/lmic/lmic.c @@ -1,2382 +1,2043 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -//! \file -#include "lmic.h" - -#if defined(DISABLE_BEACONS) && !defined(DISABLE_PING) -#error Ping needs beacon tracking -#endif - -#if !defined(MINRX_SYMS) -#define MINRX_SYMS 5 -#endif // !defined(MINRX_SYMS) -#define PAMBL_SYMS 8 -#define PAMBL_FSK 5 -#define PRERX_FSK 1 -#define RXLEN_FSK (1+5+2) - -#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) -#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) -#define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) -#define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1) -#define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2) -#define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2) -#define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms) -#define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms) -#define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms) -#define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN) -#if defined(CFG_eu868) -#define DNW2_SAFETY_ZONE ms2osticks(3000) -#endif -#if defined(CFG_us915) -#define DNW2_SAFETY_ZONE ms2osticks(750) -#endif - -// Special APIs - for development or testing -#define isTESTMODE() 0 - -DEFINE_LMIC; - - -// Fwd decls. -static void engineUpdate(void); -static void startScan (void); - - -// ================================================================================ -// BEG OS - default implementations for certain OS suport functions - -#if !defined(HAS_os_calls) - -#if !defined(os_rlsbf2) -u2_t os_rlsbf2 (xref2cu1_t buf) { - return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8)); -} -#endif - -#if !defined(os_rlsbf4) -u4_t os_rlsbf4 (xref2cu1_t buf) { - return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); -} -#endif - - -#if !defined(os_rmsbf4) -u4_t os_rmsbf4 (xref2cu1_t buf) { - return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); -} -#endif - - -#if !defined(os_wlsbf2) -void os_wlsbf2 (xref2u1_t buf, u2_t v) { - buf[0] = v; - buf[1] = v>>8; -} -#endif - -#if !defined(os_wlsbf4) -void os_wlsbf4 (xref2u1_t buf, u4_t v) { - buf[0] = v; - buf[1] = v>>8; - buf[2] = v>>16; - buf[3] = v>>24; -} -#endif - -#if !defined(os_wmsbf4) -void os_wmsbf4 (xref2u1_t buf, u4_t v) { - buf[3] = v; - buf[2] = v>>8; - buf[1] = v>>16; - buf[0] = v>>24; -} -#endif - -#if !defined(os_getBattLevel) -u1_t os_getBattLevel (void) { - return MCMD_DEVS_BATT_NOINFO; -} -#endif - -#if !defined(os_crc16) -// New CRC-16 CCITT(XMODEM) checksum for beacons: -u2_t os_crc16 (xref2u1_t data, uint len) { - u2_t remainder = 0; - u2_t polynomial = 0x1021; - for( uint i = 0; i < len; i++ ) { - remainder ^= data[i] << 8; - for( u1_t bit = 8; bit > 0; bit--) { - if( (remainder & 0x8000) ) - remainder = (remainder << 1) ^ polynomial; - else - remainder <<= 1; - } - } - return remainder; -} -#endif - -#endif // !HAS_os_calls - -// END OS - default implementations for certain OS suport functions -// ================================================================================ - -// ================================================================================ -// BEG AES - -static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) { - os_clearMem(AESaux,16); - AESaux[0] = 0x49; - AESaux[5] = dndir?1:0; - AESaux[15] = len; - os_wlsbf4(AESaux+ 6,devaddr); - os_wlsbf4(AESaux+10,seqno); -} - - -static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { - micB0(devaddr, seqno, dndir, len); - os_copyMem(AESkey,key,16); - return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len); -} - - -static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { - micB0(devaddr, seqno, dndir, len); - os_copyMem(AESkey,key,16); - // MSB because of internal structure of AES - os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len)); -} - - -static void aes_appendMic0 (xref2u1_t pdu, int len) { - os_getDevKey(AESkey); - os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES -} - - -static int aes_verifyMic0 (xref2u1_t pdu, int len) { - os_getDevKey(AESkey); - return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len); -} - - -static void aes_encrypt (xref2u1_t pdu, int len) { - os_getDevKey(AESkey); - os_aes(AES_ENC, pdu, len); -} - - -static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) { - if( len <= 0 ) - return; - os_clearMem(AESaux, 16); - AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1 - AESaux[5] = dndir?1:0; - os_wlsbf4(AESaux+ 6,devaddr); - os_wlsbf4(AESaux+10,seqno); - os_copyMem(AESkey,key,16); - os_aes(AES_CTR, payload, len); -} - - -static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) { - os_clearMem(nwkkey, 16); - nwkkey[0] = 0x01; - os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID); - os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce); - os_copyMem(artkey, nwkkey, 16); - artkey[0] = 0x02; - - os_getDevKey(AESkey); - os_aes(AES_ENC, nwkkey, 16); - os_getDevKey(AESkey); - os_aes(AES_ENC, artkey, 16); -} - -// END AES -// ================================================================================ - - -// ================================================================================ -// BEG LORA - -#if defined(CFG_eu868) // ======================================== - -#define maxFrameLen(dr) ((dr)<=DR_SF9 ? TABLE_GET_U1(maxFrameLens, (dr)) : 0xFF) -CONST_TABLE(u1_t, maxFrameLens) [] = { 64,64,64,123 }; - -CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { - ILLEGAL_RPS, - (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), - (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), - (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), - ILLEGAL_RPS -}; - -static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0 -}; -#define pow2dBm(mcmd_ladr_p1) (TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT)) - -#elif defined(CFG_us915) // ======================================== - -#define maxFrameLen(dr) ((dr)<=DR_SF11CR ? TABLE_GET_U1(maxFrameLens, (dr)) : 0xFF) -CONST_TABLE(u1_t, maxFrameLens) [] = { 24,66,142,255,255,255,255,255, 66,142 }; - -CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { - ILLEGAL_RPS, - MAKERPS(SF10, BW125, CR_4_5, 0, 0), - MAKERPS(SF9 , BW125, CR_4_5, 0, 0), - MAKERPS(SF8 , BW125, CR_4_5, 0, 0), - MAKERPS(SF7 , BW125, CR_4_5, 0, 0), - MAKERPS(SF8 , BW500, CR_4_5, 0, 0), - ILLEGAL_RPS , - ILLEGAL_RPS , - ILLEGAL_RPS , - MAKERPS(SF12, BW500, CR_4_5, 0, 0), - MAKERPS(SF11, BW500, CR_4_5, 0, 0), - MAKERPS(SF10, BW500, CR_4_5, 0, 0), - MAKERPS(SF9 , BW500, CR_4_5, 0, 0), - MAKERPS(SF8 , BW500, CR_4_5, 0, 0), - MAKERPS(SF7 , BW500, CR_4_5, 0, 0), - ILLEGAL_RPS -}; - -#define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))) - -#endif // ================================================ - -static CONST_TABLE(u1_t, SENSITIVITY)[7][3] = { - // ------------bw---------- - // 125kHz 250kHz 500kHz - { 141-109, 141-109, 141-109 }, // FSK - { 141-127, 141-124, 141-121 }, // SF7 - { 141-129, 141-126, 141-123 }, // SF8 - { 141-132, 141-129, 141-126 }, // SF9 - { 141-135, 141-132, 141-129 }, // SF10 - { 141-138, 141-135, 141-132 }, // SF11 - { 141-141, 141-138, 141-135 } // SF12 -}; - -int getSensitivity (rps_t rps) { - return -141 + TABLE_GET_U1_TWODIM(SENSITIVITY, getSf(rps), getBw(rps)); -} - -ostime_t calcAirTime (rps_t rps, u1_t plen) { - u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz - u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12 - if( sf == FSK ) { - return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8 - * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000; - } - u1_t sfx = 4*(sf+(7-SF7)); - u1_t q = sfx - (sf >= SF11 ? 8 : 0); - int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0); - if( tmp > 0 ) { - tmp = (tmp + q - 1) / q; - tmp *= getCr(rps)+5; - tmp += 8; - } else { - tmp = 8; - } - tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */; - // bw = 125000 = 15625 * 2^3 - // 250000 = 15625 * 2^4 - // 500000 = 15625 * 2^5 - // sf = 7..12 - // - // osticks = tmp * OSTICKS_PER_SEC * 1< counter reduced divisor 125000/8 => 15625 - // 2 => counter 2 shift on tmp - sfx = sf+(7-SF7) - (3+2) - bw; - int div = 15625; - if( sfx > 4 ) { - // prevent 32bit signed int overflow in last step - div >>= sfx-4; - sfx = 4; - } - // Need 32bit arithmetic for this last step - return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div; -} - -extern inline rps_t updr2rps (dr_t dr); -extern inline rps_t dndr2rps (dr_t dr); -extern inline int isFasterDR (dr_t dr1, dr_t dr2); -extern inline int isSlowerDR (dr_t dr1, dr_t dr2); -extern inline dr_t incDR (dr_t dr); -extern inline dr_t decDR (dr_t dr); -extern inline dr_t assertDR (dr_t dr); -extern inline dr_t validDR (dr_t dr); -extern inline dr_t lowerDR (dr_t dr, u1_t n); - -extern inline sf_t getSf (rps_t params); -extern inline rps_t setSf (rps_t params, sf_t sf); -extern inline bw_t getBw (rps_t params); -extern inline rps_t setBw (rps_t params, bw_t cr); -extern inline cr_t getCr (rps_t params); -extern inline rps_t setCr (rps_t params, cr_t cr); -extern inline int getNocrc (rps_t params); -extern inline rps_t setNocrc (rps_t params, int nocrc); -extern inline int getIh (rps_t params); -extern inline rps_t setIh (rps_t params, int ih); -extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); -extern inline int sameSfBw (rps_t r1, rps_t r2); - -// END LORA -// ================================================================================ - - -// Adjust DR for TX retries -// - indexed by retry count -// - return steps to lower DR -static CONST_TABLE(u1_t, DRADJUST)[2+TXCONF_ATTEMPTS] = { - // normal frames - 1st try / no retry - 0, - // confirmed frames - 0,0,1,0,1,0,1,0,0 -}; - - -// Table below defines the size of one symbol as -// symtime = 256us * 2^T(sf,bw) -// 256us is called one symunit. -// SF: -// BW: |__7___8___9__10__11__12 -// 125kHz | 2 3 4 5 6 7 -// 250kHz | 1 2 3 4 5 6 -// 500kHz | 0 1 2 3 4 5 -// -// Times for half symbol per DR -// Per DR table to minimize rounding errors -static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { -#if defined(CFG_eu868) -#define dr2hsym(dr) (TABLE_GET_OSTIME(DR2HSYM_osticks, (dr))) - 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<<1), // DR_SF7B - us2osticksRound(80) // FSK -- not used (time for 1/2 byte) -#elif defined(CFG_us915) -#define dr2hsym(dr) (TABLE_GET_OSTIME(DR2HSYM_osticks, (dr)&7)) // map DR_SFnCR -> 0-6 - us2osticksRound(128<<5), // DR_SF10 DR_SF12CR - us2osticksRound(128<<4), // DR_SF9 DR_SF11CR - us2osticksRound(128<<3), // DR_SF8 DR_SF10CR - us2osticksRound(128<<2), // DR_SF7 DR_SF9CR - us2osticksRound(128<<1), // DR_SF8C DR_SF8CR - us2osticksRound(128<<0) // ------ DR_SF7CR -#endif -}; - - -#if !defined(DISABLE_BEACONS) -static ostime_t calcRxWindow (u1_t secs, dr_t dr) { - ostime_t rxoff, err; - if( secs==0 ) { - // aka 128 secs (next becaon) - rxoff = LMIC.drift; - err = LMIC.lastDriftDiff; - } else { - // scheduled RX window within secs into current beacon period - rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp; - err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp; - } - u1_t rxsyms = MINRX_SYMS; - err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns; - LMIC.rxsyms = MINRX_SYMS + (err / dr2hsym(dr)); - - return (rxsyms-PAMBL_SYMS) * dr2hsym(dr) + rxoff; -} - - -// Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2)) -static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) { - if( ini ) { - LMIC.drift = 0; - LMIC.maxDriftDiff = 0; - LMIC.missedBcns = 0; - LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF; - } - ostime_t hsym = dr2hsym(DR_BCN); - LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym; - LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-PAMBL_SYMS) * hsym; -} -#endif // !DISABLE_BEACONS - - -#if !defined(DISABLE_PING) -// Setup scheduled RX window (ping/multicast slot) -static void rxschedInit (xref2rxsched_t rxsched) { - os_clearMem(AESkey,16); - os_clearMem(LMIC.frame+8,8); - os_wlsbf4(LMIC.frame, LMIC.bcninfo.time); - os_wlsbf4(LMIC.frame+4, LMIC.devaddr); - os_aes(AES_ENC,LMIC.frame,16); - u1_t intvExp = rxsched->intvExp; - ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units) - rxsched->rxbase = (LMIC.bcninfo.txtime + - BCN_RESERVE_osticks + - ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks - rxsched->slot = 0; - rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<dr); - rxsched->rxsyms = LMIC.rxsyms; -} - - -static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) { - again: - if( rxsched->rxtime - cando >= 0 ) - return 1; - u1_t slot; - if( (slot=rxsched->slot) >= 128 ) - return 0; - u1_t intv = 1<intvExp; - if( (rxsched->slot = (slot += (intv))) >= 128 ) - return 0; - rxsched->rxtime = rxsched->rxbase - + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp) - - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr); - rxsched->rxsyms = LMIC.rxsyms; - goto again; -} -#endif // !DISABLE_PING) - - -static ostime_t rndDelay (u1_t secSpan) { - u2_t r = os_getRndU2(); - ostime_t delay = r; - if( delay > OSTICKS_PER_SEC ) - delay = r % (u2_t)OSTICKS_PER_SEC; - if( secSpan > 0 ) - delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC; - return delay; -} - - -static void txDelay (ostime_t reftime, u1_t secSpan) { - reftime += rndDelay(secSpan); - if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) { - LMIC.globalDutyAvail = reftime; - LMIC.opmode |= OP_RNDTX; - } -} - - -static void setDrJoin (u1_t reason, u1_t dr) { - EV(drChange, INFO, (e_.reason = reason, - e_.deveui = MAIN::CDEV->getEui(), - e_.dr = dr|DR_PAGE, - e_.txpow = LMIC.adrTxPow, - e_.prevdr = LMIC.datarate|DR_PAGE, - e_.prevtxpow = LMIC.adrTxPow)); - LMIC.datarate = dr; - DO_DEVDB(LMIC.datarate,datarate); -} - - -static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) { - EV(drChange, INFO, (e_.reason = reason, - e_.deveui = MAIN::CDEV->getEui(), - e_.dr = dr|DR_PAGE, - e_.txpow = pow, - e_.prevdr = LMIC.datarate|DR_PAGE, - e_.prevtxpow = LMIC.adrTxPow)); - - if( pow != KEEP_TXPOW ) - LMIC.adrTxPow = pow; - if( LMIC.datarate != dr ) { - LMIC.datarate = dr; - DO_DEVDB(LMIC.datarate,datarate); - LMIC.opmode |= OP_NEXTCHNL; - } -} - - -#if !defined(DISABLE_PING) -void LMIC_stopPingable (void) { - LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI); -} - - -void LMIC_setPingable (u1_t intvExp) { - // Change setting - LMIC.ping.intvExp = (intvExp & 0x7); - LMIC.opmode |= OP_PINGABLE; - // App may call LMIC_enableTracking() explicitely before - // Otherwise tracking is implicitly enabled here - if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 ) - LMIC_enableTracking(0); -} - -#endif // !DISABLE_PING - -#if defined(CFG_eu868) -// ================================================================================ -// -// BEG: EU868 related stuff -// -enum { NUM_DEFAULT_CHANNELS=3 }; -static CONST_TABLE(u4_t, iniChannelFreq)[6] = { - // Join frequencies and duty cycle limit (0.1%) - EU868_F1|BAND_MILLI, EU868_F2|BAND_MILLI, EU868_F3|BAND_MILLI, - // Default operational frequencies - EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI, -}; - -static void initDefaultChannels (bit_t join) { - os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); - os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); - os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); - - LMIC.channelMap = 0x07; - u1_t su = join ? 0 : 3; - for( u1_t fu=0; fu<3; fu++,su++ ) { - LMIC.channelFreq[fu] = TABLE_GET_U4(iniChannelFreq, su); - LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7); - } - - LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1% - LMIC.bands[BAND_MILLI].txpow = 14; - LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS; - LMIC.bands[BAND_CENTI].txcap = 100; // 1% - LMIC.bands[BAND_CENTI].txpow = 14; - LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS; - LMIC.bands[BAND_DECI ].txcap = 10; // 10% - LMIC.bands[BAND_DECI ].txpow = 27; - LMIC.bands[BAND_DECI ].lastchnl = os_getRndU1() % MAX_CHANNELS; - LMIC.bands[BAND_MILLI].avail = - LMIC.bands[BAND_CENTI].avail = - LMIC.bands[BAND_DECI ].avail = os_getTime(); -} - -bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) { - if( bandidx > BAND_AUX ) return 0; - band_t* b = &LMIC.bands[bandidx]; - b->txpow = txpow; - b->txcap = txcap; - b->avail = os_getTime(); - b->lastchnl = os_getRndU1() % MAX_CHANNELS; - return 1; -} - -bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { - if( chidx >= MAX_CHANNELS ) - return 0; - if( band == -1 ) { - if( freq >= 869400000 && freq <= 869650000 ) - freq |= BAND_DECI; // 10% 27dBm - else if( (freq >= 868000000 && freq <= 868600000) || - (freq >= 869700000 && freq <= 870000000) ) - freq |= BAND_CENTI; // 1% 14dBm - else - freq |= BAND_MILLI; // 0.1% 14dBm - } else { - if( band > BAND_AUX ) return 0; - freq = (freq&~3) | band; - } - LMIC.channelFreq [chidx] = freq; - LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap; - LMIC.channelMap |= 1<> 8) * 100; - if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX ) - freq = 0; - return freq; -} - -static u1_t mapChannels (u1_t chpage, u2_t chmap) { - // Bad page, disable all channel, enable non-existent - if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 ) - return 0; // illegal input - for( u1_t chnl=0; chnltxpow; - band->avail = txbeg + airtime * band->txcap; - if( LMIC.globalDutyRate != 0 ) - LMIC.globalDutyAvail = txbeg + (airtime< 1 - lmic_printf("%lu: Updating info for TX at %lu, airtime will be %lu. Setting available time for band %d to %lu\n", os_getTime(), txbeg, airtime, freq & 0x3, band->avail); - if( LMIC.globalDutyRate != 0 ) - lmic_printf("%lu: Updating global duty avail to %lu\n", os_getTime(), LMIC.globalDutyAvail); - #endif -} - -static ostime_t nextTx (ostime_t now) { - u1_t bmap=0xF; - do { - ostime_t mintime = now + /*8h*/sec2osticks(28800); - u1_t band=0; - for( u1_t bi=0; bi<4; bi++ ) { - if( (bmap & (1< 0 ) { - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Considering band %d, which is available at %lu\n", os_getTime(), bi, LMIC.bands[bi].avail); - #endif - mintime = LMIC.bands[band = bi].avail; - } - } - // Find next channel in given band - u1_t chnl = LMIC.bands[band].lastchnl; - for( u1_t ci=0; ci= MAX_CHANNELS ) - chnl -= MAX_CHANNELS; - if( (LMIC.channelMap & (1< 1 - lmic_printf("%lu: No channel found in band %d\n", os_getTime(), band); - #endif - if( (bmap &= ~(1<>LMIC.datarate)); - #if LMIC_DEBUG_LEVEL > 1 - if (failed) - lmic_printf("%lu: Join failed\n", os_getTime()); - else - lmic_printf("%lu: Scheduling next join at %lu\n", os_getTime(), LMIC.txend); - #endif - // 1 - triggers EV_JOIN_FAILED event - return failed; -} -#endif // !DISABLE_JOIN - -// -// END: EU868 related stuff -// -// ================================================================================ -#elif defined(CFG_us915) -// ================================================================================ -// -// BEG: US915 related stuff -// - - -static void initDefaultChannels (void) { - for( u1_t i=0; i<4; i++ ) - LMIC.channelMap[i] = 0xFFFF; - LMIC.channelMap[4] = 0x00FF; -} - -static u4_t convFreq (xref2u1_t ptr) { - u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100; - if( freq < US915_FREQ_MIN || freq > US915_FREQ_MAX ) - freq = 0; - return freq; -} - -bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { - if( chidx < 72 || chidx >= 72+MAX_XCHANNELS ) - return 0; // channels 0..71 are hardwired - chidx -= 72; - LMIC.xchFreq[chidx] = freq; - LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap; - LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF)); - return 1; -} - -void LMIC_disableChannel (u1_t channel) { - if( channel < 72+MAX_XCHANNELS ) - LMIC.channelMap[channel>>4] &= ~(1<<(channel&0xF)); -} - -void LMIC_enableChannel (u1_t channel) { - if( channel < 72+MAX_XCHANNELS ) - LMIC.channelMap[channel>>4] |= (1<<(channel&0xF)); -} - -void LMIC_enableSubBand (u1_t band) { - ASSERT(band < 8); - u1_t start = band * 8; - u1_t end = start + 8; - for (int channel=start; channel < end; ++channel ) - LMIC_enableChannel(channel); -} -void LMIC_disableSubBand (u1_t band) { - ASSERT(band < 8); - u1_t start = band * 8; - u1_t end = start + 8; - for (int channel=start; channel < end; ++channel ) - LMIC_disableChannel(channel); -} -void LMIC_selectSubBand (u1_t band) { - ASSERT(band < 8); - for (int b=0; b<8; ++b) { - if (band==b) - LMIC_enableSubBand(b); - else - LMIC_disableSubBand(b); - } -} - -static u1_t mapChannels (u1_t chpage, u2_t chmap) { - if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) { - u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000; - for( u1_t u=0; u<4; u++ ) - LMIC.channelMap[u] = en125; - LMIC.channelMap[64/16] = chmap; - } else { - if( chpage >= (72+MAX_XCHANNELS+15)/16 ) - return 0; - LMIC.channelMap[chpage] = chmap; - } - return 1; -} - -static void updateTx (ostime_t txbeg) { - u1_t chnl = LMIC.txChnl; - if( chnl < 64 ) { - LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP; - LMIC.txpow = 30; - return; - } - LMIC.txpow = 26; - if( chnl < 64+8 ) { - LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP; - } else { - ASSERT(chnl < 64+8+MAX_XCHANNELS); - LMIC.freq = LMIC.xchFreq[chnl-72]; - } - - // Update global duty cycle stats - if( LMIC.globalDutyRate != 0 ) { - ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); - LMIC.globalDutyAvail = txbeg + (airtime<= DR_SF8C ) { // 500kHz - u1_t map = LMIC.channelMap[64/16]&0xFF; - for( u1_t i=0; i<8; i++ ) { - if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) { - LMIC.txChnl = 64 + (LMIC.chRnd & 7); - return; - } - } - } else { // 125kHz - for( u1_t i=0; i<64; i++ ) { - u1_t chnl = ++LMIC.chRnd & 0x3F; - if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) { - LMIC.txChnl = chnl; - return; - } - } - } - // No feasible channel found! Keep old one. -} - -#if !defined(DISABLE_BEACONS) -static void setBcnRxParams (void) { - LMIC.dataLen = 0; - LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP; - LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN); -} -#endif // !DISABLE_BEACONS - -#define setRx1Params() { \ - LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \ - if( /* TX datarate */LMIC.dndr < DR_SF8C ) \ - LMIC.dndr += DR_SF10CR - DR_SF10; \ - else if( LMIC.dndr == DR_SF8C ) \ - LMIC.dndr = DR_SF7CR; \ - LMIC.rps = dndr2rps(LMIC.dndr); \ -} - -#if !defined(DISABLE_JOIN) -static void initJoinLoop (void) { - LMIC.chRnd = 0; - LMIC.txChnl = 0; - LMIC.adrTxPow = 20; - ASSERT((LMIC.opmode & OP_NEXTCHNL)==0); - LMIC.txend = os_getTime(); - setDrJoin(DRCHG_SET, DR_SF7); -} - -static ostime_t nextJoinState (void) { - // Try the following: - // SF7/8/9/10 on a random channel 0..63 - // SF8C on a random channel 64..71 - // - u1_t failed = 0; - if( LMIC.datarate != DR_SF8C ) { - LMIC.txChnl = 64+(LMIC.txChnl&7); - setDrJoin(DRCHG_SET, DR_SF8C); - } else { - LMIC.txChnl = os_getRndU1() & 0x3F; - s1_t dr = DR_SF7 - ++LMIC.txCnt; - if( LMIC.txCnt > DR_SF7 ) { - dr = DR_SF10; - failed = 1; // All DR exhausted - signal failed - } - setDrJoin(DRCHG_SET, dr); - } - LMIC.opmode &= ~OP_NEXTCHNL; - LMIC.txend = os_getTime() + - (isTESTMODE() - // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy) - ? DNW2_SAFETY_ZONE - // Otherwise: randomize join (street lamp case): - // SF10:16, SF9=8,..SF8C:1secs - : rndDelay(16>>LMIC.datarate)); - // 1 - triggers EV_JOIN_FAILED event - return failed; -} -#endif // !DISABLE_JOIN - -// -// END: US915 related stuff -// -// ================================================================================ -#else -#error Unsupported frequency band! -#endif - - -static void runEngineUpdate (xref2osjob_t osjob) { - engineUpdate(); -} - - -static void reportEvent (ev_t ev) { - EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, - e_.eui = MAIN::CDEV->getEui(), - e_.info = ev)); - ON_LMIC_EVENT(ev); - engineUpdate(); -} - - -static void runReset (xref2osjob_t osjob) { - // Disable session - LMIC_reset(); -#if !defined(DISABLE_JOIN) - LMIC_startJoining(); -#endif // !DISABLE_JOIN - reportEvent(EV_RESET); -} - -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.dn2Ans = 0; -#endif - LMIC.moreData = 0; -#if !defined(DISABLE_MCMD_DCAP_REQ) - LMIC.dutyCapAns = 0; -#endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - LMIC.pingSetAns = 0; -#endif - LMIC.upRepeat = 0; - LMIC.adrAckReq = LINK_CHECK_INIT; - LMIC.dn2Dr = DR_DNW2; - LMIC.dn2Freq = FREQ_DNW2; -#if !defined(DISABLE_BEACONS) - LMIC.bcnChnl = CHNL_BCN; -#endif -#if !defined(DISABLE_PING) - LMIC.ping.freq = FREQ_PING; - LMIC.ping.dr = DR_PING; -#endif -} - - -// ================================================================================ -// Decoding frames - - -#if !defined(DISABLE_BEACONS) -// Decode beacon - do not overwrite bcninfo unless we have a match! -static int decodeBeacon (void) { - ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this - xref2u1_t d = LMIC.frame; - if( -#if CFG_eu868 - d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1) -#elif CFG_us915 - os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1) -#endif - ) - return 0; // first (common) part fails CRC check - // First set of fields is ok - u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF; - if( bcnnetid != LMIC.netid ) - return -1; // not the beacon we're looking for - - LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL); - // Match - update bcninfo structure - LMIC.bcninfo.snr = LMIC.snr; - LMIC.bcninfo.rssi = LMIC.rssi; - LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks; - LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]); - LMIC.bcninfo.flags |= BCN_PARTIAL; - - // Check 2nd set - if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) ) - return 1; - // Second set of fields is ok - LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit - LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto - LMIC.bcninfo.info = d[OFF_BCN_INFO]; - LMIC.bcninfo.flags |= BCN_FULL; - return 2; -} -#endif // !DISABLE_BEACONS - - -static bit_t decodeFrame (void) { - xref2u1_t d = LMIC.frame; - u1_t hdr = d[0]; - u1_t ftype = hdr & HDR_FTYPE; - int dlen = LMIC.dataLen; - const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); - if( dlen < OFF_DAT_OPTS+4 || - (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || - (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) { - // Basic sanity checks failed - EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, - e_.eui = MAIN::CDEV->getEui(), - e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]), - e_.info2 = hdr + (dlen<<8))); - norx: -#if LMIC_DEBUG_LEVEL > 0 - lmic_printf("%lu: Invalid downlink, window=%s\n", os_getTime(), window); -#endif - LMIC.dataLen = 0; - return 0; - } - // Validate exact frame length - // Note: device address was already read+evaluated in order to arrive here. - int fct = d[OFF_DAT_FCT]; - u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]); - u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]); - int olen = fct & FCT_OPTLEN; - int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame - int poff = OFF_DAT_OPTS+olen; - int pend = dlen-4; // MIC - - if( addr != LMIC.devaddr ) { - EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, - e_.eui = MAIN::CDEV->getEui(), - e_.info = addr, - e_.info2 = LMIC.devaddr)); - goto norx; - } - if( 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))); - goto norx; - } - - int port = -1; - int replayConf = 0; - - if( pend > poff ) - port = d[poff++]; - - seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn); - - if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { - EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, - e_.eui1 = MAIN::CDEV->getEui(), - e_.info1 = Base::lsbf4(&d[pend]), - e_.info2 = seqno, - e_.info3 = LMIC.devaddr)); - goto norx; - } - if( seqno < LMIC.seqnoDn ) { - if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) { - EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.seqnoDn, - e_.info2 = seqno)); - goto norx; - } - if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || 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)); - goto norx; - } - // Replay of previous sequence number allowed only if - // previous frame and repeated both requested confirmation - replayConf = 1; - } - else { - if( seqno > LMIC.seqnoDn ) { - EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.seqnoDn, - e_.info2 = seqno)); - } - 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); - } - - if( LMIC.dnConf || (fct & FCT_MORE) ) - LMIC.opmode |= OP_POLL; - - // We heard from network - LMIC.adrChanged = LMIC.rejoinCnt = 0; - if( LMIC.adrAckReq != LINK_CHECK_OFF ) - LMIC.adrAckReq = LINK_CHECK_INIT; - - // Process OPTS - int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps); - LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; - - xref2u1_t opts = &d[OFF_DAT_OPTS]; - int oidx = 0; - while( oidx < olen ) { - switch( opts[oidx] ) { - case MCMD_LCHK_ANS: { - //int gwmargin = opts[oidx+1]; - //int ngws = opts[oidx+2]; - oidx += 3; - continue; - } - 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 - oidx += 5; - - LMIC.ladrAns = 0x80 | // Include an answer into next frame up - MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK; - if( !mapChannels(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]))); - } - if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) { - // Nothing went wrong - use settings - LMIC.upRepeat = uprpt; - setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); - } - LMIC.adrChanged = 1; // Trigger an ACK to NWK - continue; - } - case MCMD_DEVS_REQ: { - LMIC.devsAns = 1; - oidx += 1; - continue; - } - case MCMD_DN2P_SET: { -#if !defined(DISABLE_MCMD_DN2P_SET) - dr_t dr = (dr_t)(opts[oidx+1] & 0x0F); - u4_t freq = convFreq(&opts[oidx+2]); - LMIC.dn2Ans = 0x80; // answer pending - if( validDR(dr) ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK; - if( freq != 0 ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK; - if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) { - LMIC.dn2Dr = dr; - LMIC.dn2Freq = freq; - DO_DEVDB(LMIC.dn2Dr,dn2Dr); - DO_DEVDB(LMIC.dn2Freq,dn2Freq); - } -#endif // !DISABLE_MCMD_DN2P_SET - oidx += 5; - continue; - } - case MCMD_DCAP_REQ: { -#if !defined(DISABLE_MCMD_DCAP_REQ) - u1_t cap = opts[oidx+1]; - // A value cap=0xFF means device is OFF unless enabled again manually. - if( cap==0xFF ) - LMIC.opmode |= OP_SHUTDOWN; // stop any sending - LMIC.globalDutyRate = cap & 0xF; - LMIC.globalDutyAvail = os_getTime(); - DO_DEVDB(cap,dutyCap); - LMIC.dutyCapAns = 1; - oidx += 2; -#endif // !DISABLE_MCMD_DCAP_REQ - continue; - } - case MCMD_SNCH_REQ: { -#if !defined(DISABLE_MCMD_SNCH_REQ) - u1_t chidx = opts[oidx+1]; // channel - u4_t freq = 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 - oidx += 6; - continue; - } - case MCMD_PING_SET: { -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - u4_t freq = convFreq(&opts[oidx+1]); - u1_t flags = 0x80; - if( freq != 0 ) { - flags |= MCMD_PING_ANS_FQACK; - LMIC.ping.freq = freq; - 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 - oidx += 4; - continue; - } - case MCMD_BCNI_ANS: { -#if !defined(DISABLE_MCMD_BCNI_ANS) && !defined(DISABLE_BEACONS) - // Ignore if tracking already enabled - if( (LMIC.opmode & OP_TRACK) == 0 ) { - LMIC.bcnChnl = opts[oidx+3]; - // Enable tracking - bcninfoTries - LMIC.opmode |= OP_TRACK; - // Cleared later in txComplete handling - triggers EV_BEACON_FOUND - 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) - - 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 - - EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS, - e_.eui = MAIN::CDEV->getEui(), - e_.lostmic = Base::lsbf4(&d[pend]), - e_.info = (LMIC.missedBcns | - (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks - - LMIC.bcnRxtime) << 8)), - e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); - } -#endif // !DISABLE_MCMD_BCNI_ANS && !DISABLE_BEACONS - oidx += 4; - continue; - } - } - 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]))); - break; - } - if( oidx != olen ) { - EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, - e_.eui = MAIN::CDEV->getEui(), - e_.info = 0x1000000 + (oidx) + (olen<<8))); - } - - if( !replayConf ) { - // Handle payload only if not a replay - // Decrypt payload - if any - if( port >= 0 && pend-poff > 0 ) - aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff); - - EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), - e_.devaddr = LMIC.devaddr, - e_.seqno = seqno, - e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN, - e_.mic = Base::lsbf4(&d[pend]), - e_.hdr = d[LORA::OFF_DAT_HDR], - e_.fct = d[LORA::OFF_DAT_FCT], - e_.port = port, - e_.plen = dlen, - e_.opts.length = olen, - memcpy(&e_.opts[0], opts, olen))); - } else { - EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY, - e_.eui = MAIN::CDEV->getEui(), - e_.info = Base::lsbf4(&d[pend]), - e_.info2 = seqno)); - } - - if( // NWK acks but we don't have a frame pending - (ackup && LMIC.txCnt == 0) || - // We sent up confirmed and we got a response in DNW1/DNW2 - // BUT it did not carry an ACK - this should never happen - // Do not resend and assume frame was not ACKed. - (!ackup && LMIC.txCnt != 0) ) { - EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK, - e_.eui = MAIN::CDEV->getEui(), - e_.info = seqno, - e_.info2 = ackup)); - } - - if( LMIC.txCnt != 0 ) // we requested an ACK - LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK; - - if( port < 0 ) { - LMIC.txrxFlags |= TXRX_NOPORT; - LMIC.dataBeg = poff; - LMIC.dataLen = 0; - } else { - LMIC.txrxFlags |= TXRX_PORT; - LMIC.dataBeg = poff; - LMIC.dataLen = pend-poff; - } -#if LMIC_DEBUG_LEVEL > 0 - lmic_printf("%lu: Received downlink, window=%s, port=%d, ack=%d\n", os_getTime(), window, port, ackup); -#endif - return 1; -} - - -// ================================================================================ -// TX/RX transaction support - - -static void setupRx2 (void) { - LMIC.txrxFlags = TXRX_DNW2; - LMIC.rps = dndr2rps(LMIC.dn2Dr); - LMIC.freq = LMIC.dn2Freq; - LMIC.dataLen = 0; - os_radio(RADIO_RX); -} - - -static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) { - ostime_t hsym = dr2hsym(dr); - - LMIC.rxsyms = MINRX_SYMS; - - // If a clock error is specified, compensate for it by extending the - // receive window - if (LMIC.clockError != 0) { - // Calculate how much the clock will drift maximally after delay has - // passed. This indicates the amount of time we can be early - // _or_ late. - ostime_t drift = (int64_t)delay * LMIC.clockError / MAX_CLOCK_ERROR; - - // Increase the receive window by twice the maximum drift (to - // compensate for a slow or a fast clock). - // decrease the rxtime to compensate for. Note that hsym is a - // *half* symbol time, so the factor 2 is hidden. First check if - // this would overflow (which can happen if the drift is very - // high, or the symbol time is low at high datarates). - if ((255 - LMIC.rxsyms) * hsym < drift) - LMIC.rxsyms = 255; - else - LMIC.rxsyms += drift / hsym; - - } - - // Center the receive window on the center of the expected preamble - // (again note that hsym is half a sumbol time, so no /2 needed) - LMIC.rxtime = LMIC.txend + delay + PAMBL_SYMS * hsym - LMIC.rxsyms * hsym; - - os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); -} - -static void setupRx1 (osjobcb_t func) { - LMIC.txrxFlags = TXRX_DNW1; - // Turn LMIC.rps from TX over to RX - LMIC.rps = setNocrc(LMIC.rps,1); - LMIC.dataLen = 0; - LMIC.osjob.func = func; - os_radio(RADIO_RX); -} - - -// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime -static void txDone (ostime_t delay, osjobcb_t func) { -#if !defined(DISABLE_PING) - if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) { - rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! - LMIC.opmode |= OP_PINGINI; - } -#endif // !DISABLE_PING - - // Change RX frequency / rps (US only) before we increment txChnl - 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. -#if defined(CFG_eu868) - if( /* TX datarate */LMIC.rxsyms == DR_FSK ) { - LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160); - LMIC.rxsyms = RXLEN_FSK; - os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); - } - else -#endif - { - schedRx12(delay, func, LMIC.dndr); - } -} - - -// ======================================== Join frames - - -#if !defined(DISABLE_JOIN) -static void onJoinFailed (xref2osjob_t osjob) { - // Notify app - must call LMIC_reset() to stop joining - // otherwise join procedure continues. - reportEvent(EV_JOIN_FAILED); -} - - -static bit_t processJoinAccept (void) { - ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0); - ASSERT((LMIC.opmode & OP_TXRXPEND)!=0); - - if( LMIC.dataLen == 0 ) { - nojoinframe: - if( (LMIC.opmode & OP_JOINING) == 0 ) { - ASSERT((LMIC.opmode & OP_REJOIN) != 0); - // REJOIN attempt for roaming - LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND); - if( LMIC.rejoinCnt < 10 ) - LMIC.rejoinCnt++; - reportEvent(EV_REJOIN_FAILED); - return 1; - } - LMIC.opmode &= ~OP_TXRXPEND; - ostime_t delay = nextJoinState(); - EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.datarate|DR_PAGE, - e_.info2 = osticks2ms(delay))); - // Build next JOIN REQUEST with next engineUpdate call - // Optionally, report join failed. - // Both after a random/chosen amount of ticks. - os_setTimedCallback(&LMIC.osjob, os_getTime()+delay, - (delay&1) != 0 - ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed - : FUNC_ADDR(runEngineUpdate)); // next step to be delayed - return 1; - } - u1_t hdr = LMIC.frame[0]; - u1_t dlen = LMIC.dataLen; - u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt! - if( (dlen != LEN_JA && dlen != LEN_JAEXT) - || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) { - EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, - e_.eui = MAIN::CDEV->getEui(), - e_.info = dlen < 4 ? 0 : mic, - e_.info2 = hdr + (dlen<<8))); - badframe: - if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) - return 0; - goto nojoinframe; - } - aes_encrypt(LMIC.frame+1, dlen-1); - if( !aes_verifyMic0(LMIC.frame, dlen-4) ) { - EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC, - e_.info = mic)); - goto badframe; - } - - u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR); - LMIC.devaddr = addr; - LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF; - -#if defined(CFG_eu868) - initDefaultChannels(0); -#endif - if( dlen > LEN_JA ) { -#if defined(CFG_us915) - goto badframe; -#endif - dlen = OFF_CFLIST; - for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) { - u4_t freq = convFreq(&LMIC.frame[dlen]); - if( freq ) { - LMIC_setupChannel(chidx, freq, 0, -1); -#if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Setup channel, idx=%d, freq=%lu\n", os_getTime(), chidx, (unsigned long)freq); -#endif - } - } - } - - // already incremented when JOIN REQ got sent off - aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey); - DO_DEVDB(LMIC.netid, netid); - DO_DEVDB(LMIC.devaddr, devaddr); - DO_DEVDB(LMIC.nwkKey, nwkkey); - DO_DEVDB(LMIC.artKey, artkey); - - EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(), - e_.deveui = MAIN::CDEV->getEui(), - e_.devaddr = LMIC.devaddr, - e_.oldaddr = oldaddr, - e_.nonce = LMIC.devNonce-1, - e_.mic = mic, - e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 - ? EV::joininfo_t::REJOIN_ACCEPT - : EV::joininfo_t::ACCEPT))); - - ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0); - if( (LMIC.opmode & OP_REJOIN) != 0 ) { - // Lower DR every try below current UP DR - LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt); - } - LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL; - LMIC.txCnt = 0; - stateJustJoined(); - LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F; - LMIC.rxDelay = LMIC.frame[OFF_JA_RXDLY]; - if (LMIC.rxDelay == 0) LMIC.rxDelay = 1; - reportEvent(EV_JOINED); - return 1; -} - - -static void processRx2Jacc (xref2osjob_t osjob) { - if( LMIC.dataLen == 0 ) - LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot - processJoinAccept(); -} - - -static void setupRx2Jacc (xref2osjob_t osjob) { - LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); - setupRx2(); -} - - -static void processRx1Jacc (xref2osjob_t osjob) { - if( LMIC.dataLen == 0 || !processJoinAccept() ) - schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr); -} - - -static void setupRx1Jacc (xref2osjob_t osjob) { - setupRx1(FUNC_ADDR(processRx1Jacc)); -} - - -static void jreqDone (xref2osjob_t osjob) { - txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc)); -} - -#endif // !DISABLE_JOIN - -// ======================================== Data frames - -// Fwd decl. -static bit_t processDnData(void); - -static void processRx2DnData (xref2osjob_t osjob) { - if( LMIC.dataLen == 0 ) { - LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot - // It could be that the gateway *is* sending a reply, but we - // just didn't pick it up. To avoid TX'ing again while the - // gateay is not listening anyway, delay the next transmission - // until DNW2_SAFETY_ZONE from now, and add up to 2 seconds of - // extra randomization. - txDelay(os_getTime() + DNW2_SAFETY_ZONE, 2); - } - processDnData(); -} - - -static void setupRx2DnData (xref2osjob_t osjob) { - LMIC.osjob.func = FUNC_ADDR(processRx2DnData); - setupRx2(); -} - - -static void processRx1DnData (xref2osjob_t osjob) { - if( LMIC.dataLen == 0 || !processDnData() ) - schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr); -} - - -static void setupRx1DnData (xref2osjob_t osjob) { - setupRx1(FUNC_ADDR(processRx1DnData)); -} - - -static void updataDone (xref2osjob_t osjob) { - txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData)); -} - -// ======================================== - - -static void buildDataFrame (void) { - bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL); - u1_t dlen = txdata ? LMIC.pendTxLen : 0; - - // Piggyback MAC options - // Prioritize by importance - int end = OFF_DAT_OPTS; -#if !defined(DISABLE_PING) - if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) { - // Indicate pingability in every UP frame - LMIC.frame[end] = MCMD_PING_IND; - LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4); - end += 2; - } -#endif // !DISABLE_PING -#if !defined(DISABLE_MCMD_DCAP_REQ) - if( LMIC.dutyCapAns ) { - LMIC.frame[end] = MCMD_DCAP_ANS; - end += 1; - LMIC.dutyCapAns = 0; - } -#endif // !DISABLE_MCMD_DCAP_REQ -#if !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( LMIC.devsAns ) { // answer to device status - LMIC.frame[end+0] = MCMD_DEVS_ANS; - LMIC.frame[end+1] = os_getBattLevel(); - LMIC.frame[end+2] = LMIC.margin; - end += 3; - LMIC.devsAns = 0; - } - if( LMIC.ladrAns ) { // answer to ADR change - LMIC.frame[end+0] = MCMD_LADR_ANS; - LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU; - end += 2; - LMIC.ladrAns = 0; - } -#if !defined(DISABLE_BEACONS) - if( LMIC.bcninfoTries > 0 ) { - LMIC.frame[end] = MCMD_BCNI_REQ; - end += 1; - } -#endif // !DISABLE_BEACONS - if( LMIC.adrChanged ) { - if( LMIC.adrAckReq < 0 ) - LMIC.adrAckReq = 0; - LMIC.adrChanged = 0; - } -#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 - ASSERT(end <= OFF_DAT_OPTS+16); - - u1_t flen = end + (txdata ? 5+dlen : 4); - if( flen > MAX_LEN_FRAME ) { - // Options and payload too big - delay payload - txdata = 0; - flen = end+4; - } - LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1; - LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled - | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0) - | (end-OFF_DAT_OPTS)); - os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); - - if( LMIC.txCnt == 0 ) { - LMIC.seqnoUp += 1; - DO_DEVDB(LMIC.seqnoUp,seqnoUp); - } else { - EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.seqnoUp-1, - e_.info2 = ((LMIC.txCnt+1) | - (TABLE_GET_U1(DRADJUST, LMIC.txCnt+1) << 8) | - ((LMIC.datarate|DR_PAGE)<<16)))); - } - os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1); - - // Clear pending DN confirmation - LMIC.dnConf = 0; - - if( txdata ) { - if( LMIC.pendTxConf ) { - // 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; - } - LMIC.frame[end] = LMIC.pendTxPort; - os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen); - aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey, - LMIC.devaddr, LMIC.seqnoUp-1, - /*up*/0, LMIC.frame+end+1, dlen); - } - aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4); - - EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), - e_.devaddr = LMIC.devaddr, - e_.seqno = LMIC.seqnoUp-1, - e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP), - e_.mic = Base::lsbf4(&LMIC.frame[flen-4]), - e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR], - e_.fct = LMIC.frame[LORA::OFF_DAT_FCT], - e_.port = LMIC.pendTxPort, - e_.plen = txdata ? dlen : 0, - e_.opts.length = end-LORA::OFF_DAT_OPTS, - memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS))); - LMIC.dataLen = flen; -} - - -#if !defined(DISABLE_BEACONS) -// Callback from HAL during scan mode or when job timer expires. -static void onBcnRx (xref2osjob_t job) { - // If we arrive via job timer make sure to put radio to rest. - os_radio(RADIO_RST); - os_clearCallback(&LMIC.osjob); - if( LMIC.dataLen == 0 ) { - // Nothing received - timeout - LMIC.opmode &= ~(OP_SCAN | OP_TRACK); - reportEvent(EV_SCAN_TIMEOUT); - return; - } - if( decodeBeacon() <= 0 ) { - // Something is wrong with the beacon - continue scan - LMIC.dataLen = 0; - os_radio(RADIO_RXON); - os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx)); - return; - } - // Found our 1st beacon - // We don't have a previous beacon to calc some drift - assume - // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm - calcBcnRxWindowFromMillis(13,1); - LMIC.opmode &= ~OP_SCAN; // turn SCAN off - LMIC.opmode |= OP_TRACK; // auto enable tracking - reportEvent(EV_BEACON_FOUND); // can be disabled in callback -} - - -// Enable receiver to listen to incoming beacons -// netid defines when scan stops (any or specific beacon) -// This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON -// Implicitely cancels any pending TX/RX transaction. -// Also cancels an onpoing joining procedure. -static void startScan (void) { - ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0); - if( (LMIC.opmode & OP_SHUTDOWN) != 0 ) - return; - // Cancel onging TX/RX transaction - LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0; - LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND); - setBcnRxParams(); - LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1); - os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx)); - os_radio(RADIO_RXON); -} - - -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. - if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) - startScan(); - return 1; // enabled -} - - -void LMIC_disableTracking (void) { - LMIC.opmode &= ~(OP_SCAN|OP_TRACK); - LMIC.bcninfoTries = 0; - engineUpdate(); -} -#endif // !DISABLE_BEACONS - - -// ================================================================================ -// -// Join stuff -// -// ================================================================================ - -#if !defined(DISABLE_JOIN) -static void buildJoinRequest (u1_t ftype) { - // Do not use pendTxData since we might have a pending - // user level frame in there. Use RX holding area instead. - xref2u1_t d = LMIC.frame; - d[OFF_JR_HDR] = ftype; - os_getArtEui(d + OFF_JR_ARTEUI); - os_getDevEui(d + OFF_JR_DEVEUI); - os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce); - aes_appendMic0(d, OFF_JR_MIC); - - EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(), - e_.arteui = MAIN::CDEV->getArtEui(), - e_.nonce = LMIC.devNonce, - e_.oldaddr = LMIC.devaddr, - e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]), - e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 - ? EV::joininfo_t::REJOIN_REQUEST - : EV::joininfo_t::REQUEST))); - LMIC.dataLen = LEN_JR; - LMIC.devNonce++; - DO_DEVDB(LMIC.devNonce,devNonce); -} - -static void startJoining (xref2osjob_t osjob) { - reportEvent(EV_JOINING); -} - -// Start join procedure if not already joined. -bit_t LMIC_startJoining (void) { - if( LMIC.devaddr == 0 ) { - // There should be no TX/RX going on - ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0); - // Lift any previous duty limitation - LMIC.globalDutyRate = 0; - // Cancel scanning - LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL); - // Setup state - LMIC.rejoinCnt = LMIC.txCnt = 0; - initJoinLoop(); - LMIC.opmode |= OP_JOINING; - // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS - os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining)); - return 1; - } - return 0; // already joined -} -#endif // !DISABLE_JOIN - - -// ================================================================================ -// -// -// -// ================================================================================ - -#if !defined(DISABLE_PING) -static void processPingRx (xref2osjob_t osjob) { - if( LMIC.dataLen != 0 ) { - LMIC.txrxFlags = TXRX_PING; - if( decodeFrame() ) { - reportEvent(EV_RXCOMPLETE); - return; - } - } - // Pick next ping slot - engineUpdate(); -} -#endif // !DISABLE_PING - - -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; - } - LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT; - } else { - // Nothing received - implies no port - LMIC.txrxFlags = TXRX_NOPORT; - } - if( LMIC.adrAckReq != LINK_CHECK_OFF ) - LMIC.adrAckReq += 1; - LMIC.dataBeg = LMIC.dataLen = 0; - txcomplete: - LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); - if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) { - LMIC.opmode &= ~OP_LINKDEAD; - reportEvent(EV_LINK_ALIVE); - } - reportEvent(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)); - setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW); - LMIC.adrAckReq = LINK_CHECK_CONT; - LMIC.opmode |= OP_REJOIN|OP_LINKDEAD; - reportEvent(EV_LINK_DEAD); - } -#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 ) { - reportEvent(EV_BEACON_FOUND); - 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; -} - - -#if !defined(DISABLE_BEACONS) -static void processBeacon (xref2osjob_t osjob) { - ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite - u1_t flags = LMIC.bcninfo.flags; - ev_t ev; - - if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) { - ev = EV_BEACON_TRACKED; - if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) { - // We don't have a previous beacon to calc some drift - assume - // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm - calcBcnRxWindowFromMillis(13,0); - goto rev; - } - // We have a previous BEACON to calculate some drift - s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx); - if( LMIC.missedBcns > 0 ) { - drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1); - } - if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) { - s2_t diff = LMIC.drift - drift; - if( diff < 0 ) diff = -diff; - LMIC.lastDriftDiff = diff; - if( LMIC.maxDriftDiff < diff ) - LMIC.maxDriftDiff = diff; - LMIC.bcninfo.flags &= ~BCN_NODDIFF; - } - LMIC.drift = drift; - LMIC.missedBcns = LMIC.rejoinCnt = 0; - LMIC.bcninfo.flags &= ~BCN_NODRIFT; - EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT, - e_.eui = MAIN::CDEV->getEui(), - e_.info = drift, - e_.info2 = /*occasion BEACON*/0)); - ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0); - } else { - ev = EV_BEACON_MISSED; - LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift; - LMIC.bcninfo.time += BCN_INTV_sec; - LMIC.missedBcns++; - // Delay any possible TX after surmised beacon - it's there although we missed it - txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4); - if( LMIC.missedBcns > MAX_MISSED_BCNS ) - LMIC.opmode |= OP_REJOIN; // try if we can roam to another network - if( LMIC.bcnRxsyms > MAX_RXSYMS ) { - LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); - reportEvent(EV_LOST_TSYNC); - return; - } - } - LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN); - LMIC.bcnRxsyms = LMIC.rxsyms; - rev: -#if CFG_us915 - LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; -#endif -#if !defined(DISABLE_PING) - if( (LMIC.opmode & OP_PINGINI) != 0 ) - rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! -#endif // !DISABLE_PING - reportEvent(ev); -} - - -static void startRxBcn (xref2osjob_t osjob) { - LMIC.osjob.func = FUNC_ADDR(processBeacon); - os_radio(RADIO_RX); -} -#endif // !DISABLE_BEACONS - - -#if !defined(DISABLE_PING) -static void startRxPing (xref2osjob_t osjob) { - LMIC.osjob.func = FUNC_ADDR(processPingRx); - os_radio(RADIO_RX); -} -#endif // !DISABLE_PING - - -// Decide what to do next for the MAC layer of a device -static void engineUpdate (void) { -#if LMIC_DEBUG_LEVEL > 0 - lmic_printf("%lu: engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode); -#endif - // Check for ongoing state: scan or TX/RX transaction - if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) - return; - -#if !defined(DISABLE_JOIN) - if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) { - LMIC_startJoining(); - return; - } -#endif // !DISABLE_JOIN - - ostime_t now = os_getTime(); - ostime_t rxtime = 0; - ostime_t txbeg = 0; - -#if !defined(DISABLE_BEACONS) - if( (LMIC.opmode & OP_TRACK) != 0 ) { - // We are tracking a beacon - ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 ); - rxtime = LMIC.bcnRxtime - RX_RAMPUP; - } -#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); - #if LMIC_DEBUG_LEVEL > 1 - if (jacc) - lmic_printf("%lu: Uplink join pending\n", os_getTime()); - else - lmic_printf("%lu: Uplink data pending\n", os_getTime()); - #endif - // Find next suitable channel and return availability time - if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) { - txbeg = LMIC.txend = nextTx(now); - LMIC.opmode &= ~OP_NEXTCHNL; - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Airtime available at %lu (channel duty limit)\n", os_getTime(), txbeg); - #endif - } else { - txbeg = LMIC.txend; - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Airtime available at %lu (previously determined)\n", os_getTime(), txbeg); - #endif - } - // Delayed TX or waiting for duty cycle? - if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) { - txbeg = LMIC.globalDutyAvail; - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Airtime available at %lu (global duty limit)\n", os_getTime(), txbeg); - #endif - } -#if !defined(DISABLE_BEACONS) - // If we're tracking a beacon... - // then make sure TX-RX transaction is complete before beacon - if( (LMIC.opmode & OP_TRACK) != 0 && - txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) { - - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Awaiting beacon before uplink\n", os_getTime()); - #endif - - // Not enough time to complete TX-RX before beacon - postpone after beacon. - // In order to avoid clustering of postponed TX right after beacon randomize start! - txDelay(rxtime + BCN_RESERVE_osticks, 16); - txbeg = 0; - goto checkrx; - } -#endif // !DISABLE_BEACONS - // Earliest possible time vs overhead to setup radio - if( txbeg - (now + TX_RAMPUP) < 0 ) { - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Ready for uplink\n", os_getTime()); - #endif - // We could send right now! - txbeg = now; - dr_t txdr = (dr_t)LMIC.datarate; -#if !defined(DISABLE_JOIN) - if( jacc ) { - u1_t ftype; - if( (LMIC.opmode & OP_REJOIN) != 0 ) { - txdr = lowerDR(txdr, LMIC.rejoinCnt); - ftype = HDR_FTYPE_REJOIN; - } else { - ftype = HDR_FTYPE_JREQ; - } - buildJoinRequest(ftype); - LMIC.osjob.func = FUNC_ADDR(jreqDone); - } else -#endif // !DISABLE_JOIN - { - if( LMIC.seqnoDn >= 0xFFFFFF80 ) { - // Imminent roll over - proactively reset MAC - EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, - e_.eui = MAIN::CDEV->getEui(), - e_.info = LMIC.seqnoDn, - e_.info2 = 0)); - // Device has to react! NWK will not roll over and just stop sending. - // Thus, we have N frames to detect a possible lock up. - reset: - os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset)); - return; - } - if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) { - // Roll over of up seq counter - EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER, - e_.eui = MAIN::CDEV->getEui(), - e_.info2 = LMIC.seqnoUp)); - // Do not run RESET event callback from here! - // App code might do some stuff after send unaware of RESET. - goto reset; - } - buildDataFrame(); - LMIC.osjob.func = FUNC_ADDR(updataDone); - } - LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr); - LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 - LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; - updateTx(txbeg); - os_radio(RADIO_TX); - return; - } - #if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: Uplink delayed until %lu\n", os_getTime(), txbeg); - #endif - // Cannot yet TX - if( (LMIC.opmode & OP_TRACK) == 0 ) - goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX - // Consider RX tasks - if( txbeg == 0 ) // zero indicates no TX pending - txbeg += 1; // TX delayed by one tick (insignificant amount of time) - } else { - // No TX pending - no scheduled RX - if( (LMIC.opmode & OP_TRACK) == 0 ) - return; - } - -#if !defined(DISABLE_BEACONS) - // Are we pingable? - checkrx: -#if !defined(DISABLE_PING) - if( (LMIC.opmode & OP_PINGINI) != 0 ) { - // One more RX slot in this beacon period? - if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) { - if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 ) - goto txdelay; - LMIC.rxsyms = LMIC.ping.rxsyms; - LMIC.rxtime = LMIC.ping.rxtime; - LMIC.freq = LMIC.ping.freq; - LMIC.rps = dndr2rps(LMIC.ping.dr); - LMIC.dataLen = 0; - ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 ); - os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing)); - return; - } - // no - just wait for the beacon - } -#endif // !DISABLE_PING - - if( txbeg != 0 && (txbeg - rxtime) < 0 ) - goto txdelay; - - setBcnRxParams(); - LMIC.rxsyms = LMIC.bcnRxsyms; - LMIC.rxtime = LMIC.bcnRxtime; - if( now - rxtime >= 0 ) { - LMIC.osjob.func = FUNC_ADDR(processBeacon); - os_radio(RADIO_RX); - return; - } - os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn)); - return; -#endif // !DISABLE_BEACONS - - txdelay: - EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY, - e_.eui = MAIN::CDEV->getEui(), - e_.info = osticks2ms(txbeg-now), - e_.info2 = LMIC.seqnoUp-1)); - os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate)); -} - - -void LMIC_setAdrMode (bit_t enabled) { - LMIC.adrEnabled = enabled ? FCT_ADREN : 0; -} - - -// Should we have/need an ext. API like this? -void LMIC_setDrTxpow (dr_t dr, s1_t txpow) { - setDrTxpow(DRCHG_SET, dr, txpow); -} - - -void LMIC_shutdown (void) { - os_clearCallback(&LMIC.osjob); - os_radio(RADIO_RST); - LMIC.opmode |= OP_SHUTDOWN; -} - - -void LMIC_reset (void) { - EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, - e_.eui = MAIN::CDEV->getEui(), - e_.info = EV_RESET)); - os_radio(RADIO_RST); - os_clearCallback(&LMIC.osjob); - - os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC)); - LMIC.devaddr = 0; - LMIC.devNonce = os_getRndU2(); - LMIC.opmode = OP_NONE; - LMIC.errcr = CR_4_5; - LMIC.adrEnabled = FCT_ADREN; - LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept - LMIC.dn2Freq = FREQ_DNW2; // ditto - LMIC.rxDelay = DELAY_DNW1; -#if !defined(DISABLE_PING) - LMIC.ping.freq = FREQ_PING; // defaults for ping - LMIC.ping.dr = DR_PING; // ditto - LMIC.ping.intvExp = 0xFF; -#endif // !DISABLE_PING -#if defined(CFG_us915) - initDefaultChannels(); -#endif - DO_DEVDB(LMIC.devaddr, devaddr); - DO_DEVDB(LMIC.devNonce, devNonce); - DO_DEVDB(LMIC.dn2Dr, dn2Dr); - DO_DEVDB(LMIC.dn2Freq, dn2Freq); -#if !defined(DISABLE_PING) - DO_DEVDB(LMIC.ping.freq, pingFreq); - DO_DEVDB(LMIC.ping.dr, pingDr); - DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); -#endif // !DISABLE_PING -} - - -void LMIC_init (void) { - LMIC.opmode = OP_SHUTDOWN; -} - - -void LMIC_clrTxData (void) { - LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL); - LMIC.pendTxLen = 0; - if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING - return; - os_clearCallback(&LMIC.osjob); - os_radio(RADIO_RST); - engineUpdate(); -} - - -void LMIC_setTxData (void) { - LMIC.opmode |= OP_TXDATA; - if( (LMIC.opmode & OP_JOINING) == 0 ) - LMIC.txCnt = 0; // cancel any ongoing TX/RX retries - engineUpdate(); -} - - -// -int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { - if( dlen > SIZEOFEXPR(LMIC.pendTxData) ) - return -2; - if( data != (xref2u1_t)0 ) - os_copyMem(LMIC.pendTxData, data, dlen); - LMIC.pendTxConf = confirmed; - LMIC.pendTxPort = port; - LMIC.pendTxLen = dlen; - LMIC_setTxData(); - return 0; -} - - -// Send a payload-less message to signal device is alive -void LMIC_sendAlive (void) { - LMIC.opmode |= OP_POLL; - engineUpdate(); -} - - -// Check if other networks are around. -void LMIC_tryRejoin (void) { - LMIC.opmode |= OP_REJOIN; - engineUpdate(); -} - -//! \brief Setup given session keys -//! and put the MAC in a state as if -//! a join request/accept would have negotiated just these keys. -//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey` -//! are unique within the network identified by `netid`. -//! NOTE: on Harvard architectures when session keys are in flash: -//! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL -//! \param netid a 24 bit number describing the network id this device is using -//! \param devaddr the 32 bit session address of the device. It is strongly recommended -//! to ensure that different devices use different numbers with high probability. -//! \param nwkKey the 16 byte network session key used for message integrity. -//! If NULL the caller has copied the key into `LMIC.nwkKey` before. -//! \param artKey the 16 byte application router session key used for message confidentiality. -//! If NULL the caller has copied the key into `LMIC.artKey` before. -void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { - LMIC.netid = netid; - LMIC.devaddr = devaddr; - if( nwkKey != (xref2u1_t)0 ) - os_copyMem(LMIC.nwkKey, nwkKey, 16); - if( artKey != (xref2u1_t)0 ) - os_copyMem(LMIC.artKey, artKey, 16); - -#if defined(CFG_eu868) - initDefaultChannels(0); -#endif - - LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); - LMIC.opmode |= OP_NEXTCHNL; - stateJustJoined(); - DO_DEVDB(LMIC.netid, netid); - DO_DEVDB(LMIC.devaddr, devaddr); - DO_DEVDB(LMIC.nwkKey, nwkkey); - DO_DEVDB(LMIC.artKey, artkey); - DO_DEVDB(LMIC.seqnoUp, seqnoUp); - DO_DEVDB(LMIC.seqnoDn, seqnoDn); -} - -// Enable/disable link check validation. -// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames -// for a while. It expects the network to provide a DN message to prove -// connectivity with a span of UP frames. If this no such prove is coming -// then the datarate is lowered and a LINK_DEAD event is generated. -// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested -// nor is the datarate changed. -// This must be called only if a session is established (e.g. after EV_JOINED) -void LMIC_setLinkCheckMode (bit_t enabled) { - LMIC.adrChanged = 0; - LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF; -} - -// Sets the max clock error to compensate for (defaults to 0, which -// allows for +/- 640 at SF7BW250). MAX_CLOCK_ERROR represents +/-100%, -// so e.g. for a +/-1% error you would pass MAX_CLOCK_ERROR * 1 / 100. -void LMIC_setClockError(u2_t error) { - LMIC.clockError = error; -} +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Copyright (c) 2016-2018 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#define LMIC_DR_LEGACY 0 +#include "lmic_bandplan.h" + +#if defined(DISABLE_BEACONS) && !defined(DISABLE_PING) +#error Ping needs beacon tracking +#endif + +DEFINE_LMIC; + + +// Fwd decls. +static void engineUpdate(void); + +#if !defined(DISABLE_BEACONS) +static void startScan (void); +#endif + +static inline void initTxrxFlags(const char *func, u1_t mask) { +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%lu: %s txrxFlags %#02x --> %02x\n", os_getTime(), func, LMIC.txrxFlags, mask); +#endif + LMIC.txrxFlags = mask; +} + +static inline void orTxrxFlags(const char *func, u1_t mask) { + initTxrxFlags(func, LMIC.txrxFlags | mask); +} + + + +// ================================================================================ +// BEG OS - default implementations for certain OS suport functions + +#if !defined(HAS_os_calls) + +#if !defined(os_rlsbf2) +u2_t os_rlsbf2 (xref2cu1_t buf) { + return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8)); +} +#endif + +#if !defined(os_rlsbf4) +u4_t os_rlsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); +} +#endif + + +#if !defined(os_rmsbf4) +u4_t os_rmsbf4 (xref2cu1_t buf) { + return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); +} +#endif + + +#if !defined(os_wlsbf2) +void os_wlsbf2 (xref2u1_t buf, u2_t v) { + buf[0] = v; + buf[1] = v>>8; +} +#endif + +#if !defined(os_wlsbf4) +void os_wlsbf4 (xref2u1_t buf, u4_t v) { + buf[0] = v; + buf[1] = v>>8; + buf[2] = v>>16; + buf[3] = v>>24; +} +#endif + +#if !defined(os_wmsbf4) +void os_wmsbf4 (xref2u1_t buf, u4_t v) { + buf[3] = v; + buf[2] = v>>8; + buf[1] = v>>16; + buf[0] = v>>24; +} +#endif + +#if !defined(os_getBattLevel) +u1_t os_getBattLevel (void) { + return MCMD_DEVS_BATT_NOINFO; +} +#endif + +#if !defined(os_crc16) +// New CRC-16 CCITT(XMODEM) checksum for beacons: +u2_t os_crc16 (xref2cu1_t data, uint len) { + u2_t remainder = 0; + u2_t polynomial = 0x1021; + for( uint i = 0; i < len; i++ ) { + remainder ^= data[i] << 8; + for( u1_t bit = 8; bit > 0; bit--) { + if( (remainder & 0x8000) ) + remainder = (remainder << 1) ^ polynomial; + else + remainder <<= 1; + } + } + return remainder; +} +#endif + +#endif // !HAS_os_calls + +// END OS - default implementations for certain OS suport functions +// ================================================================================ + +// ================================================================================ +// BEG AES + +static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) { + os_clearMem(AESaux,16); + AESaux[0] = 0x49; + AESaux[5] = dndir?1:0; + AESaux[15] = len; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); +} + + +static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) { + micB0(devaddr, seqno, dndir, len); + os_copyMem(AESkey,key,16); + // MSB because of internal structure of AES + os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len)); +} + + +static void aes_appendMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES +} + + +static int aes_verifyMic0 (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len); +} + + +static void aes_encrypt (xref2u1_t pdu, int len) { + os_getDevKey(AESkey); + os_aes(AES_ENC, pdu, len); +} + + +static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) { + if( len <= 0 ) + return; + os_clearMem(AESaux, 16); + AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1 + AESaux[5] = dndir?1:0; + os_wlsbf4(AESaux+ 6,devaddr); + os_wlsbf4(AESaux+10,seqno); + os_copyMem(AESkey,key,16); + os_aes(AES_CTR, payload, len); +} + + +static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) { + os_clearMem(nwkkey, 16); + nwkkey[0] = 0x01; + os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID); + os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce); + os_copyMem(artkey, nwkkey, 16); + artkey[0] = 0x02; + + os_getDevKey(AESkey); + os_aes(AES_ENC, nwkkey, 16); + os_getDevKey(AESkey); + os_aes(AES_ENC, artkey, 16); +} + +// END AES +// ================================================================================ + + +// ================================================================================ +// BEG LORA + +static CONST_TABLE(u1_t, SENSITIVITY)[7][3] = { + // ------------bw---------- + // 125kHz 250kHz 500kHz + { 141-109, 141-109, 141-109 }, // FSK + { 141-127, 141-124, 141-121 }, // SF7 + { 141-129, 141-126, 141-123 }, // SF8 + { 141-132, 141-129, 141-126 }, // SF9 + { 141-135, 141-132, 141-129 }, // SF10 + { 141-138, 141-135, 141-132 }, // SF11 + { 141-141, 141-138, 141-135 } // SF12 +}; + +int getSensitivity (rps_t rps) { + return -141 + TABLE_GET_U1_TWODIM(SENSITIVITY, getSf(rps), getBw(rps)); +} + +ostime_t calcAirTime (rps_t rps, u1_t plen) { + u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz + u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12 + if( sf == FSK ) { + return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8 + * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000; + } + u1_t sfx = 4*(sf+(7-SF7)); + u1_t q = sfx - (sf >= SF11 ? 8 : 0); + int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0); + if( tmp > 0 ) { + tmp = (tmp + q - 1) / q; + tmp *= getCr(rps)+5; + tmp += 8; + } else { + tmp = 8; + } + tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */; + // bw = 125000 = 15625 * 2^3 + // 250000 = 15625 * 2^4 + // 500000 = 15625 * 2^5 + // sf = 7..12 + // + // osticks = tmp * OSTICKS_PER_SEC * 1< counter reduced divisor 125000/8 => 15625 + // 2 => counter 2 shift on tmp + sfx = sf+(7-SF7) - (3+2) - bw; + int div = 15625; + if( sfx > 4 ) { + // prevent 32bit signed int overflow in last step + div >>= sfx-4; + sfx = 4; + } + // Need 32bit arithmetic for this last step + return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div; +} + +extern inline rps_t updr2rps (dr_t dr); +extern inline rps_t dndr2rps (dr_t dr); +extern inline int isFasterDR (dr_t dr1, dr_t dr2); +extern inline int isSlowerDR (dr_t dr1, dr_t dr2); +extern inline dr_t incDR (dr_t dr); +extern inline dr_t decDR (dr_t dr); +extern inline dr_t assertDR (dr_t dr); +extern inline dr_t validDR (dr_t dr); +extern inline dr_t lowerDR (dr_t dr, u1_t n); + +extern inline sf_t getSf (rps_t params); +extern inline rps_t setSf (rps_t params, sf_t sf); +extern inline bw_t getBw (rps_t params); +extern inline rps_t setBw (rps_t params, bw_t cr); +extern inline cr_t getCr (rps_t params); +extern inline rps_t setCr (rps_t params, cr_t cr); +extern inline int getNocrc (rps_t params); +extern inline rps_t setNocrc (rps_t params, int nocrc); +extern inline int getIh (rps_t params); +extern inline rps_t setIh (rps_t params, int ih); +extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); +extern inline int sameSfBw (rps_t r1, rps_t r2); + +// END LORA +// ================================================================================ + + +// Adjust DR for TX retries +// - indexed by retry count +// - return steps to lower DR +static CONST_TABLE(u1_t, DRADJUST)[2+TXCONF_ATTEMPTS] = { + // normal frames - 1st try / no retry + 0, + // confirmed frames + 0,0,1,0,1,0,1,0,0 +}; + + +// Table below defines the size of one symbol as +// symtime = 256us * 2^T(sf,bw) +// 256us is called one symunit. +// SF: +// BW: |__7___8___9__10__11__12 +// 125kHz | 2 3 4 5 6 7 +// 250kHz | 1 2 3 4 5 6 +// 500kHz | 0 1 2 3 4 5 +// + +#if !defined(DISABLE_BEACONS) +static ostime_t calcRxWindow (u1_t secs, dr_t dr) { + ostime_t rxoff, err; + if( secs==0 ) { + // aka 128 secs (next becaon) + rxoff = LMIC.drift; + err = LMIC.lastDriftDiff; + } else { + // scheduled RX window within secs into current beacon period + rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp; + err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp; + } + u1_t rxsyms = MINRX_SYMS; + err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns; + LMIC.rxsyms = MINRX_SYMS + (err / dr2hsym(dr)); + + return (rxsyms-PAMBL_SYMS) * dr2hsym(dr) + rxoff; +} + + +// Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2)) +static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) { + if( ini ) { + LMIC.drift = 0; + LMIC.maxDriftDiff = 0; + LMIC.missedBcns = 0; + LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF; + } + ostime_t hsym = dr2hsym(DR_BCN); + LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym; + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-PAMBL_SYMS) * hsym; +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +// Setup scheduled RX window (ping/multicast slot) +static void rxschedInit (xref2rxsched_t rxsched) { + os_clearMem(AESkey,16); + os_clearMem(LMIC.frame+8,8); + os_wlsbf4(LMIC.frame, LMIC.bcninfo.time); + os_wlsbf4(LMIC.frame+4, LMIC.devaddr); + os_aes(AES_ENC,LMIC.frame,16); + u1_t intvExp = rxsched->intvExp; + ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units) + rxsched->rxbase = (LMIC.bcninfo.txtime + + BCN_RESERVE_osticks + + ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks + rxsched->slot = 0; + rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<dr); + rxsched->rxsyms = LMIC.rxsyms; +} + + +static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) { + again: + if( rxsched->rxtime - cando >= 0 ) + return 1; + u1_t slot; + if( (slot=rxsched->slot) >= 128 ) + return 0; + u1_t intv = 1<intvExp; + if( (rxsched->slot = (slot += (intv))) >= 128 ) + return 0; + rxsched->rxtime = rxsched->rxbase + + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp) + - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr); + rxsched->rxsyms = LMIC.rxsyms; + goto again; +} +#endif // !DISABLE_PING) + + +ostime_t LMICcore_rndDelay (u1_t secSpan) { + u2_t r = os_getRndU2(); + ostime_t delay = r; + if( delay > OSTICKS_PER_SEC ) + delay = r % (u2_t)OSTICKS_PER_SEC; + if( secSpan > 0 ) + delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC; + return delay; +} + + +static void txDelay (ostime_t reftime, u1_t secSpan) { + reftime += LMICcore_rndDelay(secSpan); + if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) { + LMIC.globalDutyAvail = reftime; + LMIC.opmode |= OP_RNDTX; + } +} + + +void LMICcore_setDrJoin (u1_t reason, u1_t dr) { + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = LMIC.adrTxPow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); +} + + +static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) { + EV(drChange, INFO, (e_.reason = reason, + e_.deveui = MAIN::CDEV->getEui(), + e_.dr = dr|DR_PAGE, + e_.txpow = pow, + e_.prevdr = LMIC.datarate|DR_PAGE, + e_.prevtxpow = LMIC.adrTxPow)); + + if( pow != KEEP_TXPOW ) + LMIC.adrTxPow = pow; + if( LMIC.datarate != dr ) { + LMIC.datarate = dr; + DO_DEVDB(LMIC.datarate,datarate); + LMIC.opmode |= OP_NEXTCHNL; + } +} + + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void) { + LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI); +} + + +void LMIC_setPingable (u1_t intvExp) { + // Change setting + LMIC.ping.intvExp = (intvExp & 0x7); + LMIC.opmode |= OP_PINGABLE; + // App may call LMIC_enableTracking() explicitely before + // Otherwise tracking is implicitly enabled here + if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 ) + LMIC_enableTracking(0); +} + +#endif // !DISABLE_PING + +static void runEngineUpdate (xref2osjob_t osjob) { + engineUpdate(); +} + + +static void reportEvent (ev_t ev) { + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = ev)); + ON_LMIC_EVENT(ev); + engineUpdate(); +} + + +static void runReset (xref2osjob_t osjob) { + // Disable session + LMIC_reset(); +#if !defined(DISABLE_JOIN) + LMIC_startJoining(); +#endif // !DISABLE_JOIN + reportEvent(EV_RESET); +} + +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.dn2Ans = 0; +#endif + LMIC.moreData = 0; +#if !defined(DISABLE_MCMD_DCAP_REQ) + LMIC.dutyCapAns = 0; +#endif +#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) + LMIC.pingSetAns = 0; +#endif + LMIC.upRepeat = 0; + LMIC.adrAckReq = LINK_CHECK_INIT; + LMIC.dn2Dr = DR_DNW2; + LMIC.dn2Freq = FREQ_DNW2; +#if !defined(DISABLE_BEACONS) + LMIC.bcnChnl = CHNL_BCN; +#endif +#if !defined(DISABLE_PING) + LMIC.ping.freq = FREQ_PING; + LMIC.ping.dr = DR_PING; +#endif +} + + +// ================================================================================ +// Decoding frames + + +#if !defined(DISABLE_BEACONS) +// Decode beacon - do not overwrite bcninfo unless we have a match! +static int decodeBeacon (void) { + ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this + xref2u1_t d = LMIC.frame; + if(! LMICbandplan_isValidBeacon1(d)) + return 0; // first (common) part fails CRC check + // First set of fields is ok + u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF; + if( bcnnetid != LMIC.netid ) + return -1; // not the beacon we're looking for + + LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL); + // Match - update bcninfo structure + LMIC.bcninfo.snr = LMIC.snr; + LMIC.bcninfo.rssi = LMIC.rssi; + LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks; + LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]); + LMIC.bcninfo.flags |= BCN_PARTIAL; + + // Check 2nd set + if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) ) + return 1; + // Second set of fields is ok + LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit + LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto + LMIC.bcninfo.info = d[OFF_BCN_INFO]; + LMIC.bcninfo.flags |= BCN_FULL; + return 2; +} +#endif // !DISABLE_BEACONS + +// scan mac commands starting at opts[] for olen, return count of bytes consumed. +static int +scan_mac_cmds( + const uint8_t *opts, + int olen + ) { + int oidx = 0; + while( oidx < olen ) { + switch( opts[oidx] ) { + case MCMD_LCHK_ANS: { + //int gwmargin = opts[oidx+1]; + //int ngws = opts[oidx+2]; + oidx += 3; + continue; + } + 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 + oidx += 5; + + // TODO(tmm@mcci.com): LoRaWAN 1.1 requires us to process multiple + // LADR requests, and only update if all pass. So this should check + // ladrAns == 0, and only initialize if so. Need to repeat ACKs, so + // we need to count the number we see. + LMIC.ladrAns = 0x80 | // Include an answer into next frame up + MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK; + if( !LMICbandplan_mapChannels(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]))); + } + // TODO(tmm@mcci.com): see above; this needs to move outside the + // txloop. And we need to have "consistent" answers for the block + // of contiguous commands (whatever that means), and ignore the + // data rate, NbTrans (uprpt) and txPow until the last one. +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%lu: LinkAdrReq: p1:%02x chmap:%04x chpage:%02x uprt:%02x ans:%02x\n", + os_getTime(), p1, chmap, chpage, uprpt, LMIC.ladrAns + ); +#endif /* LMIC_DEBUG_LEVEL */ + + if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) { + // Nothing went wrong - use settings + LMIC.upRepeat = uprpt; + setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + } + LMIC.adrChanged = 1; // Trigger an ACK to NWK + continue; + } + case MCMD_DEVS_REQ: { + LMIC.devsAns = 1; + // LMIC.snr is SNR time 4, convert to real SNR; rounding towards zero. + const int snr = (LMIC.snr + 2) / 4; + // per [1.02] 5.5. the margin is the SNR. + LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr)); + oidx += 1; + continue; + } + case MCMD_DN2P_SET: { +#if !defined(DISABLE_MCMD_DN2P_SET) + 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 + if( validDR(dr) ) + LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK; + if( freq != 0 ) + LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK; + if (rx1DrOffset <= 3) + LMIC.dn2Ans |= MCMD_DN2P_ANS_RX1DrOffsetAck; + + if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK| MCMD_DN2P_ANS_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 + oidx += 5; + continue; + } + case MCMD_DCAP_REQ: { +#if !defined(DISABLE_MCMD_DCAP_REQ) + u1_t cap = opts[oidx+1]; + // A value cap=0xFF means device is OFF unless enabled again manually. + if( cap==0xFF ) + LMIC.opmode |= OP_SHUTDOWN; // stop any sending + LMIC.globalDutyRate = cap & 0xF; + LMIC.globalDutyAvail = os_getTime(); + DO_DEVDB(cap,dutyCap); + LMIC.dutyCapAns = 1; + oidx += 2; +#endif // !DISABLE_MCMD_DCAP_REQ + continue; + } + case MCMD_SNCH_REQ: { +#if !defined(DISABLE_MCMD_SNCH_REQ) + u1_t chidx = opts[oidx+1]; // channel + 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 + oidx += 6; + continue; + } + case MCMD_PING_SET: { +#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) + u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); + u1_t flags = 0x80; + if( freq != 0 ) { + flags |= MCMD_PING_ANS_FQACK; + LMIC.ping.freq = freq; + 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 + oidx += 4; + continue; + } + case MCMD_BCNI_ANS: { +#if !defined(DISABLE_MCMD_BCNI_ANS) && !defined(DISABLE_BEACONS) + // Ignore if tracking already enabled + if( (LMIC.opmode & OP_TRACK) == 0 ) { + LMIC.bcnChnl = opts[oidx+3]; + // Enable tracking - bcninfoTries + LMIC.opmode |= OP_TRACK; + // Cleared later in txComplete handling - triggers EV_BEACON_FOUND + 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) + - 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 + + EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS, + e_.eui = MAIN::CDEV->getEui(), + e_.lostmic = Base::lsbf4(&d[pend]), + e_.info = (LMIC.missedBcns | + (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks + - LMIC.bcnRxtime) << 8)), + e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); + } +#endif // !DISABLE_MCMD_BCNI_ANS && !DISABLE_BEACONS + oidx += 4; + continue; + } /* 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 + txParam &= (MCMD_TxParam_RxDWELL_MASK| + MCMD_TxParam_TxDWELL_MASK| + MCMD_TxParam_MaxEIRP_MASK); + LMIC.txParam = txParam; + LMIC.txParamSetupAns = 1; +#endif // LMIC_ENABLE_TxParamSetupReq + oidx += 2; + continue; + } /* end case */ + } /* end switch */ + /* unrecognized mac commands fall out of switch to here */ + 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]))); + /* stop processing options */ + break; + } /* end while */ + return oidx; +} + +static bit_t decodeFrame (void) { + xref2u1_t d = LMIC.frame; + u1_t hdr = d[0]; + u1_t ftype = hdr & HDR_FTYPE; + int dlen = LMIC.dataLen; +#if LMIC_DEBUG_LEVEL > 0 + const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); +#endif + + if( dlen < OFF_DAT_OPTS+4 || + (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || + (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) { + // Basic sanity checks failed + EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]), + e_.info2 = hdr + (dlen<<8))); + norx: +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%lu: Invalid downlink, window=%s\n", os_getTime(), window); +#endif + LMIC.dataLen = 0; + return 0; + } + // Validate exact frame length + // Note: device address was already read+evaluated in order to arrive here. + int fct = d[OFF_DAT_FCT]; + u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]); + u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]); + int olen = fct & FCT_OPTLEN; + int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame + int poff = OFF_DAT_OPTS+olen; + int pend = dlen-4; // MIC + + if( addr != LMIC.devaddr ) { + EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS, + e_.eui = MAIN::CDEV->getEui(), + e_.info = addr, + e_.info2 = LMIC.devaddr)); + goto norx; + } + if( 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))); + goto norx; + } + + int port = -1; + int replayConf = 0; + + if( pend > poff ) + port = d[poff++]; + + seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn); + + if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) { + EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC, + e_.eui1 = MAIN::CDEV->getEui(), + e_.info1 = Base::lsbf4(&d[pend]), + e_.info2 = seqno, + e_.info3 = LMIC.devaddr)); + goto norx; + } + if( seqno < LMIC.seqnoDn ) { + if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + goto norx; + } + if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || 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)); + goto norx; + } + // Replay of previous sequence number allowed only if + // previous frame and repeated both requested confirmation + replayConf = 1; + } + else { + if( seqno > LMIC.seqnoDn ) { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = seqno)); + } + 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); + } + + if( LMIC.dnConf || (fct & FCT_MORE) ) + LMIC.opmode |= OP_POLL; + + // We heard from network + LMIC.adrChanged = LMIC.rejoinCnt = 0; + if( LMIC.adrAckReq != LINK_CHECK_OFF ) + LMIC.adrAckReq = LINK_CHECK_INIT; + + 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("%lu: process options (olen=%#x)\n", os_getTime(), olen); +#endif + + xref2u1_t opts = &d[OFF_DAT_OPTS]; + int oidx = scan_mac_cmds(opts, olen); + if( oidx != olen ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = 0x1000000 + (oidx) + (olen<<8))); + } + + if( !replayConf ) { + // Handle payload only if not a replay + // Decrypt payload - if any + if( port >= 0 && pend-poff > 0 ) { + aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff); + if (port == 0) { + // this is a mac command. scan the options. +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%lu: process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff); +#endif + int optendindex = scan_mac_cmds(d+poff, pend-poff); + if (optendindex != pend-poff) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF( + "%lu: error processing mac commands for port 0 " + "(len=%#x, optendindex=%#x)\n", + os_getTime(), pend-poff, optendindex + ); +#endif + } + } + } // end decrypt payload + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = seqno, + e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN, + e_.mic = Base::lsbf4(&d[pend]), + e_.hdr = d[LORA::OFF_DAT_HDR], + e_.fct = d[LORA::OFF_DAT_FCT], + e_.port = port, + e_.plen = dlen, + e_.opts.length = olen, + memcpy(&e_.opts[0], opts, olen))); + } else { + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = Base::lsbf4(&d[pend]), + e_.info2 = seqno)); + } + + if( // NWK acks but we don't have a frame pending + (ackup && LMIC.txCnt == 0) || + // We sent up confirmed and we got a response in DNW1/DNW2 + // BUT it did not carry an ACK - this should never happen + // Do not resend and assume frame was not ACKed. + (!ackup && LMIC.txCnt != 0) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK, + e_.eui = MAIN::CDEV->getEui(), + e_.info = seqno, + e_.info2 = ackup)); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%lu: ??ack error ack=%d txCnt=%d\n", os_getTime(), ackup, LMIC.txCnt); +#endif + } + + if( LMIC.txCnt != 0 ) // we requested an ACK + orTxrxFlags(__func__, ackup ? TXRX_ACK : TXRX_NACK); + + if( port <= 0 ) { + orTxrxFlags(__func__, TXRX_NOPORT); + LMIC.dataBeg = poff; + LMIC.dataLen = 0; + } else { + orTxrxFlags(__func__, TXRX_PORT); + LMIC.dataBeg = poff; + LMIC.dataLen = pend-poff; + } +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%lu: Received downlink, window=%s, port=%d, ack=%d, txrxFlags=%#x\n", os_getTime(), window, port, ackup, LMIC.txrxFlags); +#endif + return 1; +} + + +// ================================================================================ +// TX/RX transaction support + + +static void setupRx2 (void) { + initTxrxFlags(__func__, TXRX_DNW2); + LMIC.rps = dndr2rps(LMIC.dn2Dr); + LMIC.freq = LMIC.dn2Freq; + LMIC.dataLen = 0; + os_radio(RADIO_RX); +} + + +static void schedRx12 (ostime_t delay, osjobcb_t func, u1_t dr) { + ostime_t hsym = dr2hsym(dr); + + LMIC.rxsyms = MINRX_SYMS; + + // If a clock error is specified, compensate for it by extending the + // receive window + if (LMIC.clockError != 0) { + // Calculate how much the clock will drift maximally after delay has + // passed. This indicates the amount of time we can be early + // _or_ late. + ostime_t drift = (int64_t)delay * LMIC.clockError / MAX_CLOCK_ERROR; + + // Increase the receive window by twice the maximum drift (to + // compensate for a slow or a fast clock). + // decrease the rxtime to compensate for. Note that hsym is a + // *half* symbol time, so the factor 2 is hidden. First check if + // this would overflow (which can happen if the drift is very + // high, or the symbol time is low at high datarates). + if ((255 - LMIC.rxsyms) * hsym < drift) + LMIC.rxsyms = 255; + else + LMIC.rxsyms += drift / hsym; + + } + + // Center the receive window on the center of the expected preamble + // (again note that hsym is half a sumbol time, so no /2 needed) + LMIC.rxtime = LMIC.txend + delay + PAMBL_SYMS * hsym - LMIC.rxsyms * hsym; + + LMIC_X_DEBUG_PRINTF("%lu: sched Rx12 %lu\n", os_getTime(), LMIC.rxtime - RX_RAMPUP); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); +} + +static void setupRx1 (osjobcb_t func) { + initTxrxFlags(__func__, TXRX_DNW1); + // Turn LMIC.rps from TX over to RX + LMIC.rps = setNocrc(LMIC.rps,1); + LMIC.dataLen = 0; + LMIC.osjob.func = func; + os_radio(RADIO_RX); +} + + +// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime +static void txDone (ostime_t delay, osjobcb_t func) { +#if !defined(DISABLE_PING) + if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) { + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! + LMIC.opmode |= OP_PINGINI; + } +#endif // !DISABLE_PING + + // Change RX frequency / 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. + if( LMICbandplan_isFSK() ) { + LMICbandplan_txDoneFSK(delay, func); + } + else + { + schedRx12(delay, func, LMIC.dndr); + } +} + + +// ======================================== Join frames + + +#if !defined(DISABLE_JOIN) +static void onJoinFailed (xref2osjob_t osjob) { + // Notify app - must call LMIC_reset() to stop joining + // otherwise join procedure continues. + reportEvent(EV_JOIN_FAILED); +} + + +static bit_t processJoinAccept (void) { + ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0); + ASSERT((LMIC.opmode & OP_TXRXPEND)!=0); + + if( LMIC.dataLen == 0 ) { + nojoinframe: + if( (LMIC.opmode & OP_JOINING) == 0 ) { + ASSERT((LMIC.opmode & OP_REJOIN) != 0); + // REJOIN attempt for roaming + LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND); + if( LMIC.rejoinCnt < 10 ) + LMIC.rejoinCnt++; + reportEvent(EV_REJOIN_FAILED); + return 1; + } + LMIC.opmode &= ~OP_TXRXPEND; + int failed = LMICbandplan_nextJoinState(); + EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.datarate|DR_PAGE, + e_.info2 = failed)); + // Build next JOIN REQUEST with next engineUpdate call + // Optionally, report join failed. + // Both after a random/chosen amount of ticks. That time + // is in LMIC.txend. The delay here is either zero or 1 + // tick; onJoinFailed()/runEngineUpdate() are responsible + // for honoring that. XXX(tmm@mcci.com) The IBM 1.6 code + // claimed to return a delay but really returns 0 or 1. + // Once we update as923 to return failed after dr2, we + // can take out this #if. +#if CFG_region != LMIC_REGION_as923 + os_setTimedCallback(&LMIC.osjob, os_getTime()+failed, + failed + ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed + : FUNC_ADDR(runEngineUpdate)); // next step to be delayed +#else + // in the join of AS923 v1.1 older, only DR2 is used. Therefore, + // not much improvement when it handles two different behavior; + // onJoinFailed or runEngineUpdate. + os_setTimedCallback(&LMIC.osjob, os_getTime()+failed, + FUNC_ADDR(onJoinFailed)); +#endif + return 1; + } + u1_t hdr = LMIC.frame[0]; + u1_t dlen = LMIC.dataLen; + u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt! + if( (dlen != LEN_JA && dlen != LEN_JAEXT) + || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME, + e_.eui = MAIN::CDEV->getEui(), + e_.info = dlen < 4 ? 0 : mic, + e_.info2 = hdr + (dlen<<8))); + badframe: + if( (LMIC.txrxFlags & TXRX_DNW1) != 0 ) + return 0; + goto nojoinframe; + } + aes_encrypt(LMIC.frame+1, dlen-1); + if( !aes_verifyMic0(LMIC.frame, dlen-4) ) { + EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC, + e_.info = mic)); + goto badframe; + } + + u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR); + LMIC.devaddr = addr; + LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF; + + // initDefaultChannels(0) for EU-like, nothing otherwise + LMICbandplan_joinAcceptChannelClear(); + + if (!LMICbandplan_hasJoinCFlist() && dlen > LEN_JA) { + // if no JoinCFList, we're supposed to continue + // the join per 2.2.5 of LoRaWAN regional 2.2.4 + // https://github.com/mcci-catena/arduino-lmic/issues/19 + } else if ( LMICbandplan_hasJoinCFlist() && dlen > LEN_JA ) { + dlen = OFF_CFLIST; + for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) { + u4_t freq = LMICbandplan_convFreq(&LMIC.frame[dlen]); + if( freq ) { + LMIC_setupChannel(chidx, freq, 0, -1); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%lu: Setup channel, idx=%d, freq=%lu\n", os_getTime(), chidx, (unsigned long)freq); +#endif + } + } + } + + // already incremented when JOIN REQ got sent off + aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey); + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + + EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(), + e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.oldaddr = oldaddr, + e_.nonce = LMIC.devNonce-1, + e_.mic = mic, + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_ACCEPT + : EV::joininfo_t::ACCEPT))); + + ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0); + // + // XXX(tmm@mcci.com) OP_REJOIN confuses me, and I'm not sure why we're + // adjusting DRs here. We've just recevied a join accept, and the + // datarate therefore shouldn't be in play. + // + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // TODO(tmm@mcci.com) regionalize + // Lower DR every try below current UP DR + LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt); +#else + // in the join of AS923 v1.1 or older, only DR2 (SF10) is used. + LMIC.datarate = AS923_DR_SF10; +#endif + } + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL; + LMIC.txCnt = 0; + stateJustJoined(); + LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F; + LMIC.rx1DrOffset = (LMIC.frame[OFF_JA_DLSET] >> 4) & 0x7; + LMIC.rxDelay = LMIC.frame[OFF_JA_RXDLY]; + if (LMIC.rxDelay == 0) LMIC.rxDelay = 1; + reportEvent(EV_JOINED); + return 1; +} + + +static void processRx2Jacc (xref2osjob_t osjob) { + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + } + processJoinAccept(); +} + + +static void setupRx2Jacc (xref2osjob_t osjob) { + LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); + setupRx2(); +} + + +static void processRx1Jacc (xref2osjob_t osjob) { + if( LMIC.dataLen == 0 || !processJoinAccept() ) + schedRx12(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc), LMIC.dn2Dr); +} + + +static void setupRx1Jacc (xref2osjob_t osjob) { + setupRx1(FUNC_ADDR(processRx1Jacc)); +} + + +static void jreqDone (xref2osjob_t osjob) { + txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc)); +} + +#endif // !DISABLE_JOIN + +// ======================================== Data frames + +// Fwd decl. +static bit_t processDnData(void); + +static void processRx2DnDataDelay (xref2osjob_t osjob) { + processDnData(); +} + +static void processRx2DnData (xref2osjob_t osjob) { + if( LMIC.dataLen == 0 ) { + initTxrxFlags(__func__, 0); // nothing in 1st/2nd DN slot + // Delay callback processing to avoid up TX while gateway is txing our missed frame! + // Since DNW2 uses SF12 by default we wait 3 secs. + os_setTimedCallback(&LMIC.osjob, + (os_getTime() + DNW2_SAFETY_ZONE + LMICcore_rndDelay(2)), + FUNC_ADDR(processRx2DnDataDelay)); + return; + } + processDnData(); +} + + +static void setupRx2DnData (xref2osjob_t osjob) { + LMIC.osjob.func = FUNC_ADDR(processRx2DnData); + setupRx2(); +} + + +static void processRx1DnData (xref2osjob_t osjob) { + if( LMIC.dataLen == 0 || !processDnData() ) + schedRx12(sec2osticks(LMIC.rxDelay +(int)DELAY_EXTDNW2), FUNC_ADDR(setupRx2DnData), LMIC.dn2Dr); +} + + +static void setupRx1DnData (xref2osjob_t osjob) { + setupRx1(FUNC_ADDR(processRx1DnData)); +} + + +static void updataDone (xref2osjob_t osjob) { + txDone(sec2osticks(LMIC.rxDelay), FUNC_ADDR(setupRx1DnData)); +} + +// ======================================== + + +static void buildDataFrame (void) { + bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL); + u1_t dlen = txdata ? LMIC.pendTxLen : 0; + + // Piggyback MAC options + // Prioritize by importance + int end = OFF_DAT_OPTS; +#if !defined(DISABLE_PING) + if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) { + // Indicate pingability in every UP frame + LMIC.frame[end] = MCMD_PING_IND; + LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4); + end += 2; + } +#endif // !DISABLE_PING +#if !defined(DISABLE_MCMD_DCAP_REQ) + if( LMIC.dutyCapAns ) { + LMIC.frame[end] = MCMD_DCAP_ANS; + end += 1; + LMIC.dutyCapAns = 0; + } +#endif // !DISABLE_MCMD_DCAP_REQ + if( LMIC.devsAns ) { // answer to device status + LMIC.frame[end+0] = MCMD_DEVS_ANS; + LMIC.frame[end+1] = os_getBattLevel(); + LMIC.frame[end+2] = LMIC.devAnsMargin; + end += 3; + LMIC.devsAns = 0; + } + if( LMIC.ladrAns ) { // answer to ADR change + LMIC.frame[end+0] = MCMD_LADR_ANS; + LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU; + end += 2; + LMIC.ladrAns = 0; + } +#if !defined(DISABLE_BEACONS) + if( LMIC.bcninfoTries > 0 ) { + LMIC.frame[end] = MCMD_BCNI_REQ; + end += 1; + } +#endif // !DISABLE_BEACONS + if( LMIC.adrChanged ) { + if( LMIC.adrAckReq < 0 ) + LMIC.adrAckReq = 0; + 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 + ASSERT(end <= OFF_DAT_OPTS+16); + + u1_t flen = end + (txdata ? 5+dlen : 4); + if( flen > MAX_LEN_FRAME ) { + // Options and payload too big - delay payload + txdata = 0; + flen = end+4; + } + LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1; + LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled + | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0) + | (end-OFF_DAT_OPTS)); + os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr); + + if( LMIC.txCnt == 0 ) { + LMIC.seqnoUp += 1; + DO_DEVDB(LMIC.seqnoUp,seqnoUp); + } else { + EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoUp-1, + e_.info2 = ((LMIC.txCnt+1) | + (TABLE_GET_U1(DRADJUST, LMIC.txCnt+1) << 8) | + ((LMIC.datarate|DR_PAGE)<<16)))); + } + os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1); + + // Clear pending DN confirmation + LMIC.dnConf = 0; + + if( txdata ) { + if( LMIC.pendTxConf ) { + // 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; + } + LMIC.frame[end] = LMIC.pendTxPort; + os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen); + aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey, + LMIC.devaddr, LMIC.seqnoUp-1, + /*up*/0, LMIC.frame+end+1, dlen); + } + aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4); + + EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(), + e_.devaddr = LMIC.devaddr, + e_.seqno = LMIC.seqnoUp-1, + e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP), + e_.mic = Base::lsbf4(&LMIC.frame[flen-4]), + e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR], + e_.fct = LMIC.frame[LORA::OFF_DAT_FCT], + e_.port = LMIC.pendTxPort, + e_.plen = txdata ? dlen : 0, + e_.opts.length = end-LORA::OFF_DAT_OPTS, + memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS))); + LMIC.dataLen = flen; +} + + +#if !defined(DISABLE_BEACONS) +// Callback from HAL during scan mode or when job timer expires. +static void onBcnRx (xref2osjob_t job) { + // If we arrive via job timer make sure to put radio to rest. + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + if( LMIC.dataLen == 0 ) { + // Nothing received - timeout + LMIC.opmode &= ~(OP_SCAN | OP_TRACK); + reportEvent(EV_SCAN_TIMEOUT); + return; + } + if( decodeBeacon() <= 0 ) { + // Something is wrong with the beacon - continue scan + LMIC.dataLen = 0; + os_radio(RADIO_RXON); + os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx)); + return; + } + // Found our 1st beacon + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,1); + LMIC.opmode &= ~OP_SCAN; // turn SCAN off + LMIC.opmode |= OP_TRACK; // auto enable tracking + reportEvent(EV_BEACON_FOUND); // can be disabled in callback +} + + +// Enable receiver to listen to incoming beacons +// netid defines when scan stops (any or specific beacon) +// This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON +// Implicitely cancels any pending TX/RX transaction. +// Also cancels an onpoing joining procedure. +static void startScan (void) { + ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0); + if( (LMIC.opmode & OP_SHUTDOWN) != 0 ) + return; + // Cancel onging TX/RX transaction + LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0; + LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND); + LMICbandplan_setBcnRxParams(); + LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx)); + os_radio(RADIO_RXON); +} + + +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. + if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) + startScan(); + return 1; // enabled +} + + +void LMIC_disableTracking (void) { + LMIC.opmode &= ~(OP_SCAN|OP_TRACK); + LMIC.bcninfoTries = 0; + engineUpdate(); +} +#endif // !DISABLE_BEACONS + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ================================================================================ +// +// Join stuff +// +// ================================================================================ + +#if !defined(DISABLE_JOIN) +static void buildJoinRequest (u1_t ftype) { + // Do not use pendTxData since we might have a pending + // user level frame in there. Use RX holding area instead. + xref2u1_t d = LMIC.frame; + d[OFF_JR_HDR] = ftype; + os_getArtEui(d + OFF_JR_ARTEUI); + os_getDevEui(d + OFF_JR_DEVEUI); + os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce); + aes_appendMic0(d, OFF_JR_MIC); + + EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(), + e_.arteui = MAIN::CDEV->getArtEui(), + e_.nonce = LMIC.devNonce, + e_.oldaddr = LMIC.devaddr, + e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]), + e_.reason = ((LMIC.opmode & OP_REJOIN) != 0 + ? EV::joininfo_t::REJOIN_REQUEST + : EV::joininfo_t::REQUEST))); + LMIC.dataLen = LEN_JR; + LMIC.devNonce++; + DO_DEVDB(LMIC.devNonce,devNonce); +} + +static void startJoining (xref2osjob_t osjob) { + reportEvent(EV_JOINING); +} + +// Start join procedure if not already joined. +bit_t LMIC_startJoining (void) { + if( LMIC.devaddr == 0 ) { + // There should be no TX/RX going on + ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0); + // Lift any previous duty limitation + LMIC.globalDutyRate = 0; + // Cancel scanning + LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL); + // Setup state + LMIC.rejoinCnt = LMIC.txCnt = 0; + LMICbandplan_initJoinLoop(); + LMIC.opmode |= OP_JOINING; + // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS + os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining)); + return 1; + } + return 0; // already joined +} +#endif // !DISABLE_JOIN + + +// ================================================================================ +// +// +// +// ================================================================================ + +#if !defined(DISABLE_PING) +static void processPingRx (xref2osjob_t osjob) { + if( LMIC.dataLen != 0 ) { + initTxrxFlags(__func__, TXRX_PING); + if( decodeFrame() ) { + reportEvent(EV_RXCOMPLETE); + return; + } + } + // Pick next ping slot + engineUpdate(); +} +#endif // !DISABLE_PING + + +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); + } else { + // Nothing received - implies no port + initTxrxFlags(__func__, TXRX_NOPORT); + } + if( LMIC.adrAckReq != LINK_CHECK_OFF ) + LMIC.adrAckReq += 1; + LMIC.dataBeg = LMIC.dataLen = 0; + txcomplete: + LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND); + if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) { + LMIC.opmode &= ~OP_LINKDEAD; + reportEvent(EV_LINK_ALIVE); + } + reportEvent(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)); + setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW); + LMIC.adrAckReq = LINK_CHECK_CONT; + LMIC.opmode |= OP_REJOIN|OP_LINKDEAD; + reportEvent(EV_LINK_DEAD); + } +#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 ) { + reportEvent(EV_BEACON_FOUND); + 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; +} + + +#if !defined(DISABLE_BEACONS) +static void processBeacon (xref2osjob_t osjob) { + ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite + u1_t flags = LMIC.bcninfo.flags; + ev_t ev; + + if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) { + ev = EV_BEACON_TRACKED; + if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) { + // We don't have a previous beacon to calc some drift - assume + // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm + calcBcnRxWindowFromMillis(13,0); + goto rev; + } + // We have a previous BEACON to calculate some drift + s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx); + if( LMIC.missedBcns > 0 ) { + drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1); + } + if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) { + s2_t diff = LMIC.drift - drift; + if( diff < 0 ) diff = -diff; + LMIC.lastDriftDiff = diff; + if( LMIC.maxDriftDiff < diff ) + LMIC.maxDriftDiff = diff; + LMIC.bcninfo.flags &= ~BCN_NODDIFF; + } + LMIC.drift = drift; + LMIC.missedBcns = LMIC.rejoinCnt = 0; + LMIC.bcninfo.flags &= ~BCN_NODRIFT; + EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT, + e_.eui = MAIN::CDEV->getEui(), + e_.info = drift, + e_.info2 = /*occasion BEACON*/0)); + ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0); + } else { + ev = EV_BEACON_MISSED; + LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift; + LMIC.bcninfo.time += BCN_INTV_sec; + LMIC.missedBcns++; + // Delay any possible TX after surmised beacon - it's there although we missed it + txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4); + if( LMIC.missedBcns > MAX_MISSED_BCNS ) + LMIC.opmode |= OP_REJOIN; // try if we can roam to another network + if( LMIC.bcnRxsyms > MAX_RXSYMS ) { + LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN); + reportEvent(EV_LOST_TSYNC); + return; + } + } + LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN); + LMIC.bcnRxsyms = LMIC.rxsyms; + rev: + LMICbandplan_advanceBeaconChannel(); +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) + rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! +#endif // !DISABLE_PING + reportEvent(ev); +} + + +static void startRxBcn (xref2osjob_t osjob) { + LMIC.osjob.func = FUNC_ADDR(processBeacon); + os_radio(RADIO_RX); +} +#endif // !DISABLE_BEACONS + + +#if !defined(DISABLE_PING) +static void startRxPing (xref2osjob_t osjob) { + LMIC.osjob.func = FUNC_ADDR(processPingRx); + os_radio(RADIO_RX); +} +#endif // !DISABLE_PING + + +// Decide what to do next for the MAC layer of a device +static void engineUpdate (void) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("%lu: engineUpdate, opmode=0x%x\n", os_getTime(), LMIC.opmode); +#endif + // Check for ongoing state: scan or TX/RX transaction + if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) + return; + +#if !defined(DISABLE_JOIN) + if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) { + LMIC_startJoining(); + return; + } +#endif // !DISABLE_JOIN + + ostime_t now = os_getTime(); + ostime_t rxtime = 0; + ostime_t txbeg = 0; + +#if !defined(DISABLE_BEACONS) + if( (LMIC.opmode & OP_TRACK) != 0 ) { + // We are tracking a beacon + ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 ); + rxtime = LMIC.bcnRxtime - RX_RAMPUP; + } +#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 + if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) { + txbeg = LMIC.txend = LMICbandplan_nextTx(now); + LMIC.opmode &= ~OP_NEXTCHNL; + } else { + txbeg = LMIC.txend; + } + // Delayed TX or waiting for duty cycle? + if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) + txbeg = LMIC.globalDutyAvail; +#if !defined(DISABLE_BEACONS) + // If we're tracking a beacon... + // then make sure TX-RX transaction is complete before beacon + if( (LMIC.opmode & OP_TRACK) != 0 && + txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) { + // Not enough time to complete TX-RX before beacon - postpone after beacon. + // In order to avoid clustering of postponed TX right after beacon randomize start! + txDelay(rxtime + BCN_RESERVE_osticks, 16); + txbeg = 0; + goto checkrx; + } +#endif // !DISABLE_BEACONS + // Earliest possible time vs overhead to setup radio + if( txbeg - (now + TX_RAMPUP) < 0 ) { + // We could send right now! + txbeg = now; + dr_t txdr = (dr_t)LMIC.datarate; +#if !defined(DISABLE_JOIN) + if( jacc ) { + u1_t ftype; + if( (LMIC.opmode & OP_REJOIN) != 0 ) { +#if CFG_region != LMIC_REGION_as923 + // in AS923 v1.1 or older, no need to change the datarate. + txdr = lowerDR(txdr, LMIC.rejoinCnt); +#endif + ftype = HDR_FTYPE_REJOIN; + } else { + ftype = HDR_FTYPE_JREQ; + } + buildJoinRequest(ftype); + LMIC.osjob.func = FUNC_ADDR(jreqDone); + } else +#endif // !DISABLE_JOIN + { + if( LMIC.seqnoDn >= 0xFFFFFF80 ) { + // Imminent roll over - proactively reset MAC + EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info = LMIC.seqnoDn, + e_.info2 = 0)); + // Device has to react! NWK will not roll over and just stop sending. + // Thus, we have N frames to detect a possible lock up. + reset: + os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset)); + return; + } + if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) { + // Roll over of up seq counter + EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER, + e_.eui = MAIN::CDEV->getEui(), + e_.info2 = LMIC.seqnoUp)); + // Do not run RESET event callback from here! + // App code might do some stuff after send unaware of RESET. + goto reset; + } + buildDataFrame(); + LMIC.osjob.func = FUNC_ADDR(updataDone); + } + LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr); + LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 + LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; + LMICbandplan_updateTx(txbeg); + reportEvent(EV_TXSTART); + os_radio(RADIO_TX); + return; + } + // Cannot yet TX + if( (LMIC.opmode & OP_TRACK) == 0 ) + goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX + // Consider RX tasks + if( txbeg == 0 ) // zero indicates no TX pending + txbeg += 1; // TX delayed by one tick (insignificant amount of time) + } else { + // No TX pending - no scheduled RX + if( (LMIC.opmode & OP_TRACK) == 0 ) + return; + } + +#if !defined(DISABLE_BEACONS) + // Are we pingable? + checkrx: +#if !defined(DISABLE_PING) + if( (LMIC.opmode & OP_PINGINI) != 0 ) { + // One more RX slot in this beacon period? + if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) { + if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 ) + goto txdelay; + LMIC.rxsyms = LMIC.ping.rxsyms; + LMIC.rxtime = LMIC.ping.rxtime; + LMIC.freq = LMIC.ping.freq; + LMIC.rps = dndr2rps(LMIC.ping.dr); + LMIC.dataLen = 0; + ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 ); + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing)); + return; + } + // no - just wait for the beacon + } +#endif // !DISABLE_PING + + if( txbeg != 0 && (txbeg - rxtime) < 0 ) + goto txdelay; + + LMICbandplan_setBcnRxParams(); + LMIC.rxsyms = LMIC.bcnRxsyms; + LMIC.rxtime = LMIC.bcnRxtime; + if( now - rxtime >= 0 ) { + LMIC.osjob.func = FUNC_ADDR(processBeacon); + os_radio(RADIO_RX); + return; + } + os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn)); + return; +#endif // !DISABLE_BEACONS + + txdelay: + EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY, + e_.eui = MAIN::CDEV->getEui(), + e_.info = osticks2ms(txbeg-now), + e_.info2 = LMIC.seqnoUp-1)); + LMIC_X_DEBUG_PRINTF("%lu: next engine update in %lu\n", now, txbeg-TX_RAMPUP); + os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate)); +} + + +void LMIC_setAdrMode (bit_t enabled) { + LMIC.adrEnabled = enabled ? FCT_ADREN : 0; +} + + +// Should we have/need an ext. API like this? +void LMIC_setDrTxpow (dr_t dr, s1_t txpow) { + setDrTxpow(DRCHG_SET, dr, txpow); +} + + +void LMIC_shutdown (void) { + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + LMIC.opmode |= OP_SHUTDOWN; +} + + +void LMIC_reset (void) { + EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV, + e_.eui = MAIN::CDEV->getEui(), + e_.info = EV_RESET)); + os_radio(RADIO_RST); + os_clearCallback(&LMIC.osjob); + + os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC)); + LMIC.devaddr = 0; + LMIC.devNonce = os_getRndU2(); + LMIC.opmode = OP_NONE; + LMIC.errcr = CR_4_5; + LMIC.adrEnabled = FCT_ADREN; + LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept + LMIC.dn2Freq = FREQ_DNW2; // ditto + LMIC.rxDelay = DELAY_DNW1; +#if !defined(DISABLE_PING) + LMIC.ping.freq = FREQ_PING; // defaults for ping + LMIC.ping.dr = DR_PING; // ditto + LMIC.ping.intvExp = 0xFF; +#endif // !DISABLE_PING + LMICbandplan_resetDefaultChannels(); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.devNonce, devNonce); + DO_DEVDB(LMIC.dn2Dr, dn2Dr); + DO_DEVDB(LMIC.dn2Freq, dn2Freq); +#if !defined(DISABLE_PING) + DO_DEVDB(LMIC.ping.freq, pingFreq); + DO_DEVDB(LMIC.ping.dr, pingDr); + DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); +#endif // !DISABLE_PING +} + + +void LMIC_init (void) { + LMIC.opmode = OP_SHUTDOWN; + LMICbandplan_init(); +} + + +void LMIC_clrTxData (void) { + LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL); + LMIC.pendTxLen = 0; + if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING + return; + os_clearCallback(&LMIC.osjob); + os_radio(RADIO_RST); + engineUpdate(); +} + + +void LMIC_setTxData (void) { + LMIC.opmode |= OP_TXDATA; + if( (LMIC.opmode & OP_JOINING) == 0 ) + LMIC.txCnt = 0; // cancel any ongoing TX/RX retries + engineUpdate(); +} + + +// +int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { + if( dlen > SIZEOFEXPR(LMIC.pendTxData) ) + return -2; + if( data != (xref2u1_t)0 ) + os_copyMem(LMIC.pendTxData, data, dlen); + LMIC.pendTxConf = confirmed; + LMIC.pendTxPort = port; + LMIC.pendTxLen = dlen; + LMIC_setTxData(); + return 0; +} + + +// Send a payload-less message to signal device is alive +void LMIC_sendAlive (void) { + LMIC.opmode |= OP_POLL; + engineUpdate(); +} + + +// Check if other networks are around. +void LMIC_tryRejoin (void) { + LMIC.opmode |= OP_REJOIN; + engineUpdate(); +} + +//! \brief Setup given session keys +//! and put the MAC in a state as if +//! a join request/accept would have negotiated just these keys. +//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey` +//! are unique within the network identified by `netid`. +//! NOTE: on Harvard architectures when session keys are in flash: +//! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL +//! \param netid a 24 bit number describing the network id this device is using +//! \param devaddr the 32 bit session address of the device. It is strongly recommended +//! to ensure that different devices use different numbers with high probability. +//! \param nwkKey the 16 byte network session key used for message integrity. +//! If NULL the caller has copied the key into `LMIC.nwkKey` before. +//! \param artKey the 16 byte application router session key used for message confidentiality. +//! If NULL the caller has copied the key into `LMIC.artKey` before. + +// TODO(tmm@mcci.com) we ought to also save the channels that were returned by the +// join accept; right now this has to be done by the caller (or it doesn't get done). +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + LMIC.netid = netid; + LMIC.devaddr = devaddr; + if( nwkKey != (xref2u1_t)0 ) + os_copyMem(LMIC.nwkKey, nwkKey, 16); + if( artKey != (xref2u1_t)0 ) + os_copyMem(LMIC.artKey, artKey, 16); + + LMICbandplan_setSessionInitDefaultChannels(); + + LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI); + LMIC.opmode |= OP_NEXTCHNL; + stateJustJoined(); + DO_DEVDB(LMIC.netid, netid); + DO_DEVDB(LMIC.devaddr, devaddr); + DO_DEVDB(LMIC.nwkKey, nwkkey); + DO_DEVDB(LMIC.artKey, artkey); + DO_DEVDB(LMIC.seqnoUp, seqnoUp); + DO_DEVDB(LMIC.seqnoDn, seqnoDn); +} + +// Enable/disable link check validation. +// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames +// for a while. It expects the network to provide a DN message to prove +// connectivity with a span of UP frames. If this no such prove is coming +// then the datarate is lowered and a LINK_DEAD event is generated. +// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested +// nor is the datarate changed. +// This must be called only if a session is established (e.g. after EV_JOINED) +void LMIC_setLinkCheckMode (bit_t enabled) { + LMIC.adrChanged = 0; + LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF; +} + +// Sets the max clock error to compensate for (defaults to 0, which +// allows for +/- 640 at SF7BW250). MAX_CLOCK_ERROR represents +/-100%, +// so e.g. for a +/-1% error you would pass MAX_CLOCK_ERROR * 1 / 100. +void LMIC_setClockError(u2_t error) { + LMIC.clockError = error; +} + +// \brief return the uplink sequence number for the next transmission. +// This simple getter returns the uplink sequence number maintained by the LMIC engine. +// The caller should store the value and restore it (see LMIC_setSeqnoUp) on +// LMIC initialization to ensure monotonically increasing sequence numbers. +// It's also useful in debugging, as it allows you to correlate a debug trace event with +// a specific packet sent over the air. +u4_t LMIC_getSeqnoUp(void) { + return LMIC.seqnoUp; +} + +// \brief set the uplink sequence number for the next transmission. +// Use the function on startup to ensure that the next transmission uses +// a sequence number higher than the last transmission. +u4_t LMIC_setSeqnoUp(u4_t seq_no) { + u4_t last = LMIC.seqnoUp; + LMIC.seqnoUp = seq_no; + return last; +} + +// \brief return the current session keys returned from join. +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey) { + *netid = LMIC.netid; + *devaddr = LMIC.devaddr; + memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey)); + memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey)); +} diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.h b/lib/arduino-lmic-master/src/lmic/lmic.h similarity index 65% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.h rename to lib/arduino-lmic-master/src/lmic/lmic.h index 4a4c86be..fa27d960 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/lmic.h +++ b/lib/arduino-lmic-master/src/lmic/lmic.h @@ -1,320 +1,433 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -//! @file -//! @brief LMIC API - -#ifndef _lmic_h_ -#define _lmic_h_ - -#include "oslmic.h" -#include "lorabase.h" - -#ifdef __cplusplus -extern "C"{ -#endif - -// LMIC version -#define LMIC_VERSION_MAJOR 1 -#define LMIC_VERSION_MINOR 5 -#define LMIC_VERSION_BUILD 1431528305 - -enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length -enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames -enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests -enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this - -enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link - LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead - LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate - LINK_CHECK_OFF =-128 }; // link check disabled - -enum { TIME_RESYNC = 6*128 }; // secs -enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon -enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon -enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time -enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send - -#if defined(CFG_eu868) // EU868 spectrum ==================================================== - -enum { MAX_CHANNELS = 16 }; //!< Max supported channels -enum { MAX_BANDS = 4 }; - -enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels -//! \internal -struct band_t { - u2_t txcap; // duty cycle limitation: 1/txcap - s1_t txpow; // maximum TX power - u1_t lastchnl; // last used channel - ostime_t avail; // channel is blocked until this time -}; -TYPEDEF_xref2band_t; //!< \internal - -#elif defined(CFG_us915) // US915 spectrum ================================================= - -enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable -enum { MAX_TXPOW_125kHz = 30 }; - -#endif // ========================================================================== - -// Keep in sync with evdefs.hpp::drChange -enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD }; -enum { KEEP_TXPOW = -128 }; - - -#if !defined(DISABLE_PING) -//! \internal -struct rxsched_t { - u1_t dr; - u1_t intvExp; // 0..7 - u1_t slot; // runs from 0 to 128 - u1_t rxsyms; - ostime_t rxbase; - ostime_t rxtime; // start of next spot - u4_t freq; -}; -TYPEDEF_xref2rxsched_t; //!< \internal -#endif // !DISABLE_PING - - -#if !defined(DISABLE_BEACONS) -//! Parsing and tracking states of beacons. -enum { BCN_NONE = 0x00, //!< No beacon received - BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) - BCN_FULL = 0x02, //!< Full beacon decoded - BCN_NODRIFT = 0x04, //!< No drift value measured yet - BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet -//! Information about the last and previous beacons. -struct bcninfo_t { - ostime_t txtime; //!< Time when the beacon was sent - s1_t rssi; //!< Adjusted RSSI value of last received beacon - s1_t snr; //!< Scaled SNR value of last received beacon - u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. - u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) - // - u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) - s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) - s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) -}; -#endif // !DISABLE_BEACONS - -// purpose of receive window - lmic_t.rxState -enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 }; -// Netid values / lmic_t.netid -enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; -// MAC operation modes (lmic_t.opmode). -enum { OP_NONE = 0x0000, - OP_SCAN = 0x0001, // radio scan to find a beacon - OP_TRACK = 0x0002, // track my networks beacon (netid) - OP_JOINING = 0x0004, // device joining in progress (blocks other activities) - OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) - OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data - OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST - OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything - OP_TXRXPEND = 0x0080, // TX/RX transaction pending - OP_RNDTX = 0x0100, // prevent TX lining up after a beacon - OP_PINGINI = 0x0200, // pingable is initialized and scheduling active - OP_PINGABLE = 0x0400, // we're pingable - OP_NEXTCHNL = 0x0800, // find a new channel - OP_LINKDEAD = 0x1000, // link was reported as dead - OP_TESTMODE = 0x2000, // developer test mode -}; -// TX-RX transaction flags - report back to user -enum { TXRX_ACK = 0x80, // confirmed UP frame was acked - TXRX_NACK = 0x40, // confirmed UP frame was not acked - TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port - TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port - TXRX_DNW1 = 0x01, // received in 1st DN slot - TXRX_DNW2 = 0x02, // received in 2dn DN slot - TXRX_PING = 0x04 }; // received in a scheduled RX slot -// Event types for event callback -enum _ev_t { EV_SCAN_TIMEOUT=1, 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 }; -typedef enum _ev_t ev_t; - -enum { - // This value represents 100% error in LMIC.clockError - MAX_CLOCK_ERROR = 65536, -}; - -struct lmic_t { - // Radio settings TX/RX (also accessed by HAL) - ostime_t txend; - ostime_t rxtime; - u4_t freq; - s1_t rssi; - s1_t snr; - rps_t rps; - u1_t rxsyms; - u1_t dndr; - s1_t txpow; // dBm - - osjob_t osjob; - - // Channel scheduling -#if defined(CFG_eu868) - band_t bands[MAX_BANDS]; - u4_t channelFreq[MAX_CHANNELS]; - u2_t channelDrMap[MAX_CHANNELS]; - u2_t channelMap; -#elif defined(CFG_us915) - u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) - u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto - u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits - u2_t chRnd; // channel randomizer -#endif - u1_t txChnl; // channel for next TX - u1_t globalDutyRate; // max rate: 1/2^k - ostime_t globalDutyAvail; // time device can send again - - u4_t netid; // current network id (~0 - none) - u2_t opmode; - u1_t upRepeat; // configured up repeat - s1_t adrTxPow; // ADR adjusted TX power - u1_t datarate; // current data rate - u1_t errcr; // error coding rate (used for TX only) - u1_t rejoinCnt; // adjustment for rejoin datarate -#if !defined(DISABLE_BEACONS) - s2_t drift; // last measured drift - s2_t lastDriftDiff; - s2_t maxDriftDiff; -#endif - - u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX - // represents +/-100% error - - u1_t pendTxPort; - u1_t pendTxConf; // confirmed data - u1_t pendTxLen; // +0x80 = confirmed - u1_t pendTxData[MAX_LEN_PAYLOAD]; - - u2_t devNonce; // last generated nonce - u1_t nwkKey[16]; // network session key - u1_t artKey[16]; // application router session key - devaddr_t devaddr; - u4_t seqnoDn; // device level down stream seqno - u4_t seqnoUp; - - u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 - s1_t adrAckReq; // counter until we reset data rate (0=off) - 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 - 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 - // 2nd RX window (after up stream) - u1_t dn2Dr; - u4_t dn2Freq; -#if !defined(DISABLE_MCMD_DN2P_SET) - u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs -#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 -#if !defined(DISABLE_PING) - rxsched_t ping; // pingable setup -#endif - - // Public part of MAC state - u1_t txCnt; - u1_t txrxFlags; // transaction flags (TX-RX combo) - u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) - u1_t dataLen; // 0 no data or zero length data, >0 byte count of data - u1_t frame[MAX_LEN_FRAME]; - -#if !defined(DISABLE_BEACONS) - u1_t bcnChnl; - u1_t bcnRxsyms; // - ostime_t bcnRxtime; - bcninfo_t bcninfo; // Last received beacon info -#endif -}; -//! \var struct lmic_t LMIC -//! The state of LMIC MAC layer is encapsulated in this variable. -DECLARE_LMIC; //!< \internal - -//! Construct a bit map of allowed datarates from drlo to drhi (both included). -#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) -#if defined(CFG_eu868) -enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 }; -bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); -#endif -bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); -void LMIC_disableChannel (u1_t channel); -#if defined(CFG_us915) -void LMIC_enableChannel (u1_t channel); -void LMIC_enableSubBand (u1_t band); -void LMIC_disableSubBand (u1_t band); -void LMIC_selectSubBand (u1_t band); -#endif - -void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow -void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) -#if !defined(DISABLE_JOIN) -bit_t LMIC_startJoining (void); -#endif - -void LMIC_shutdown (void); -void LMIC_init (void); -void LMIC_reset (void); -void LMIC_clrTxData (void); -void LMIC_setTxData (void); -int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); -void LMIC_sendAlive (void); - -#if !defined(DISABLE_BEACONS) -bit_t LMIC_enableTracking (u1_t tryBcnInfo); -void LMIC_disableTracking (void); -#endif - -#if !defined(DISABLE_PING) -void LMIC_stopPingable (void); -void LMIC_setPingable (u1_t intvExp); -#endif -#if !defined(DISABLE_JOIN) -void LMIC_tryRejoin (void); -#endif - -void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); -void LMIC_setLinkCheckMode (bit_t enabled); -void LMIC_setClockError(u2_t error); - -// Declare onEvent() function, to make sure any definition will have the -// C conventions, even when in a C++ file. -DECL_ON_LMIC_EVENT; - -// Special APIs - for development or testing -// !!!See implementation for caveats!!! - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _lmic_h_ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016 Matthijs Kooijman. + * Copyright (c) 2016-2018 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! @file +//! @brief LMIC API + +#ifndef _lmic_h_ +#define _lmic_h_ + +#include "oslmic.h" +#include "lorabase.h" + +#if LMIC_DEBUG_LEVEL > 0 || LMIC_X_DEBUG_LEVEL > 0 +# if defined(LMIC_DEBUG_INCLUDE) +# define LMIC_STRINGIFY_(x) #x +# define LMIC_STRINGIFY(x) LMIC_STRINGIFY_(x) +# include LMIC_STRINGIFY(LMIC_DEBUG_INCLUDE) +# endif +# ifdef LMIC_DEBUG_PRINTF_FN + extern void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_PRINTF_FN +#endif + +// if LMIC_DEBUG_PRINTF is now defined, just use it. This lets you do anything +// you like with a sufficiently crazy header file. +#if LMIC_DEBUG_LEVEL > 0 +# ifndef LMIC_DEBUG_PRINTF +// otherwise, check whether someone configured a print-function to be used, +// and use it if so. +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# ifndef LMIC_DEBUG_INCLUDE // If you use LMIC_DEBUG_INCLUDE, put the declaration in there + void LMIC_DEBUG_PRINTF_FN(const char *f, ...); +# endif // ndef LMIC_DEBUG_INCLUDE +# else // ndef LMIC_DEBUG_PRINTF_FN +// if there's no other info, just use printf. In a pure Arduino environment, +// that's what will happen. +# include +# define LMIC_DEBUG_PRINTF(f, ...) printf(f, ## __VA_ARGS__) +# endif // ndef LMIC_DEBUG_PRINTF_FN +# endif // ndef LMIC_DEBUG_PRINTF +# ifndef LMIC_DEBUG_FLUSH +# ifdef LMIC_DEBUG_FLUSH_FN +# define LMIC_DEBUG_FLUSH() LMIC_DEBUG_FLUSH_FN() +# else // ndef LMIC_DEBUG_FLUSH_FN +// if there's no other info, assume that flush is not needed. +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +# endif // ndef LMIC_DEBUG_FLUSH_FN +# endif // ndef LMIC_DEBUG_FLUSH +#else // LMIC_DEBUG_LEVEL == 0 +// If debug level is zero, printf and flush expand to nothing. +# define LMIC_DEBUG_PRINTF(f, ...) do { ; } while (0) +# define LMIC_DEBUG_FLUSH() do { ; } while (0) +#endif // LMIC_DEBUG_LEVEL == 0 + +// +// LMIC_X_DEBUG_LEVEL enables additional, special print functions for debugging +// RSSI features. This is used sparingly. +#if LMIC_X_DEBUG_LEVEL > 0 +# ifdef LMIC_DEBUG_PRINTF_FN +# define LMIC_X_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) +# else +# error "LMIC_DEBUG_PRINTF_FN must be defined for LMIC_X_DEBUG_LEVEL > 0." +# endif +#else +# define LMIC_X_DEBUG_PRINTF(f, ...) do {;} while(0) +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// LMIC version -- this is ths IBM LMIC version +#define LMIC_VERSION_MAJOR 1 +#define LMIC_VERSION_MINOR 6 +#define LMIC_VERSION_BUILD 1468577746 + +// Arduino LMIC version +#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, 2, 2, 0) + +#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ + (((v) >> 24u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_MINOR(v) \ + (((v) >> 16u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_PATCH(v) \ + (((v) >> 8u) & 0xFFu) + +#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \ + ((v) & 0xFFu) + +//! Only For Antenna Tuning Tests ! +//#define CFG_TxContinuousMode 1 + +enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length +enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames +enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests +enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this + +enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link + LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead + LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate + LINK_CHECK_OFF =-128 }; // link check disabled + +enum { TIME_RESYNC = 6*128 }; // secs +enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon +enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon +enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time +enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send + +#if CFG_LMIC_EU_like // EU868 spectrum ==================================================== + +enum { MAX_CHANNELS = 16 }; //!< Max supported channels +enum { MAX_BANDS = 4 }; + +enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels +//! \internal +struct band_t { + u2_t txcap; // duty cycle limitation: 1/txcap + s1_t txpow; // maximum TX power + u1_t lastchnl; // last used channel + ostime_t avail; // channel is blocked until this time +}; +TYPEDEF_xref2band_t; //!< \internal + +#elif CFG_LMIC_US_like // US915 spectrum ================================================= + +enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable + +#endif // ========================================================================== + +// Keep in sync with evdefs.hpp::drChange +enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD }; +enum { KEEP_TXPOW = -128 }; + + +#if !defined(DISABLE_PING) +//! \internal +struct rxsched_t { + u1_t dr; + u1_t intvExp; // 0..7 + u1_t slot; // runs from 0 to 128 + u1_t rxsyms; + ostime_t rxbase; + ostime_t rxtime; // start of next spot + u4_t freq; +}; +TYPEDEF_xref2rxsched_t; //!< \internal +#endif // !DISABLE_PING + + +#if !defined(DISABLE_BEACONS) +//! Parsing and tracking states of beacons. +enum { BCN_NONE = 0x00, //!< No beacon received + BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) + BCN_FULL = 0x02, //!< Full beacon decoded + BCN_NODRIFT = 0x04, //!< No drift value measured yet + BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet +//! Information about the last and previous beacons. +struct bcninfo_t { + ostime_t txtime; //!< Time when the beacon was sent + s1_t rssi; //!< Adjusted RSSI value of last received beacon + s1_t snr; //!< Scaled SNR value of last received beacon + u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. + u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) + // + u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) + s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) + s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) +}; +#endif // !DISABLE_BEACONS + +// purpose of receive window - lmic_t.rxState +enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 }; +// Netid values / lmic_t.netid +enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; +// MAC operation modes (lmic_t.opmode). +enum { OP_NONE = 0x0000, + OP_SCAN = 0x0001, // radio scan to find a beacon + OP_TRACK = 0x0002, // track my networks beacon (netid) + OP_JOINING = 0x0004, // device joining in progress (blocks other activities) + OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) + OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data + OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST + OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything + OP_TXRXPEND = 0x0080, // TX/RX transaction pending + OP_RNDTX = 0x0100, // prevent TX lining up after a beacon + OP_PINGINI = 0x0200, // pingable is initialized and scheduling active + OP_PINGABLE = 0x0400, // we're pingable + OP_NEXTCHNL = 0x0800, // find a new channel + OP_LINKDEAD = 0x1000, // link was reported as dead + OP_TESTMODE = 0x2000, // developer test mode +}; +// TX-RX transaction flags - report back to user +enum { TXRX_ACK = 0x80, // confirmed UP frame was acked + TXRX_NACK = 0x40, // confirmed UP frame was not acked + TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port + TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port + TXRX_DNW1 = 0x01, // received in 1st DN slot + TXRX_DNW2 = 0x02, // received in 2dn DN slot + TXRX_PING = 0x04 }; // received in a scheduled RX slot +// Event types for event callback +enum _ev_t { EV_SCAN_TIMEOUT=1, 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 }; +typedef enum _ev_t ev_t; + +enum { + // This value represents 100% error in LMIC.clockError + MAX_CLOCK_ERROR = 65536, +}; + +struct lmic_t { + // Radio settings TX/RX (also accessed by HAL) + ostime_t txend; + ostime_t rxtime; + + // LBT info + ostime_t lbt_ticks; // ticks to listen + s1_t lbt_dbmax; // max permissible dB on our channel (eg -80) + + u4_t freq; + s1_t rssi; + s1_t snr; // LMIC.snr is SNR times 4 + rps_t rps; + u1_t rxsyms; + u1_t dndr; + s1_t txpow; // dBm + + osjob_t osjob; + + // Channel scheduling +#if CFG_LMIC_EU_like + band_t bands[MAX_BANDS]; + u4_t channelFreq[MAX_CHANNELS]; + u2_t channelDrMap[MAX_CHANNELS]; + u2_t channelMap; +#elif CFG_LMIC_US_like + u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) + u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto + u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t activeChannels125khz; + u2_t activeChannels500khz; +#endif + u1_t txChnl; // channel for next TX + u1_t globalDutyRate; // max rate: 1/2^k + ostime_t globalDutyAvail; // time device can send again + + u4_t netid; // current network id (~0 - none) + u2_t opmode; + u1_t upRepeat; // configured up repeat + s1_t adrTxPow; // ADR adjusted TX power + u1_t datarate; // current data rate + u1_t errcr; // error coding rate (used for TX only) + u1_t rejoinCnt; // adjustment for rejoin datarate +#if !defined(DISABLE_BEACONS) + s2_t drift; // last measured drift + s2_t lastDriftDiff; + s2_t maxDriftDiff; +#endif + + u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX + // represents +/-100% error + + u1_t pendTxPort; + u1_t pendTxConf; // confirmed data + u1_t pendTxLen; // +0x80 = confirmed + u1_t pendTxData[MAX_LEN_PAYLOAD]; + + u2_t devNonce; // last generated nonce + u1_t nwkKey[16]; // network session key + u1_t artKey[16]; // application router session key + devaddr_t devaddr; + u4_t seqnoDn; // device level down stream seqno + u4_t seqnoUp; + + u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 + s1_t adrAckReq; // counter until we reset data rate (0=off) + 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 + + // rx1DrOffset is the offset from uplink to downlink datarate + u1_t rx1DrOffset; // captured from join. zero by default. + + // 2nd RX window (after up stream) + u1_t dn2Dr; + u4_t dn2Freq; +#if !defined(DISABLE_MCMD_DN2P_SET) + u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs +#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 +#if !defined(DISABLE_PING) + rxsched_t ping; // pingable setup +#endif + + // Public part of MAC state + u1_t txCnt; + u1_t txrxFlags; // transaction flags (TX-RX combo) + u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) + u1_t dataLen; // 0 no data or zero length data, >0 byte count of data + u1_t frame[MAX_LEN_FRAME]; + +#if !defined(DISABLE_BEACONS) + u1_t bcnChnl; + u1_t bcnRxsyms; // + ostime_t bcnRxtime; + bcninfo_t bcninfo; // Last received beacon info +#endif + + u1_t noRXIQinversion; +}; +//! \var struct lmic_t LMIC +//! The state of LMIC MAC layer is encapsulated in this variable. +DECLARE_LMIC; //!< \internal + +//! Construct a bit map of allowed datarates from drlo to drhi (both included). +#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) +bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); +bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); +void LMIC_disableChannel (u1_t channel); +void LMIC_enableSubBand(u1_t band); +void LMIC_enableChannel(u1_t channel); +void LMIC_disableSubBand(u1_t band); +void LMIC_selectSubBand(u1_t band); + +void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow +void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) +#if !defined(DISABLE_JOIN) +bit_t LMIC_startJoining (void); +#endif + +void LMIC_shutdown (void); +void LMIC_init (void); +void LMIC_reset (void); +void LMIC_clrTxData (void); +void LMIC_setTxData (void); +int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); +void LMIC_sendAlive (void); + +#if !defined(DISABLE_BEACONS) +bit_t LMIC_enableTracking (u1_t tryBcnInfo); +void LMIC_disableTracking (void); +#endif + +#if !defined(DISABLE_PING) +void LMIC_stopPingable (void); +void LMIC_setPingable (u1_t intvExp); +#endif +#if !defined(DISABLE_JOIN) +void LMIC_tryRejoin (void); +#endif + +void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); +void LMIC_setLinkCheckMode (bit_t enabled); +void LMIC_setClockError(u2_t error); + +u4_t LMIC_getSeqnoUp (void); +u4_t LMIC_setSeqnoUp (u4_t); +void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey); + +// Declare onEvent() function, to make sure any definition will have the +// C conventions, even when in a C++ file. +DECL_ON_LMIC_EVENT; + + + +// Special APIs - for development or testing +// !!!See implementation for caveats!!! + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lmic_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_as923.c b/lib/arduino-lmic-master/src/lmic/lmic_as923.c new file mode 100644 index 00000000..f0e0250b --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_as923.c @@ -0,0 +1,364 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_as923) +// ================================================================================ +// +// BEG: AS923 related stuff +// + +// see table in section 2.7.3 +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 0. +static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { + 59+5, // [0] + 59+5, // [1] + 59+5, // [2] + 123+5, // [3] + 230+5, // [4] + 230+5, // [5] + 230+5, // [6] + 230+5 // [7] +}; + +// see table in 2.7.6 -- this assumes UplinkDwellTime = 1. +static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = { + 0, // [0] + 0, // [1] + 19+5, // [2] + 61+5, // [3] + 133+5, // [4] + 250+5, // [5] + 250+5, // [6] + 250+5 // [7] +}; + +static uint8_t +LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) { + return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0; +} + +static uint8_t +LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) { + return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0; +} + +uint8_t LMICas923_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens_dwell0)) { + if (LMICas923_getUplinkDwellBit(LMIC.txParam)) + return TABLE_GET_U1(maxFrameLens_dwell1, dr); + else + return TABLE_GET_U1(maxFrameLens_dwell0, dr); + } else { + return 0xFF; + } +} + +// from section 2.7.3. These are all referenced to the max EIRP of the +// device, which is set by TxParams +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 0, // [0]: MaxEIRP + -2, // [1]: MaxEIRP - 2dB + -6, // [2]: MaxEIRP - 4dB + -8, // [3]: MaxEIRP - 6dB + -4, // [4]: MaxEIRP - 8dB + -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 +static CONST_TABLE(s1_t, TXMAXEIRP)[16] = { + 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 +}; + +static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { + if (mcmd_txparam == 0xFF) + return AS923_TX_EIRP_MAX_DBM; + else + return TABLE_GET_S1( + TXMAXEIRP, + (mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >> + MCMD_TxParam_MaxEIRP_SHIFT + ); +} + +// translate from an encoded power to an actual power using +// the maxeirp setting. +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 adj; +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + 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 << 1), // DR_SF7B: 250K bps, DR_SF7 + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICas923_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// Default duty cycle is 1%. +enum { NUM_DEFAULT_CHANNELS = 2 }; +static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = { + // Default operational frequencies + AS923_F1 | BAND_CENTI, + AS923_F2 | BAND_CENTI, +}; + +// as923 ignores join, becuase the channel setup is the same either way. +void LMICas923_initDefaultChannels(bit_t join) { + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; futxpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq = (freq&~3) | BAND_CENTI; + } else { + if (band != BAND_CENTI) return 0; + freq = (freq&~3) | band; + } + LMIC.channelFreq[chidx] = freq; + LMIC.channelDrMap[chidx] = + drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) + : drmap; + LMIC.channelMap |= 1 << chidx; // enabled right away + return 1; +} + + + +u4_t LMICas923_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AS923_FREQ_MIN || freq > AS923_FREQ_MAX) + freq = 0; + return freq; +} + +// when can we join next? +ostime_t LMICas923_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_CENTI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_CENTI].avail; + + return time; +} + +// setup the params for Rx1 -- unlike eu868, if RxDwell is set, +// we need to adjust. +void LMICas923_setRx1Params(void) { + int minDr; + int const txdr = LMIC.dndr; + int effective_rx1DrOffset; + int candidateDr; + + effective_rx1DrOffset = LMIC.rx1DrOffset; + // per section 2.7.7 of regional, lines 1101:1103: + switch (effective_rx1DrOffset) { + case 6: effective_rx1DrOffset = -1; break; + 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); +} + + +// return the next time, but also do channel hopping here +// identical to the EU868 version; but note that we only have BAND_CENTI +// at work. +ostime_t LMICas923_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + +#if !defined(DISABLE_BEACONS) +void LMICas923_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICas923_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// txDone handling for FSK. +void +LMICas923_txDoneFSK(ostime_t delay, osjobcb_t func) { + LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160); + LMIC.rxsyms = RXLEN_FSK; + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); +} + +void +LMICas923_initJoinLoop(void) { + LMIC.txParam = 0xFF; + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ AS923_TX_EIRP_MAX_DBM); +} + +void +LMICas923_updateTx(ostime_t txbeg) { + u4_t freq = LMIC.channelFreq[LMIC.txChnl]; + // Update global/band specific duty cycle stats + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + // Update channel/global duty cycle stats + xref2band_t band = &LMIC.bands[freq & 0x3]; + LMIC.freq = freq & ~(u4_t)3; + LMIC.txpow = LMICas923_getMaxEIRP(LMIC.txParam); + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + + +// +// END: AS923 related stuff +// +// ================================================================================ +#endif diff --git a/lib/arduino-lmic-master/src/lmic/lmic_au921.c b/lib/arduino-lmic-master/src/lmic/lmic_au921.c new file mode 100644 index 00000000..5783e83e --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_au921.c @@ -0,0 +1,216 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_au921) +// ================================================================================ +// +// BEG: AU921 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [4] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [5] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + 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 +}; + +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) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0xFF; +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + 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 << 1), // DR_SF8C + us2osticksRound(128 << 0), // ------ + us2osticksRound(128 << 5), // DR_SF12CR + 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, +// becuase the times don't match between the upper half and lower half +// of the table. +ostime_t LMICau921_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + + +u4_t LMICau921_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < AU921_FREQ_MIN || freq > AU921_FREQ_MAX) + freq = 0; + return freq; +} + +// au921: no support for xchannels. +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + return 0; // all channels are hardwired. +} + +void LMIC_disableChannel(u1_t channel) { + if (channel < 72) { + if (ENABLED_CHANNEL(channel)) { + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } +} + +void LMIC_enableChannel(u1_t channel) { + if (channel < 72) { + if (!ENABLED_CHANNEL(channel)) { + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } +} + +void LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + LMIC_enableChannel(64 + band); +} +void LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + LMIC_disableChannel(64 + band); +} +void LMIC_selectSubBand(u1_t band) { + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + LMIC_enableSubBand(b); + else + LMIC_disableSubBand(b); + } +} + +void LMICau921_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + LMIC.txpow = AU921_TX_EIRP_MAX_DBM; + if (chnl < 64) { + LMIC.freq = AU921_125kHz_UPFBASE + chnl*AU921_125kHz_UPFSTEP; + } else { + ASSERT(chnl < 64 + 8); + LMIC.freq = AU921_500kHz_UPFBASE + (chnl - 64)*AU921_500kHz_UPFSTEP; + } + + // Update global duty cycle stats + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); + } +} + +#if !defined(DISABLE_BEACONS) +void LMICau921_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = AU921_500kHz_DNFBASE + LMIC.bcnChnl * AU921_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// set the Rx1 dndr, rps. +void LMICau921_setRx1Params(void) { + u1_t const txdr = LMIC.dndr; + u1_t candidateDr; + LMIC.freq = AU921_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU921_500kHz_DNFSTEP; + if ( /* TX datarate */txdr < AU921_DR_SF8C) + candidateDr = txdr + 8 - LMIC.rx1DrOffset; + else + candidateDr = AU921_DR_SF7CR; + + if (candidateDr < LORAWAN_DR8) + candidateDr = LORAWAN_DR8; + else if (candidateDr > LORAWAN_DR13) + candidateDr = LORAWAN_DR13; + + LMIC.dndr = candidateDr; + LMIC.rps = dndr2rps(LMIC.dndr); +} + + +// +// END: AU921 related stuff +// +// ================================================================================ +#endif diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan.h new file mode 100644 index 00000000..0c3c5036 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan.h @@ -0,0 +1,175 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_bandplan_h_ +# define _lmic_bandplan_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +#if defined(CFG_eu868) +# include "lmic_bandplan_eu868.h" +#elif defined(CFG_us915) +# include "lmic_bandplan_us915.h" +#elif defined(CFG_au921) +# include "lmic_bandplan_au921.h" +#elif defined(CFG_as923) +# include "lmic_bandplan_as923.h" +#elif defined(CFG_in866) +# include "lmic_bandplan_in866.h" +#else +# error "CFG_... not properly set for bandplan" +#endif + +// check post-conditions +#ifndef DNW2_SAFETY_ZONE +# error "DNW2_SAFETY_ZONE not defined by bandplan" +#endif + +#ifndef maxFrameLen +# error "maxFrameLen() not defined by bandplan" +#endif + +#ifndef pow2dBm +# error "pow2dBm() not defined by bandplan" +#endif + +#ifndef dr2hsym +# error "dr2hsym() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isValidBeacon1) && !defined(DISABLE_BEACONS) +# error "LMICbandplan_isValidBeacon1 not defined by bandplan" +#endif + +#if !defined(LMICbandplan_isFSK) +# error "LMICbandplan_isFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_txDoneFSK) +# error "LMICbandplan_txDoneFSK() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_joinAcceptChannelClear) +# error "LMICbandplan_joinAcceptChannelClear() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_getInitialDrJoin) +# error "LMICbandplan_getInitialDrJoin() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_hasJoinCFlist) +# error "LMICbandplan_hasJoinCFlist() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_advanceBeaconChannel) +# error "LMICbandplan_advanceBeaconChannel() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_resetDefaultChannels) +# error "LMICbandplan_resetDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setSessionInitDefaultChannels) +# error "LMICbandplan_setSessionInitDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setBcnRxParams) +# error "LMICbandplan_setBcnRxParams() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_mapChannels) +# error "LMICbandplan_mapChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_convFreq) +# error "LMICbandplan_convFreq() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_setRx1Params) +# error "LMICbandplan_setRx1Params() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initJoinLoop) +# error "LMICbandplan_initJoinLoop() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextTx) +# error "LMICbandplan_nextTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_updateTx) +# error "LMICbandplan_updateTx() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinState) +# error "LMICbandplan_nextJoinState() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_initDefaultChannels) +# error "LMICbandplan_initDefaultChannels() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_nextJoinTime) +# error "LMICbandplan_nextJoinTime() not defined by bandplan" +#endif + +#if !defined(LMICbandplan_init) +# error "LMICbandplan_init() not defined by bandplan" +#endif +// +// Things common to lmic.c code +// +#if !defined(MINRX_SYMS) +#define MINRX_SYMS 5 +#endif // !defined(MINRX_SYMS) +#define PAMBL_SYMS 8 +#define PAMBL_FSK 5 +#define PRERX_FSK 1 +#define RXLEN_FSK (1+5+2) + +#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec) +#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms) +#define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms) +#define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1) +#define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2) +#define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2) +#define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms) +#define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms) +#define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms) +#define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN) + +// Special APIs - for development or testing +#define isTESTMODE() 0 + +// internal APIs +ostime_t LMICcore_rndDelay(u1_t secSpan); +void LMICcore_setDrJoin(u1_t reason, u1_t dr); + +#endif // _lmic_bandplan_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h new file mode 100644 index 00000000..50017f29 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_as923.h @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_as923_h_ +# define _lmic_as923_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +uint8_t LMICas923_maxFrameLen(uint8_t dr); +#define maxFrameLen(dr) LMICas923_maxFrameLen(dr) + +int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICas923_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICas923_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICas923_dr2hsym(dr) + +static inline int +LMICas923_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICas923_isValidBeacon1(pFrame) + +// override default for LMICbandplan_resetDefaultChannels +void +LMICas923_resetDefaultChannels(void); + +#undef LMICbandplan_resetDefaultChannels +#define LMICbandplan_resetDefaultChannels() \ + LMICas923_resetDefaultChannels() + +// override default for LMICbandplan_init +void LMICas923_init(void); + +#undef LMICbandplan_init +#define LMICbandplan_init() \ + LMICas923_init() + + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == AS923_DR_FSK) + +// txDone handling for FSK. +void +LMICas923_txDoneFSK(ostime_t delay, osjobcb_t func); + +#define LMICbandplan_txDoneFsk(delay, func) LMICas923_txDoneFSK(delay, func) + +#define LMICbandplan_getInitialDrJoin() (AS923_DR_SF10) + +void LMICas923_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICas923_setBcnRxParams() + +u4_t LMICas923_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICas923_convFreq(ptr) + +void LMICas923_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICas923_initJoinLoop() + +// for as923, depending on dwell, we may need to do something else +#undef LMICbandplan_setRx1Params +void LMICas923_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICas923_setRx1Params() + +ostime_t LMICas923_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICas923_nextTx(now) + +ostime_t LMICas923_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICas923_nextJoinState() + +void LMICas923_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICas923_initDefaultChannels(join) + +// override default for LMICbandplan_updateTX +#undef LMICbandplan_updateTx +void LMICas923_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICas923_updateTx(txbeg) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICas923_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICas923_nextJoinTime(now) + +#endif // _lmic_as923_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan_au921.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_au921.h new file mode 100644 index 00000000..f1731872 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_au921.h @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_au921_h_ +# define _lmic_au921_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (AU921_DR_SF8C) + + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +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))) + +ostime_t LMICau921_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICau921_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7) + +void LMICau921_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICau921_setBcnRxParams() + +u4_t LMICau921_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICau921_convFreq(ptr) + +void LMICau921_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICau921_setRx1Params() + +void LMICau921_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICau921_updateTx(txbeg) + +#endif // _lmic_au921_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h new file mode 100644 index 00000000..d1e3adcc --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_eu868.h @@ -0,0 +1,92 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu868_h_ +# define _lmic_eu868_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +uint8_t LMICeu868_maxFrameLen(uint8_t dr); +#define maxFrameLen(dr) LMICeu868_maxFrameLen(dr) + +int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICeu868_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICeu868_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICeu868_dr2hsym(dr) + + +// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional +// spec. https://github.com/mcci-catena/arduino-lmic/issues/18 +static inline int +LMICeu868_isValidBeacon1(const uint8_t *d) { + return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICeu868_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == EU868_DR_FSK) + +// txDone handling for FSK. +void +LMICeu868_txDoneFSK(ostime_t delay, osjobcb_t func); + +#define LMICbandplan_txDoneFsk(delay, func) LMICeu868_txDoneFSK(delay, func) + +#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7) + +void LMICeu868_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICeu868_setBcnRxParams() + +u4_t LMICeu868_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICeu868_convFreq(ptr) + +void LMICeu868_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICeu868_initJoinLoop() + +ostime_t LMICeu868_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICeu868_nextTx(now) + +ostime_t LMICeu868_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICeu868_nextJoinState() + +void LMICeu868_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICeu868_initDefaultChannels(join) + +#undef LMICbandplan_nextJoinTime +ostime_t LMICeu868_nextJoinTime(ostime_t now); +#define LMICbandplan_nextJoinTime(now) LMICeu868_nextJoinTime(now) + +#endif // _lmic_eu868_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h new file mode 100644 index 00000000..ad1b4e4f --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_in866.h @@ -0,0 +1,85 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_in866_h_ +# define _lmic_in866_h_ + +#ifndef _lmic_eu_like_h_ +# include "lmic_eu_like.h" +#endif + +uint8_t LMICin866_maxFrameLen(uint8_t dr); +#define maxFrameLen(dr) LMICin866_maxFrameLen(dr) + +int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICin866_pow2dBm(mcmd_ladr_p1) + +// Times for half symbol per DR +// Per DR table to minimize rounding errors +ostime_t LMICin866_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICin866_dr2hsym(dr) + +static inline int +LMICin866_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#undef LMICbandplan_isValidBeacon1 +#define LMICbandplan_isValidBeacon1(pFrame) LMICin866_isValidBeacon1(pFrame) + +// override default for LMICbandplan_isFSK() +#undef LMICbandplan_isFSK +#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == IN866_DR_FSK) + +// txDone handling for FSK. +void +LMICin866_txDoneFSK(ostime_t delay, osjobcb_t func); + +#define LMICbandplan_txDoneFsk(delay, func) LMICin866_txDoneFSK(delay, func) + +#define LMICbandplan_getInitialDrJoin() (IN866_DR_SF7) + +void LMICin866_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICin866_setBcnRxParams() + +u4_t LMICin866_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICin866_convFreq(ptr) + +void LMICin866_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICin866_initJoinLoop() + +ostime_t LMICin866_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICin866_nextTx(now) + +ostime_t LMICin866_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICin866_nextJoinState() + +void LMICin866_initDefaultChannels(bit_t join); +#define LMICbandplan_initDefaultChannels(join) LMICin866_initDefaultChannels(join) + +#endif // _lmic_in866_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h new file mode 100644 index 00000000..28ae2c8a --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_bandplan_us915.h @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_us915_h_ +# define _lmic_us915_h_ + +// preconditions for lmic_us_like.h +#define LMICuslike_getFirst500kHzDR() (US915_DR_SF8C) + +#ifndef _lmic_us_like_h_ +# include "lmic_us_like.h" +#endif + +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))) + +ostime_t LMICus915_dr2hsym(uint8_t dr); +#define dr2hsym(dr) LMICus915_dr2hsym(dr) + + +#define LMICbandplan_getInitialDrJoin() (US915_DR_SF7) + +void LMICus915_setBcnRxParams(void); +#define LMICbandplan_setBcnRxParams() LMICus915_setBcnRxParams() + +u4_t LMICus915_convFreq(xref2cu1_t ptr); +#define LMICbandplan_convFreq(ptr) LMICus915_convFreq(ptr) + +void LMICus915_setRx1Params(void); +#define LMICbandplan_setRx1Params() LMICus915_setRx1Params() + +void LMICus915_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(txbeg) LMICus915_updateTx(txbeg) + +#endif // _lmic_us915_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_config_preconditions.h b/lib/arduino-lmic-master/src/lmic/lmic_config_preconditions.h new file mode 100644 index 00000000..4d31cb36 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_config_preconditions.h @@ -0,0 +1,181 @@ +/* lmic_config_preconditions.h Fri May 19 2017 23:58:34 tmm */ + +/* + +Module: lmic_config_preconditions.h + +Function: + Preconditions for LMIC configuration. + +Version: + V2.0.0 Sun Aug 06 2017 17:40:44 tmm Edit level 1 + +Copyright notice: + This file copyright (C) 2017 by + + MCCI Corporation + 3520 Krums Corners Road + Ithaca, NY 14850 + + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Author: + Terry Moore, MCCI Corporation July 2017 + +Revision history: + 2.0.0 Sun Aug 06 2017 17:40:44 tmm + Module created. + +*/ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# define _LMIC_CONFIG_PRECONDITIONS_H_ + +// We need to be able to compile with different options without editing source. +// When building with a more advanced environment, set the following variable: +// ARDUINO_LMIC_PROJECT_CONFIG_H=my_project_config.h +// +// otherwise the lmic_project_config.h from the ../../project_config directory will be used. +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H +# define ARDUINO_LMIC_PROJECT_CONFIG_H ../../project_config/lmic_project_config.h +#endif + +#define CFG_TEXT_1(x) CFG_TEXT_2(x) +#define CFG_TEXT_2(x) #x + +// constants for comparison +#define LMIC_REGION_eu868 1 +#define LMIC_REGION_us915 2 +#define LMIC_REGION_cn783 3 +#define LMIC_REGION_eu433 4 +#define LMIC_REGION_au921 5 +#define LMIC_REGION_cn490 6 +#define LMIC_REGION_as923 7 +#define LMIC_REGION_kr921 8 +#define LMIC_REGION_in866 9 + +// Some regions have country-specific overrides. For generality, we specify +// country codes using the LMIC_COUNTY_CODE_C() macro These values are chosen +// from the 2-letter domain suffixes standardized by ISO-3166-1 alpha2 (see +// https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). They are therefore +// 16-bit constants. By convention, we use UPPER-CASE letters, thus +// LMIC_COUNTRY_CODE('J', 'P'), not ('j', 'p'). +#define LMIC_COUNTRY_CODE_C(c1, c2) ((c1) * 256 + (c2)) + +// this special code means "no country code defined" +#define LMIC_COUNTRY_CODE_NONE 0 + +// specific countries. Only the ones that are needed by the code are defined. +#define LMIC_COUNTRY_CODE_JP LMIC_COUNTRY_CODE_C('J', 'P') + +// include the file that the user is really supposed to edit. But for really strange +// ports, this can be suppressed +#ifndef ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS +# include CFG_TEXT_1(ARDUINO_LMIC_PROJECT_CONFIG_H) +#endif /* ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS */ + +// a mask of the supported regions +// TODO(tmm@mcci.com) consider moving this block to a central file as it's not +// user-editable. +#define LMIC_REGIONS_SUPPORTED ( \ + (1 << LMIC_REGION_eu868) | \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au921) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + /* (1 << LMIC_REGION_kr921) | */ \ + (1 << LMIC_REGION_in866) | \ + 0) + +// +// Our input is a -D of one of CFG_eu868, CFG_us915, CFG_as923, CFG_au915, CFG_in866 +// More will be added in the the future. So at this point we create CFG_region with +// following values. These are in order of the sections in the manual. Not all of the +// below are supported yet. +// +# define CFG_LMIC_REGION_MASK \ + ((defined(CFG_eu868) << LMIC_REGION_eu868) | \ + (defined(CFG_us915) << LMIC_REGION_us915) | \ + (defined(CFG_cn783) << LMIC_REGION_cn783) | \ + (defined(CFG_eu433) << LMIC_REGION_eu433) | \ + (defined(CFG_au921) << LMIC_REGION_au921) | \ + (defined(CFG_cn490) << LMIC_REGION_cn490) | \ + (defined(CFG_as923) << LMIC_REGION_as923) | \ + (defined(CFG_kr921) << LMIC_REGION_kr921) | \ + (defined(CFG_in866) << LMIC_REGION_in866) | \ + 0) + +// the selected region. +#if defined(CFG_eu868) +# define CFG_region LMIC_REGION_eu868 +#elif defined(CFG_us915) +# define CFG_region LMIC_REGION_us915 +#elif defined(CFG_cn783) +# define CFG_region LMIC_REGION_cn783 +#elif defined(CFG_eu433) +# define CFG_region LMIC_REGION_eu433 +#elif defined(CFG_au921) +# define CFG_region LMIC_REGION_au921 +#elif defined(CFG_cn490) +# define CFG_region LMIC_REGION_cn490 +#elif defined(CFG_as923) +# define CFG_region LMIC_REGION_as923 +#elif defined(CFG_kr921) +# define CFG_region LMIC_REGION_kr921 +#elif defined(CFG_in866) +# define CFG_region LMIC_REGION_in866 +#else +# define CFG_region 0 +#endif + +// finally the mask of` US-like and EU-like regions +#define CFG_LMIC_EU_like_MASK ( \ + (1 << LMIC_REGION_eu868) | \ + /* (1 << LMIC_REGION_us915) | */ \ + (1 << LMIC_REGION_cn783) | \ + (1 << LMIC_REGION_eu433) | \ + /* (1 << LMIC_REGION_au921) | */ \ + /* (1 << LMIC_REGION_cn490) | */ \ + (1 << LMIC_REGION_as923) | \ + (1 << LMIC_REGION_kr921) | \ + (1 << LMIC_REGION_in866) | \ + 0) + +#define CFG_LMIC_US_like_MASK ( \ + /* (1 << LMIC_REGION_eu868) | */ \ + (1 << LMIC_REGION_us915) | \ + /* (1 << LMIC_REGION_cn783) | */ \ + /* (1 << LMIC_REGION_eu433) | */ \ + (1 << LMIC_REGION_au921) | \ + /* (1 << LMIC_REGION_cn490) | */ \ + /* (1 << LMIC_REGION_as923) | */ \ + /* (1 << LMIC_REGION_kr921) | */ \ + /* (1 << LMIC_REGION_in866) | */ \ + 0) + +#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK)) +#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK)) + + + +#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_eu868.c b/lib/arduino-lmic-master/src/lmic/lmic_eu868.c new file mode 100644 index 00000000..3c873345 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_eu868.c @@ -0,0 +1,233 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_eu868) +// ================================================================================ +// +// BEG: EU868 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), + (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0), + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), + ILLEGAL_RPS +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { 64,64,64,123 }; + +uint8_t LMICeu868_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0xFF; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0 +}; + +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); +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + 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 << 1), // DR_SF7B + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICeu868_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +enum { NUM_DEFAULT_CHANNELS = 3 }; +static CONST_TABLE(u4_t, iniChannelFreq)[6] = { + // Join frequencies and duty cycle limit (0.1%) + EU868_F1 | BAND_MILLI, EU868_F2 | BAND_MILLI, EU868_F3 | BAND_MILLI, + // Default operational frequencies and duty cycle limit (1%) + EU868_F1 | BAND_CENTI, EU868_F2 | BAND_CENTI, EU868_F3 | BAND_CENTI, +}; + +void LMICeu868_initDefaultChannels(bit_t join) { + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + u1_t su = join ? 0 : NUM_DEFAULT_CHANNELS; + for (u1_t fu = 0; fu BAND_AUX) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + if (freq >= 869400000 && freq <= 869650000) + freq |= BAND_DECI; // 10% 27dBm + else if ((freq >= 868000000 && freq <= 868600000) || + (freq >= 869700000 && freq <= 870000000)) + freq |= BAND_CENTI; // 1% 14dBm + else + freq |= BAND_MILLI; // 0.1% 14dBm + } + else { + if (band > BAND_AUX) return 0; + 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 + return 1; +} + + + +u4_t LMICeu868_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX) + freq = 0; + return freq; +} + +ostime_t LMICeu868_nextJoinTime(ostime_t time) { + // is the avail time in the future? + if ((s4_t) (time - LMIC.bands[BAND_MILLI].avail) < 0) + // yes: then wait until then. + time = LMIC.bands[BAND_MILLI].avail; + + return time; +} + +ostime_t LMICeu868_nextTx(ostime_t now) { + u1_t bmap = 0xF; + do { + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + for (u1_t bi = 0; bi<4; bi++) { + if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[band = bi].avail; + } + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return mintime; + } + } + if ((bmap &= ~(1 << band)) == 0) { + // No feasible channel found! + return mintime; + } + } while (1); +} + + +#if !defined(DISABLE_BEACONS) +void LMICeu868_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICeu868_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// txDone handling for FSK. +void +LMICeu868_txDoneFSK(ostime_t delay, osjobcb_t func) { + LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160); + LMIC.rxsyms = RXLEN_FSK; + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); +} + +void +LMICeu868_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ EU868_TX_EIRP_MAX_DBM); +} + +// +// END: EU868 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/lmic_eu_like.c b/lib/arduino-lmic-master/src/lmic/lmic_eu_like.c new file mode 100644 index 00000000..75349616 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_eu_like.c @@ -0,0 +1,159 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_EU_like + +void LMIC_enableSubBand(u1_t band) { +} + +void LMIC_disableSubBand(u1_t band) { +} + +void LMIC_disableChannel(u1_t channel) { + LMIC.channelFreq[channel] = 0; + LMIC.channelDrMap[channel] = 0; + LMIC.channelMap &= ~(1 << channel); +} + +// this is a no-op provided for compatibilty +void LMIC_enableChannel(u1_t channel) { +} + +u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) { + // Bad page, disable all channel, enable non-existent + if (chpage != 0 || chmap == 0 || (chmap & ~LMIC.channelMap) != 0) + return 0; // illegal input + for (u1_t chnl = 0; chnltxpow; + band->avail = txbeg + airtime * band->txcap; + if (LMIC.globalDutyRate != 0) + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); +} + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { + u1_t failed = 0; + + // Try each default channel with same DR + // If all fail try next lower datarate + if (++LMIC.txChnl == /* NUM_DEFAULT_CHANNELS */ nDefaultChannels) + LMIC.txChnl = 0; + if ((++LMIC.txCnt % nDefaultChannels) == 0) { + // Lower DR every nth try (having all default channels with same DR) + // + // TODO(tmm@mcci.com) add new DR_REGIN_JOIN_MIN instead of LORAWAN_DR0; + // then we can eliminate the LMIC_REGION_as923 below because we'll set + // the failed flag here. This will cause the outer caller to take the + // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() + // + if (LMIC.datarate == LORAWAN_DR0) + failed = 1; // we have tried all DR - signal EV_JOIN_FAILED + else + { +// TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. +#if CFG_region != LMIC_REGION_as923 + LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); +#else + // in the join of AS923 v1.1 or older, only DR2 is used. + // no need to change the DR. + LMIC.datarate = AS923_DR_SF10; +#endif + } + } + // Clear NEXTCHNL because join state engine controls channel hopping + LMIC.opmode &= ~OP_NEXTCHNL; + // Move txend to randomize synchronized concurrent joins. + // Duty cycle is based on txend. + ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = time + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF12:255, SF11:127, .., SF7:8secs + // + : DNW2_SAFETY_ZONE + LMICcore_rndDelay(255 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif // !DISABLE_JOIN + +#endif // CFG_LMIC_EU_like diff --git a/lib/arduino-lmic-master/src/lmic/lmic_eu_like.h b/lib/arduino-lmic-master/src/lmic/lmic_eu_like.h new file mode 100644 index 00000000..f147790d --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_eu_like.h @@ -0,0 +1,98 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_eu_like_h_ +# define _lmic_eu_like_h_ + +#ifndef _lmic_h_ +# include "lmic.h" +#endif + +// make sure we want US-like code +#if !CFG_LMIC_EU_like +# error "lmic not configured for EU-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(3000) + +// provide a default for LMICbandplan_isValidBeacon1() +static inline int +LMICeulike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICeulike_isValidBeacon1(pFrame) + + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneDoFSK() +#define LMICbandplan_txDoneFSK(delay, func) do { } while (0) + +#define LMICbandplan_joinAcceptChannelClear() LMICbandplan_initDefaultChannels(/* normal, not join */ 0) + +enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 }; + +// there's a CFList on joins for EU-like plans +#define LMICbandplan_hasJoinCFlist() (1) + +#define LMICbandplan_advanceBeaconChannel() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_resetDefaultChannels() \ + do { /* nothing */ } while (0) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { LMICbandplan_initDefaultChannels(/* normal, not join */ 0); } while (0) + +u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(c, m) LMICeulike_mapChannels(c, m) + +void LMICeulike_initJoinLoop(u1_t nDefaultChannels, s1_t adrTxPow); + +#define LMICbandplan_setRx1Params() \ + do { /*LMIC.freq/rps remain unchanged*/ } while (0) + +void LMICeulike_updateTx(ostime_t txbeg); +#define LMICbandplan_updateTx(t) LMICeulike_updateTx(t) + +ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels); + +static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICeulike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +#endif // _lmic_eu_like_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_in866.c b/lib/arduino-lmic-master/src/lmic/lmic_in866.c new file mode 100644 index 00000000..70011f39 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_in866.c @@ -0,0 +1,205 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_in866) +// ================================================================================ +// +// BEG: IN866 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, + (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0] + (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1] + (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2] + (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3] + (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4] + (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5] + ILLEGAL_RPS, // [6] + (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0), // [7] + ILLEGAL_RPS +}; + +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 + return 0xFF; +} + +static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { + 20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0 +}; + +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); +} + +// only used in this module, but used by variant macro dr2hsym(). +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + 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 << 1), // -- + us2osticksRound(80) // FSK -- not used (time for 1/2 byte) +}; + +ostime_t LMICin866_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, dr); +} + + +// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets +// us reuse code. +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_F3 | BAND_MILLI, +}; + +// india ignores join, becuase the channel setup is the same either way. +void LMICin866_initDefaultChannels(bit_t join) { + os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq)); + os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap)); + os_clearMem(&LMIC.bands, sizeof(LMIC.bands)); + + LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1; + for (u1_t fu = 0; fu BAND_MILLI) return 0; + //band_t* b = &LMIC.bands[bandidx]; + xref2band_t b = &LMIC.bands[bandidx]; + b->txpow = txpow; + b->txcap = txcap; + b->avail = os_getTime(); + b->lastchnl = os_getRndU1() % MAX_CHANNELS; + return 1; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx >= MAX_CHANNELS) + return 0; + if (band == -1) { + freq |= 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 + return 1; +} + + + +u4_t LMICin866_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < IN866_FREQ_MIN || freq > IN866_FREQ_MAX) + freq = 0; + return freq; +} + +// return the next time, but also do channel hopping here +// since there's no duty cycle limitation, and no dwell limitation, +// we simply loop through the channels sequentially. +ostime_t LMICin866_nextTx(ostime_t now) { + const u1_t band = BAND_MILLI; + + for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { + // Find next channel in given band + u1_t chnl = LMIC.bands[band].lastchnl; + for (u1_t ci = 0; ci= MAX_CHANNELS) + chnl -= MAX_CHANNELS; + if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled + (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && + band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band + LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; + return now; + } + } + } + + // no enabled channel found! just use the last channel. + return now; +} + +#if !defined(DISABLE_BEACONS) +void LMICin866_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +#if !defined(DISABLE_JOIN) +ostime_t LMICin866_nextJoinState(void) { + return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS); +} +#endif // !DISABLE_JOIN + +// txDone handling for FSK. +void +LMICin866_txDoneFSK(ostime_t delay, osjobcb_t func) { + LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160); + LMIC.rxsyms = RXLEN_FSK; + os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func); +} + +void +LMICin866_initJoinLoop(void) { + LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ IN866_TX_EIRP_MAX_DBM); +} + +// +// END: IN866 related stuff +// +// ================================================================================ +#endif \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/lmic_us915.c b/lib/arduino-lmic-master/src/lmic/lmic_us915.c new file mode 100644 index 00000000..c863f187 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_us915.c @@ -0,0 +1,209 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if defined(CFG_us915) +// ================================================================================ +// +// BEG: US915 related stuff +// + +CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { + ILLEGAL_RPS, // [-1] + MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [0] + MAKERPS(SF9 , BW125, CR_4_5, 0, 0), // [1] + MAKERPS(SF8 , BW125, CR_4_5, 0, 0), // [2] + MAKERPS(SF7 , BW125, CR_4_5, 0, 0), // [3] + MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [4] + ILLEGAL_RPS , // [5] + ILLEGAL_RPS , // [6] + ILLEGAL_RPS , // [7] + MAKERPS(SF12, BW500, CR_4_5, 0, 0), // [8] + MAKERPS(SF11, BW500, CR_4_5, 0, 0), // [9] + MAKERPS(SF10, BW500, CR_4_5, 0, 0), // [10] + 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 // [14] +}; + +static CONST_TABLE(u1_t, maxFrameLens)[] = { 24,66,142,255,255,255,255,255, 66,142 }; + +uint8_t LMICus915_maxFrameLen(uint8_t dr) { + if (dr < LENOF_TABLE(maxFrameLens)) + return TABLE_GET_U1(maxFrameLens, dr); + else + return 0xFF; +} + +static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { + us2osticksRound(128 << 5), // DR_SF10 DR_SF12CR + us2osticksRound(128 << 4), // DR_SF9 DR_SF11CR + us2osticksRound(128 << 3), // DR_SF8 DR_SF10CR + us2osticksRound(128 << 2), // DR_SF7 DR_SF9CR + us2osticksRound(128 << 1), // DR_SF8C DR_SF8CR + us2osticksRound(128 << 0) // ------ DR_SF7CR +}; + +ostime_t LMICus915_dr2hsym(uint8_t dr) { + return TABLE_GET_OSTIME(DR2HSYM_osticks, (dr) & 7); // map DR_SFnCR -> 0-6 +} + + + +u4_t LMICus915_convFreq(xref2cu1_t ptr) { + u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100; + if (freq < US915_FREQ_MIN || freq > US915_FREQ_MAX) + freq = 0; + return freq; +} + +bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS) + return 0; // channels 0..71 are hardwired + LMIC.xchFreq[chidx - 72] = freq; + // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const + LMIC.xchDrMap[chidx - 72] = drmap == 0 ? DR_RANGE_MAP(US915_DR_SF10, US915_DR_SF8C) : drmap; + LMIC.channelMap[chidx >> 4] |= (1 << (chidx & 0xF)); + return 1; +} + +void LMIC_disableChannel(u1_t channel) { + if (channel < 72 + MAX_XCHANNELS) { + if (ENABLED_CHANNEL(channel)) { + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz--; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz--; + } + LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF)); + } +} + +void LMIC_enableChannel(u1_t channel) { + if (channel < 72 + MAX_XCHANNELS) { + if (!ENABLED_CHANNEL(channel)) { + if (IS_CHANNEL_125khz(channel)) + LMIC.activeChannels125khz++; + else if (IS_CHANNEL_500khz(channel)) + LMIC.activeChannels500khz++; + } + LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF)); + } +} + +void LMIC_enableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + + // enable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + LMIC_enableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Enable it, too. + LMIC_enableChannel(64 + band); +} +void LMIC_disableSubBand(u1_t band) { + ASSERT(band < 8); + u1_t start = band * 8; + u1_t end = start + 8; + + // disable all eight 125 kHz channels in this subband + for (int channel = start; channel < end; ++channel) + LMIC_disableChannel(channel); + + // there's a single 500 kHz channel associated with + // each group of 8 125 kHz channels. Disable it, too. + LMIC_disableChannel(64 + band); +} +void LMIC_selectSubBand(u1_t band) { + ASSERT(band < 8); + for (int b = 0; b<8; ++b) { + if (band == b) + LMIC_enableSubBand(b); + else + LMIC_disableSubBand(b); + } +} + +void LMICus915_updateTx(ostime_t txbeg) { + u1_t chnl = LMIC.txChnl; + if (chnl < 64) { + LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP; + if (LMIC.activeChannels125khz >= 50) + LMIC.txpow = 30; + else + LMIC.txpow = 21; + } else { + // at 500kHz bandwidth, we're allowed more power. + LMIC.txpow = 26; + if (chnl < 64 + 8) { + LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; + } + else { + ASSERT(chnl < 64 + 8 + MAX_XCHANNELS); + LMIC.freq = LMIC.xchFreq[chnl - 72]; + } + } + + // Update global duty cycle stats + if (LMIC.globalDutyRate != 0) { + ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen); + LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); + } +} + +#if !defined(DISABLE_BEACONS) +void LMICus915_setBcnRxParams(void) { + LMIC.dataLen = 0; + LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP; + LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN); +} +#endif // !DISABLE_BEACONS + +// TODO(tmm@mcci.com): parmeterize for US-like +void LMICus915_setRx1Params(void) { + LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; + if( /* TX datarate */LMIC.dndr < US915_DR_SF8C ) + LMIC.dndr += US915_DR_SF10CR - US915_DR_SF10; + else if( LMIC.dndr == US915_DR_SF8C ) + LMIC.dndr = US915_DR_SF7CR; + LMIC.rps = dndr2rps(LMIC.dndr); +} + + +// +// END: US915 related stuff +// +// ================================================================================ +#endif diff --git a/lib/arduino-lmic-master/src/lmic/lmic_us_like.c b/lib/arduino-lmic-master/src/lmic/lmic_us_like.c new file mode 100644 index 00000000..7fd9c495 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_us_like.c @@ -0,0 +1,257 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_US_like + +#ifndef LMICuslike_getFirst500kHzDR +# error "LMICuslike_getFirst500kHzDR() not defined by bandplan" +#endif + +static void setNextChannel(uint start, uint end, uint count) { + ASSERT(count>0); + ASSERT(start>= 1) { + if (chmap & 1) { + LMIC_enableSubBand(subband); + } else { + LMIC_disableSubBand(subband); + } + + // don't change any channels below + base = top = 0; + } + } else if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) { + u1_t const en125 = chpage == MCMD_LADR_CHP_125ON; + + // enable or disable all 125kHz channels + for (u1_t chnl = 0; chnl < 64; ++chnl) { + if (en125) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + + // then apply mask to top 8 channels. + base = 64; + top = 72; + } else { + return 0; + } + + // apply chmap to channels in [base..top-1]. + // Use enable/disable channel to keep activeChannel counts in sync. + for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) { + if (chmap & 0x0001) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + return 1; +} + +// US does not have duty cycling - return now as earliest TX time +// but also do the channel hopping dance. +ostime_t LMICuslike_nextTx(ostime_t now) { + // TODO(tmm@mcci.com): use a static const for US-like + if (LMIC.datarate >= LMICuslike_getFirst500kHzDR()) { // 500kHz + ASSERT(LMIC.activeChannels500khz>0); + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + } + else { // 125kHz + ASSERT(LMIC.activeChannels125khz>0); + setNextChannel(0, 64, LMIC.activeChannels125khz); + } + return now; +} + +#if !defined(DISABLE_JOIN) +void LMICuslike_initJoinLoop(void) { + // set an initial condition so that setNextChannel()'s preconds are met + LMIC.txChnl = 0; + + // then chose a new channel. This gives us a random first channel for + // the join. Minor nit: if channel 0 is enabled, it will never be used + // as the first join channel. The join logic uses the current txChnl, + // then changes after the rx window expires; so we need to set a valid + // starting point. + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // initialize the adrTxPower. + // TODO(tmm@mcci.com): is this right for all US-like regions + LMIC.adrTxPow = 20; // dBm + ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0); + + // make sure LMIC.txend is valid. + LMIC.txend = os_getTime(); + + // make sure the datarate is set to DR0 per LoRaWAN regional reqts V1.0.2, + // section 2.2.2 + // TODO(tmm@mcci.com): parameterize this for US-like + LMICcore_setDrJoin(DRCHG_SET, LORAWAN_DR0); + + // TODO(tmm@mcci.com) need to implement the transmit randomization and + // duty cycle restrictions from LoRaWAN V1.0.2 section 7. +} +#endif // !DISABLE_JOIN + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICuslike_nextJoinState(void) { + // Try the following: + // DR0 (SF10) on a random channel 0..63 + // (honoring enable mask) + // DR4 (SF8C) on a random 500 kHz channel 64..71 + // (always determined by + // previously selected + // 125 kHz channel) + // + u1_t failed = 0; + // TODO(tmm@mcci.com) parameterize for US-like + if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { + // assume that 500 kHz equiv of last 125 kHz channel + // is also enabled, and use it next. + LMIC.txChnl = 64 + (LMIC.txChnl >> 3); + LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); + } + else { + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // TODO(tmm@mcci.com) parameterize + s1_t dr = LORAWAN_DR0; + if ((++LMIC.txCnt & 0x7) == 0) { + failed = 1; // All DR exhausted - signal failed + } + LMICcore_setDrJoin(DRCHG_SET, dr); + } + LMIC.opmode &= ~OP_NEXTCHNL; + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = os_getTime() + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF10:16, SF9=8,..SF8C:1secs + : LMICcore_rndDelay(16 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif + +#endif // CFG_LMIC_US_like diff --git a/lib/arduino-lmic-master/src/lmic/lmic_us_like.h b/lib/arduino-lmic-master/src/lmic/lmic_us_like.h new file mode 100644 index 00000000..66bc549a --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_us_like.h @@ -0,0 +1,100 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lmic_us_like_h_ +# define _lmic_us_like_h_ + +// make sure we want US-like code +#if !CFG_LMIC_US_like +# error "lmic not configured for us-like bandplan" +#endif + +// TODO(tmm@mcci.com): this should come from the lmic.h or lorabase.h file; and +// it's probably affected by the fix to this issue: +// https://github.com/mcci-catena/arduino-lmic/issues/2 +#define DNW2_SAFETY_ZONE ms2osticks(750) + +#define IS_CHANNEL_125khz(c) (c<64) +#define IS_CHANNEL_500khz(c) (c>=64 && c<72) +#define ENABLED_CHANNEL(chnl) ((LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0x0F))) != 0) + +// provide the isValidBeacon1 function -- int for bool. +static inline int +LMICuslike_isValidBeacon1(const uint8_t *d) { + return os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1); +} + +#define LMICbandplan_isValidBeacon1(pFrame) LMICuslike_isValidBeacon1(pFrame) + +// provide a default for LMICbandplan_isFSK() +#define LMICbandplan_isFSK() (0) + +// provide a default LMICbandplan_txDoneFSK() +#define LMICbandplan_txDoneFSK(delay, func) do { } while (0) + +// provide a default LMICbandplan_joinAcceptChannelClear() +#define LMICbandplan_joinAcceptChannelClear() do { } while (0) + +// no CFList on joins for US-like plans +#define LMICbandplan_hasJoinCFlist() (0) + +#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 +// reset or just restore the last sub-band selected by the user. +#define LMICbandplan_resetDefaultChannels() \ + LMICbandplan_initDefaultChannels(/* normal */ 0) + +void LMICuslike_initDefaultChannels(bit_t fJoin); +#define LMICbandplan_initDefaultChannels(fJoin) LMICuslike_initDefaultChannels(fJoin) + +#define LMICbandplan_setSessionInitDefaultChannels() \ + do { /* nothing */} while (0) + +u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap); +#define LMICbandplan_mapChannels(chpage, chmap) LMICuslike_mapChannels(chpage, chmap) + +ostime_t LMICuslike_nextTx(ostime_t now); +#define LMICbandplan_nextTx(now) LMICuslike_nextTx(now) + +void LMICuslike_initJoinLoop(void); +#define LMICbandplan_initJoinLoop() LMICuslike_initJoinLoop() + +ostime_t LMICuslike_nextJoinState(void); +#define LMICbandplan_nextJoinState() LMICuslike_nextJoinState(); + +static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) { + return now; +} +#define LMICbandplan_nextJoinTime(now) LMICeulike_nextJoinTime(now) + +#define LMICbandplan_init() \ + do { /* nothing */ } while (0) + +#endif // _lmic_us_like_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lmic_util.c b/lib/arduino-lmic-master/src/lmic/lmic_util.c new file mode 100644 index 00000000..0d56c259 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_util.c @@ -0,0 +1,335 @@ +/* + +Module: lmic_util.c + +Function: + Encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2019 + +*/ + +#include "lmic_util.h" + +#include + +/* + +Name: LMIC_f2sflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15: sign + bits 14..11: biased exponent + bits 10..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFFF for negative values <= 1.0; + 0x7FFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt16( + float f + ) + { + if (f <= -1.0) + return 0xFFFF; + else if (f >= 1.0) + return 0x7FFF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x8000; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 11) + 0.5; + if (outputFraction >= (1 << 11u)) + { + // reduce output fraction + outputFraction = 1 << 10; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FFF | sign; + + return (uint16_t)(sign | (iExp << 11u) | outputFraction); + } + } + +/* + +Name: LMIC_f2sflt12() + +Function: + Encode a floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range (-1.0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bit 11: sign + bits 10..7: biased exponent + bits 6..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0xFFF for negative values <= 1.0; + 0x7FF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2sflt12( + float f + ) + { + if (f <= -1.0) + return 0xFFF; + else if (f >= 1.0) + return 0x7FF; + else + { + int iExp; + float normalValue; + uint16_t sign; + + normalValue = frexpf(f, &iExp); + + sign = 0; + if (normalValue < 0) + { + // set the "sign bit" of the result + // and work with the absolute value of normalValue. + sign = 0x800; + normalValue = -normalValue; + } + + // abs(f) is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + iExp = 0; + + // bit 15 is the sign + // bits 14..11 are the exponent + // bits 10..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 7) + 0.5; + if (outputFraction >= (1 << 7u)) + { + // reduce output fraction + outputFraction = 1 << 6; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0x7FF | sign; + + return (uint16_t)(sign | (iExp << 7u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt16() + +Function: + Encode a floating point number into a uint16_t. + +Definition: + uint16_t LMIC_f2uflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15..12: biased exponent + bits 11..0: mantissa + + The float is properly rounded, and saturates. + + Note that the encoded value is sign/magnitude format, rather than + two's complement for negative values. + +Returns: + 0x0000 for values < 0.0; + 0xFFFF for positive values >= 1.0; + Otherwise an appropriate encoding of the input float. + +*/ + +uint16_t +LMIC_f2uflt16( + float f + ) + { + if (f < 0.0) + return 0; + else if (f >= 1.0) + return 0xFFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // underflow. + iExp = 0; + + // bits 15..12 are the exponent + // bits 11..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 12) + 0.5; + if (outputFraction >= (1 << 12u)) + { + // reduce output fraction + outputFraction = 1 << 11; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFFF; + + return (uint16_t)((iExp << 12u) | outputFraction); + } + } + +/* + +Name: LMIC_f2uflt12() + +Function: + Encode positive floating point number into a uint16_t using only 12 bits. + +Definition: + uint16_t LMIC_f2sflt16( + float f + ); + +Description: + The float to be transmitted must be a number in the range [0, 1.0). + It is converted to 16-bit integer formatted as follows: + + bits 15-12: zero + bits 11..8: biased exponent + bits 7..0: mantissa + + The float is properly rounded, and saturates. + +Returns: + 0x000 for negative values < 0.0; + 0xFFF for positive values >= 1.0; + Otherwise an appropriate float. + +*/ + +uint16_t +LMIC_f2uflt12( + float f + ) + { + if (f < 0.0) + return 0x000; + else if (f >= 1.0) + return 0xFFF; + else + { + int iExp; + float normalValue; + + normalValue = frexpf(f, &iExp); + + // f is supposed to be in [0..1), so useful exp + // is [0..-15] + iExp += 15; + if (iExp < 0) + // graceful underflow + iExp = 0; + + // bits 11..8 are the exponent + // bits 7..0 are the fraction + // we conmpute the fraction and then decide if we need to round. + uint16_t outputFraction = ldexpf(normalValue, 8) + 0.5; + if (outputFraction >= (1 << 8u)) + { + // reduce output fraction + outputFraction = 1 << 7; + // increase exponent + ++iExp; + } + + // check for overflow and return max instead. + if (iExp > 15) + return 0xFFF; + + return (uint16_t)((iExp << 8u) | outputFraction); + } + } diff --git a/lib/arduino-lmic-master/src/lmic/lmic_util.h b/lib/arduino-lmic-master/src/lmic/lmic_util.h new file mode 100644 index 00000000..d99217d0 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lmic_util.h @@ -0,0 +1,34 @@ +/* + +Module: lmic_util.h + +Function: + Declare encoding and decoding utilities for LMIC clients. + +Copyright & License: + See accompanying LICENSE file. + +Author: + Terry Moore, MCCI September 2019 + +*/ + +#ifndef _LMIC_UTIL_H_ +# define _LMIC_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +uint16_t LMIC_f2sflt16(float); +uint16_t LMIC_f2sflt12(float); +uint16_t LMIC_f2uflt16(float); +uint16_t LMIC_f2uflt12(float); + +#ifdef __cplusplus +} +#endif + +#endif /* _LMIC_UTIL_H_ */ diff --git a/lib/arduino-lmic-master/src/lmic/lorabase.h b/lib/arduino-lmic-master/src/lmic/lorabase.h new file mode 100644 index 00000000..97f992dd --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase.h @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyritght (c) 2017 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _lorabase_h_ +#define _lorabase_h_ + +#ifdef __cplusplus +extern "C"{ +#endif + +// ================================================================================ +// BEG: Keep in sync with lorabase.hpp +// + +enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; +enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; +enum _bw_t { BW125=0, BW250, BW500, BWrfu }; +typedef u1_t cr_t; +typedef u1_t sf_t; +typedef u1_t bw_t; +typedef u1_t dr_t; +// Radio parameter set (encodes SF/BW/CR/IH/NOCRC) +typedef u2_t rps_t; +TYPEDEF_xref2rps_t; + +enum { ILLEGAL_RPS = 0xFF }; + +// Global maximum frame length +enum { STD_PREAMBLE_LEN = 8 }; +enum { MAX_LEN_FRAME = 64 }; +enum { LEN_DEVNONCE = 2 }; +enum { LEN_ARTNONCE = 3 }; +enum { LEN_NETID = 3 }; +enum { DELAY_JACC1 = 5 }; // in secs +enum { DELAY_DNW1 = 1 }; // in secs down window #1 +enum { DELAY_EXTDNW2 = 1 }; // in secs +enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs +enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 +enum { BCN_INTV_exp = 7 }; +enum { BCN_INTV_sec = 1< 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 + + // 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 +}; + +enum { + MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis +}; +enum { + MCMD_LADR_ANS_RFU = 0xF8, // RFU bits + MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level + MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate + MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled +}; +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 +}; +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 +}; +enum { + MCMD_PING_ANS_RFU = 0xFE, + MCMD_PING_ANS_FQACK = 0x01 +}; + +enum { + MCMD_DEVS_EXT_POWER = 0x00, // external power supply + MCMD_DEVS_BATT_MIN = 0x01, // min battery value + MCMD_DEVS_BATT_MAX = 0xFE, // max battery value + MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level +}; + +// Bit fields byte#3 of MCMD_LADR_REQ 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 +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 +}; + +// bit fields of the TxParam request +enum { + MCMD_TxParam_RxDWELL_SHIFT = 5, + MCMD_TxParam_RxDWELL_MASK = 1 << MCMD_TxParam_RxDWELL_SHIFT, + MCMD_TxParam_TxDWELL_SHIFT = 4, + MCMD_TxParam_TxDWELL_MASK = 1 << MCMD_TxParam_TxDWELL_SHIFT, + MCMD_TxParam_MaxEIRP_SHIFT = 0, + MCMD_TxParam_MaxEIRP_MASK = 0xF << MCMD_TxParam_MaxEIRP_SHIFT, +}; + +// Device address +typedef u4_t devaddr_t; + +// RX quality (device) +enum { RSSI_OFF=64, SNR_SCALEUP=4 }; + +inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); } +inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); } +inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); } +inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } +inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } +inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } +inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } +inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } +inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } +inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } +inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { + return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); +} +#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) +// Two frames with params r1/r2 would interfere on air: same SFx + BWx +inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } + +extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; +inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } +inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } +inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } +inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } +inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate +inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate +inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR +inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range +inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps + +// +// BEG: Keep in sync with lorabase.hpp +// ================================================================================ + + +// Calculate airtime +ostime_t calcAirTime (rps_t rps, u1_t plen); +// Sensitivity at given SF/BW +int getSensitivity (rps_t rps); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _lorabase_h_ diff --git a/lib/arduino-lmic-master/src/lmic/lorabase_as923.h b/lib/arduino-lmic-master/src/lmic/lorabase_as923.h new file mode 100644 index 00000000..9cba8c45 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase_as923.h @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_as923_h_ +#define _lorabase_as923_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AS923 (always in scope) +| +\****************************************************************************/ + +enum _dr_as923_t { + AS923_DR_SF12 = 0, + AS923_DR_SF11, + AS923_DR_SF10, + AS923_DR_SF9, + AS923_DR_SF8, + AS923_DR_SF7, + AS923_DR_SF7B, + AS923_DR_FSK, + AS923_DR_NONE +}; + +// Bands: +// g1 : 1% 16dBm +// freq band datarates +enum { + AS923_F1 = 923200000, // g1 SF7-12 + AS923_F2 = 923400000, // g1 SF7-12 + AS923_FDOWN = 923200000, // (RX2 freq, DR2) + AS923_FBCN = 923400000, // default BCN, DR3 + AS923_FPING = 923400000, // default ping, DR3 +}; +enum { + AS923_FREQ_MIN = 915000000, + AS923_FREQ_MAX = 928000000 +}; +enum { + AS923_TX_EIRP_MAX_DBM = 16 // 16 dBm +}; +enum { DR_PAGE_AS923 = 0x10 * (LMIC_REGION_as923 - 1) }; + +enum { AS923_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { AS923JP_LBT_US = 5000 }; // microseconds of LBT time -- 5000 ==> + // 5 ms. We use us rather than ms for + // future 128us support, and just for + // backward compatibility -- there + // is code that uses the _US constant, + // and it's awkward to break it. + +enum { AS923JP_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX + // we measure more than this, we don't tx. + +// AS923 v1.1, all channels face a 1% duty cycle. So this will have to change +// in the future via a config. But this code base needs major changes for +// v1.1 in any case. +enum { AS923_V102_TX_CAP = 100 }; // v1.0.2 allows 100% + +#ifndef AS923_TX_CAP +# define AS923_TX_CAP AS923_V102_TX_CAP +#endif + +#endif /* _lorabase_as923_h_ */ diff --git a/lib/arduino-lmic-master/src/lmic/lorabase_au921.h b/lib/arduino-lmic-master/src/lmic/lorabase_au921.h new file mode 100644 index 00000000..d4c33b9f --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase_au921.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_au921_h_ +#define _lorabase_au921_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for AS921 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for AU 921 MHz +enum _dr_as921_t { + AU921_DR_SF12 = 0, + AU921_DR_SF11, + AU921_DR_SF10, + AU921_DR_SF9, + AU921_DR_SF8, + AU921_DR_SF7, + AU921_DR_SF8C, + AU921_DR_NONE, + // Devices behind a router: + AU921_DR_SF12CR = 8, + AU921_DR_SF11CR, + AU921_DR_SF10CR, + AU921_DR_SF9CR, + AU921_DR_SF8CR, + AU921_DR_SF7CR +}; + +// Default frequency plan for AU 921MHz +enum { + AU921_125kHz_UPFBASE = 915200000, + AU921_125kHz_UPFSTEP = 200000, + AU921_500kHz_UPFBASE = 915900000, + AU921_500kHz_UPFSTEP = 1600000, + AU921_500kHz_DNFBASE = 923300000, + AU921_500kHz_DNFSTEP = 600000 +}; +enum { + AU921_FREQ_MIN = 915000000, + AU921_FREQ_MAX = 928000000 +}; +enum { + AU921_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; + +enum { DR_PAGE_AU921 = 0x10 * (LMIC_REGION_au921 - 1) }; + +enum { AU921_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_au921_h_ */ \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/lorabase_eu868.h b/lib/arduino-lmic-master/src/lmic/lorabase_eu868.h new file mode 100644 index 00000000..0040ad0e --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase_eu868.h @@ -0,0 +1,92 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_eu868_h_ +#define _lorabase_eu868_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for EU868 (always in scope) +| +\****************************************************************************/ + +// +// Default frequency plan for EU 868MHz ISM band +// data rates +// this is a little confusing: the integer values of these constants are the +// DataRates from the LoRaWAN Regional Parmaeter spec. The names are just +// convenient indications, so we can use them in the rare case that we need to +// choose a DataRate by SF and configuration, not by DR code. + +enum _dr_eu868_t { + EU868_DR_SF12 = 0, + EU868_DR_SF11, + EU868_DR_SF10, + EU868_DR_SF9, + EU868_DR_SF8, + EU868_DR_SF7, + EU868_DR_SF7B, + EU868_DR_FSK, + EU868_DR_NONE +}; + +// Bands: +// g1 : 1% 14dBm +// g2 : 0.1% 14dBm +// g3 : 10% 27dBm +// freq band datarates +enum { + EU868_F1 = 868100000, // g1 SF7-12 + EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250 + EU868_F3 = 868500000, // g1 SF7-12 + EU868_F4 = 868850000, // g2 SF7-12 + EU868_F5 = 869050000, // g2 SF7-12 + EU868_F6 = 869525000, // g3 SF7-12 + EU868_J4 = 864100000, // g2 SF7-12 used during join + EU868_J5 = 864300000, // g2 SF7-12 ditto + EU868_J6 = 864500000, // g2 SF7-12 ditto +}; +enum { + EU868_FREQ_MIN = 863000000, + EU868_FREQ_MAX = 870000000 +}; +enum { + EU868_TX_EIRP_MAX_DBM = 16 // 16 dBm EIRP. So subtract 3 dBm for a 3 dBi antenna. +}; + +enum { EU868_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +enum { DR_PAGE_EU868 = 0x10 * (LMIC_REGION_eu868 - 1) }; + +#endif /* _lorabase_eu868_h_ */ \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/lorabase_in866.h b/lib/arduino-lmic-master/src/lmic/lorabase_in866.h new file mode 100644 index 00000000..6955a761 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase_in866.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_in866_h_ +#define _lorabase_in866_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for IN866 (always in scope) +| +\****************************************************************************/ + +enum _dr_in866_t { + IN866_DR_SF12 = 0, // DR0 + IN866_DR_SF11, // DR1 + IN866_DR_SF10, // DR2 + IN866_DR_SF9, // DR3 + IN866_DR_SF8, // DR4 + IN866_DR_SF7, // DR5 + IN866_DR_RFU, // - + IN866_DR_FSK, // DR7 + IN866_DR_NONE +}; + +// There is no dwell-time or duty-cycle limitation for IN +// +// max power: 30dBM +// +// freq datarates +enum { + IN866_F1 = 865062500, // SF7-12 (DR0-5) + IN866_F2 = 865402500, // SF7-12 (DR0-5) + IN866_F3 = 865985000, // SF7-12 (DR0-5) + IN866_FB = 866550000, // beacon/ping +}; +enum { + IN866_FREQ_MIN = 865000000, + IN866_FREQ_MAX = 867000000 +}; +enum { + IN866_TX_EIRP_MAX_DBM = 30 // 30 dBm +}; +enum { DR_PAGE_IN866 = 0x10 * (LMIC_REGION_in866 - 1) }; + +enum { IN866_LMIC_REGION_EIRP = 1 }; // region uses EIRP + +#endif /* _lorabase_in866_h_ */ \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/lorabase_us915.h b/lib/arduino-lmic-master/src/lmic/lorabase_us915.h new file mode 100644 index 00000000..0a771849 --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/lorabase_us915.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* All rights reserved. +* +* Copyright (c) 2017 MCCI Corporation +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _lorabase_us915_h_ +#define _lorabase_us915_h_ + +#ifndef _LMIC_CONFIG_PRECONDITIONS_H_ +# include "lmic_config_preconditions.h" +#endif + +/****************************************************************************\ +| +| Basic definitions for US915 (always in scope) +| +\****************************************************************************/ + +// Frequency plan for US 915MHz ISM band +// data rates +enum _dr_us915_t { + US915_DR_SF10 = 0, + US915_DR_SF9, + US915_DR_SF8, + US915_DR_SF7, + US915_DR_SF8C, + US915_DR_NONE, + // Devices "behind a router" (and upper half of DR list): + US915_DR_SF12CR = 8, + US915_DR_SF11CR, + US915_DR_SF10CR, + US915_DR_SF9CR, + US915_DR_SF8CR, + US915_DR_SF7CR +}; + +// Default frequency plan for US 915MHz +enum { + US915_125kHz_UPFBASE = 902300000, + US915_125kHz_UPFSTEP = 200000, + US915_500kHz_UPFBASE = 903000000, + US915_500kHz_UPFSTEP = 1600000, + US915_500kHz_DNFBASE = 923300000, + US915_500kHz_DNFSTEP = 600000 +}; +enum { + US915_FREQ_MIN = 902000000, + US915_FREQ_MAX = 928000000 +}; +enum { + US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're + // on an 64-channel bandplan. See code + // that computes tx power. +}; +enum { DR_PAGE_US915 = 0x10 * (LMIC_REGION_us915 - 1) }; + +enum { US915_LMIC_REGION_EIRP = 0 }; // region doesn't use EIRP, uses tx power + +#endif /* _lorabase_us915_h_ */ \ No newline at end of file diff --git a/lib/arduino-lmic-master/src/lmic/oslmic.c b/lib/arduino-lmic-master/src/lmic/oslmic.c new file mode 100644 index 00000000..e2bc570d --- /dev/null +++ b/lib/arduino-lmic-master/src/lmic/oslmic.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2017 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +extern const struct lmic_pinmap lmic_pins; + +// RUNTIME STATE +static struct { + osjob_t* scheduledjobs; + osjob_t* runnablejobs; +} OS; + +int os_init_ex (const void *pintable) { + memset(&OS, 0x00, sizeof(OS)); + hal_init_ex(pintable); + if (! radio_init()) + return 0; + LMIC_init(); + return 1; +} + +void os_init() { + if (os_init_ex((const void *)&lmic_pins)) + return; + ASSERT(0); +} + +ostime_t os_getTime () { + return hal_ticks(); +} + +// unlink job from queue, return if removed +static int unlinkjob (osjob_t** pnext, osjob_t* job) { + for( ; *pnext; pnext = &((*pnext)->next)) { + if(*pnext == job) { // unlink + *pnext = job->next; + return 1; + } + } + return 0; +} + +// clear scheduled job +void os_clearCallback (osjob_t* job) { + hal_disableIRQs(); + unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job); + hal_enableIRQs(); +} + +// schedule immediately runnable job +void os_setCallback (osjob_t* job, osjobcb_t cb) { + osjob_t** pnext; + hal_disableIRQs(); + // remove if job was already queued + unlinkjob(&OS.runnablejobs, job); + // fill-in job + job->func = cb; + job->next = NULL; + // add to end of run queue + for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); + *pnext = job; + hal_enableIRQs(); +} + +// schedule timed job +void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { + osjob_t** pnext; + hal_disableIRQs(); + // remove if job was already queued + unlinkjob(&OS.scheduledjobs, job); + // fill-in job + job->deadline = time; + job->func = cb; + job->next = NULL; + // insert into schedule + for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { + if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) + // enqueue before next element and stop + job->next = *pnext; + break; + } + } + *pnext = job; + hal_enableIRQs(); +} + +// execute jobs from timer and from run queue +void os_runloop () { + while(1) { + os_runloop_once(); + } +} + +void os_runloop_once() { + osjob_t* j = NULL; + hal_disableIRQs(); + // check for runnable jobs + if(OS.runnablejobs) { + j = OS.runnablejobs; + OS.runnablejobs = j->next; + } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs + j = OS.scheduledjobs; + OS.scheduledjobs = j->next; + } else { // nothing pending + hal_sleep(); // wake by irq (timer already restarted) + } + hal_enableIRQs(); + if(j) { // run job callback + j->func(j); + } +} diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.h b/lib/arduino-lmic-master/src/lmic/oslmic.h similarity index 80% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.h rename to lib/arduino-lmic-master/src/lmic/oslmic.h index 2253030e..2ad162c7 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/oslmic.h +++ b/lib/arduino-lmic-master/src/lmic/oslmic.h @@ -1,288 +1,316 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -//! \file -#ifndef _oslmic_h_ -#define _oslmic_h_ - -// Dependencies required for the LoRa MAC in C to run. -// These settings can be adapted to the underlying system. -// You should not, however, change the lmic.[hc] - -#include "config.h" -#include -#include - -#ifdef __cplusplus -extern "C"{ -#endif - -//================================================================================ -//================================================================================ -// Target platform as C library -typedef uint8_t bit_t; -typedef uint8_t u1_t; -typedef int8_t s1_t; -typedef uint16_t u2_t; -typedef int16_t s2_t; -typedef uint32_t u4_t; -typedef int32_t s4_t; -typedef unsigned int uint; -typedef const char* str_t; - -#include -#include "hal.h" -#define EV(a,b,c) /**/ -#define DO_DEVDB(field1,field2) /**/ -#if !defined(CFG_noassert) -#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) -#else -#define ASSERT(cond) /**/ -#endif - -#define os_clearMem(a,b) memset(a,0,b) -#define os_copyMem(a,b,c) memcpy(a,b,c) - -typedef struct osjob_t osjob_t; -typedef struct band_t band_t; -typedef struct chnldef_t chnldef_t; -typedef struct rxsched_t rxsched_t; -typedef struct bcninfo_t bcninfo_t; -typedef const u1_t* xref2cu1_t; -typedef u1_t* xref2u1_t; -#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t -#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t -#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t -#define TYPEDEF_xref2band_t typedef band_t* xref2band_t -#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t - -#define SIZEOFEXPR(x) sizeof(x) - -#define ON_LMIC_EVENT(ev) onEvent(ev) -#define DECL_ON_LMIC_EVENT void onEvent(ev_t e) - -extern u4_t AESAUX[]; -extern u4_t AESKEY[]; -#define AESkey ((u1_t*)AESKEY) -#define AESaux ((u1_t*)AESAUX) -#define FUNC_ADDR(func) (&(func)) - -u1_t radio_rand1 (void); -#define os_getRndU1() radio_rand1() - -#define DEFINE_LMIC struct lmic_t LMIC -#define DECLARE_LMIC extern struct lmic_t LMIC - -void radio_init (void); -void radio_irq_handler (u1_t dio); -void os_init (void); -void os_runloop (void); -void os_runloop_once (void); - -//================================================================================ - - -#ifndef RX_RAMPUP -#define RX_RAMPUP (us2osticks(2000)) -#endif -#ifndef TX_RAMPUP -#define TX_RAMPUP (us2osticks(2000)) -#endif - -#ifndef OSTICKS_PER_SEC -#define OSTICKS_PER_SEC 32768 -#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 -#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. -#endif - -typedef s4_t ostime_t; - -#if !HAS_ostick_conv -#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) -#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) -#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) -#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) -#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) -// Special versions -#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) -#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) -#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) -#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) -#endif - - -struct osjob_t; // fwd decl. -typedef void (*osjobcb_t) (struct osjob_t*); -struct osjob_t { - struct osjob_t* next; - ostime_t deadline; - osjobcb_t func; -}; -TYPEDEF_xref2osjob_t; - - -#ifndef HAS_os_calls - -#ifndef os_getDevKey -void os_getDevKey (xref2u1_t buf); -#endif -#ifndef os_getArtEui -void os_getArtEui (xref2u1_t buf); -#endif -#ifndef os_getDevEui -void os_getDevEui (xref2u1_t buf); -#endif -#ifndef os_setCallback -void os_setCallback (xref2osjob_t job, osjobcb_t cb); -#endif -#ifndef os_setTimedCallback -void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); -#endif -#ifndef os_clearCallback -void os_clearCallback (xref2osjob_t job); -#endif -#ifndef os_getTime -ostime_t os_getTime (void); -#endif -#ifndef os_getTimeSecs -uint os_getTimeSecs (void); -#endif -#ifndef os_radio -void os_radio (u1_t mode); -#endif -#ifndef os_getBattLevel -u1_t os_getBattLevel (void); -#endif - -#ifndef os_rlsbf4 -//! Read 32-bit quantity from given pointer in little endian byte order. -u4_t os_rlsbf4 (xref2cu1_t buf); -#endif -#ifndef os_wlsbf4 -//! Write 32-bit quntity into buffer in little endian byte order. -void os_wlsbf4 (xref2u1_t buf, u4_t value); -#endif -#ifndef os_rmsbf4 -//! Read 32-bit quantity from given pointer in big endian byte order. -u4_t os_rmsbf4 (xref2cu1_t buf); -#endif -#ifndef os_wmsbf4 -//! Write 32-bit quntity into buffer in big endian byte order. -void os_wmsbf4 (xref2u1_t buf, u4_t value); -#endif -#ifndef os_rlsbf2 -//! Read 16-bit quantity from given pointer in little endian byte order. -u2_t os_rlsbf2 (xref2cu1_t buf); -#endif -#ifndef os_wlsbf2 -//! Write 16-bit quntity into buffer in little endian byte order. -void os_wlsbf2 (xref2u1_t buf, u2_t value); -#endif - -//! Get random number (default impl for u2_t). -#ifndef os_getRndU2 -#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) -#endif -#ifndef os_crc16 -u2_t os_crc16 (xref2u1_t d, uint len); -#endif - -#endif // !HAS_os_calls - -// ====================================================================== -// Table support -// These macros for defining a table of constants and retrieving values -// from it makes it easier for other platforms (like AVR) to optimize -// table accesses. -// Use CONST_TABLE() whenever declaring or defining a table, and -// TABLE_GET_xx whenever accessing its values. The actual name of the -// declared variable will be modified to prevent accidental direct -// access. The accessor macros forward to an inline function to allow -// proper type checking of the array element type. - -// Helper to add a prefix to the table name -#define RESOLVE_TABLE(table) constant_table_ ## table - -// Accessors for table elements -#define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) -#define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) -#define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) -#define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) -#define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) -#define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) -#define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) -#define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) - -#if defined(__AVR__) - #include - // Macro to define the getter functions. This loads data from - // progmem using pgm_read_xx, or accesses memory directly when the - // index is a constant so gcc can optimize it away; - #define TABLE_GETTER(postfix, type, pgm_type) \ - inline type table_get ## postfix(const type *table, size_t index) { \ - if (__builtin_constant_p(table[index])) \ - return table[index]; \ - return pgm_read_ ## pgm_type(&table[index]); \ - } - - TABLE_GETTER(_u1, u1_t, byte); - TABLE_GETTER(_s1, s1_t, byte); - TABLE_GETTER(_u2, u2_t, word); - TABLE_GETTER(_s2, s2_t, word); - TABLE_GETTER(_u4, u4_t, dword); - TABLE_GETTER(_s4, s4_t, dword); - - // This assumes ostime_t is 4 bytes, so error out if it is not - typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; - TABLE_GETTER(_ostime, ostime_t, dword); - - // For AVR, store constants in PROGMEM, saving on RAM usage - #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) - - #define lmic_printf(fmt, ...) printf_P(PSTR(fmt), ## __VA_ARGS__) -#else - inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } - inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } - inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } - inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } - inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } - inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } - inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } - - // Declare a table - #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) - #define lmic_printf printf -#endif - -// ====================================================================== -// AES support -// !!Keep in sync with lorabase.hpp!! - -#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too -#define AES_ENC 0x00 -#define AES_DEC 0x01 -#define AES_MIC 0x02 -#define AES_CTR 0x04 -#define AES_MICNOAUX 0x08 -#endif -#ifndef AESkey // if AESkey is defined as macro all other values must be too -extern xref2u1_t AESkey; -extern xref2u1_t AESaux; -#endif -#ifndef os_aes -u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _oslmic_h_ +/* + * Copyright (c) 2014-2016 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//! \file +#ifndef _oslmic_h_ +#define _oslmic_h_ + +// Dependencies required for the LoRa MAC in C to run. +// These settings can be adapted to the underlying system. +// You should not, however, change the lmic.[hc] + +#include "config.h" +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +//================================================================================ +//================================================================================ +// Target platform as C library +typedef uint8_t bit_t; +typedef uint8_t u1_t; +typedef int8_t s1_t; +typedef uint16_t u2_t; +typedef int16_t s2_t; +typedef uint32_t u4_t; +typedef int32_t s4_t; +typedef unsigned int uint; +typedef const char* str_t; + +#include +#include "hal.h" +#define EV(a,b,c) /**/ +#define DO_DEVDB(field1,field2) /**/ +#if !defined(CFG_noassert) +#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) +#else +#define ASSERT(cond) /**/ +#endif + +#define os_clearMem(a,b) memset(a,0,b) +#define os_copyMem(a,b,c) memcpy(a,b,c) + +typedef struct osjob_t osjob_t; +typedef struct band_t band_t; +typedef struct chnldef_t chnldef_t; +typedef struct rxsched_t rxsched_t; +typedef struct bcninfo_t bcninfo_t; +typedef const u1_t* xref2cu1_t; +typedef u1_t* xref2u1_t; +typedef s4_t ostime_t; + +#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t +#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t +#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t +#define TYPEDEF_xref2band_t typedef band_t* xref2band_t +#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t + +#define SIZEOFEXPR(x) sizeof(x) + +#define ON_LMIC_EVENT(ev) onEvent(ev) +#define DECL_ON_LMIC_EVENT void onEvent(ev_t e) + +extern u4_t AESAUX[]; +extern u4_t AESKEY[]; +#define AESkey ((u1_t*)AESKEY) +#define AESaux ((u1_t*)AESAUX) +#define FUNC_ADDR(func) (&(func)) + +u1_t radio_rand1 (void); +#define os_getRndU1() radio_rand1() + +#define DEFINE_LMIC struct lmic_t LMIC +#define DECLARE_LMIC extern struct lmic_t LMIC + +typedef struct oslmic_radio_rssi_s oslmic_radio_rssi_t; + +struct oslmic_radio_rssi_s { + s2_t min_rssi; + s2_t max_rssi; + s2_t mean_rssi; + u2_t n_rssi; +}; + +int radio_init (void); +void radio_irq_handler (u1_t dio); +void radio_irq_handler_v2 (u1_t dio, ostime_t tref); +void os_init (void); +int os_init_ex (const void *pPinMap); +void os_runloop (void); +void os_runloop_once (void); +u1_t radio_rssi (void); +void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi); + +//================================================================================ + + +#ifndef RX_RAMPUP +#define RX_RAMPUP (us2osticks(2000)) +#endif +#ifndef TX_RAMPUP +#define TX_RAMPUP (us2osticks(2000)) +#endif + +#ifndef OSTICKS_PER_SEC +#define OSTICKS_PER_SEC 32768 +#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 +#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. +#endif + +#if !HAS_ostick_conv +#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) +#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) +#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) +#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) +#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) +// Special versions +#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) +#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) +#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) +#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) +#endif + + +struct osjob_t; // fwd decl. +typedef void (*osjobcb_t) (struct osjob_t*); +struct osjob_t { + struct osjob_t* next; + ostime_t deadline; + osjobcb_t func; +}; +TYPEDEF_xref2osjob_t; + + +#ifndef HAS_os_calls + +#ifndef os_getDevKey +void os_getDevKey (xref2u1_t buf); +#endif +#ifndef os_getArtEui +void os_getArtEui (xref2u1_t buf); +#endif +#ifndef os_getDevEui +void os_getDevEui (xref2u1_t buf); +#endif +#ifndef os_setCallback +void os_setCallback (xref2osjob_t job, osjobcb_t cb); +#endif +#ifndef os_setTimedCallback +void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); +#endif +#ifndef os_clearCallback +void os_clearCallback (xref2osjob_t job); +#endif +#ifndef os_getTime +ostime_t os_getTime (void); +#endif +#ifndef os_getTimeSecs +uint os_getTimeSecs (void); +#endif +#ifndef os_radio +void os_radio (u1_t mode); +#endif +#ifndef os_getBattLevel +u1_t os_getBattLevel (void); +#endif + +#ifndef os_rlsbf4 +//! Read 32-bit quantity from given pointer in little endian byte order. +u4_t os_rlsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf4 +//! Write 32-bit quntity into buffer in little endian byte order. +void os_wlsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rmsbf4 +//! Read 32-bit quantity from given pointer in big endian byte order. +u4_t os_rmsbf4 (xref2cu1_t buf); +#endif +#ifndef os_wmsbf4 +//! Write 32-bit quntity into buffer in big endian byte order. +void os_wmsbf4 (xref2u1_t buf, u4_t value); +#endif +#ifndef os_rlsbf2 +//! Read 16-bit quantity from given pointer in little endian byte order. +u2_t os_rlsbf2 (xref2cu1_t buf); +#endif +#ifndef os_wlsbf2 +//! Write 16-bit quntity into buffer in little endian byte order. +void os_wlsbf2 (xref2u1_t buf, u2_t value); +#endif + +//! Get random number (default impl for u2_t). +#ifndef os_getRndU2 +#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) +#endif +#ifndef os_crc16 +u2_t os_crc16 (xref2cu1_t d, uint len); +#endif + +#endif // !HAS_os_calls + +// ====================================================================== +// Table support +// These macros for defining a table of constants and retrieving values +// from it makes it easier for other platforms (like AVR) to optimize +// table accesses. +// Use CONST_TABLE() whenever declaring or defining a table, and +// TABLE_GET_xx whenever accessing its values. The actual name of the +// declared variable will be modified to prevent accidental direct +// access. The accessor macros forward to an inline function to allow +// proper type checking of the array element type. + +// Helper to add a prefix to the table name +#define RESOLVE_TABLE(table) constant_table_ ## table + +// get number of entries in table +#define LENOF_TABLE(table) (sizeof(RESOLVE_TABLE(table)) / sizeof(RESOLVE_TABLE(table)[0])) + +// Accessors for table elements +#define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) +#define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) +#define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) +#define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) +#define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) +#define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) +#define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) +#define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) + +#if defined(__AVR__) + #include + // Macro to define the getter functions. This loads data from + // progmem using pgm_read_xx, or accesses memory directly when the + // index is a constant so gcc can optimize it away; + #define TABLE_GETTER(postfix, type, pgm_type) \ + inline type table_get ## postfix(const type *table, size_t index) { \ + if (__builtin_constant_p(table[index])) \ + return table[index]; \ + return pgm_read_ ## pgm_type(&table[index]); \ + } + + TABLE_GETTER(_u1, u1_t, byte); + TABLE_GETTER(_s1, s1_t, byte); + TABLE_GETTER(_u2, u2_t, word); + TABLE_GETTER(_s2, s2_t, word); + TABLE_GETTER(_u4, u4_t, dword); + TABLE_GETTER(_s4, s4_t, dword); + + // This assumes ostime_t is 4 bytes, so error out if it is not + typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; + TABLE_GETTER(_ostime, ostime_t, dword); + + // For AVR, store constants in PROGMEM, saving on RAM usage + #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) +#else + inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } + inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } + inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } + inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } + inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } + inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } + inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } + + // Declare a table + #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) +#endif + +// ====================================================================== +// AES support +// !!Keep in sync with lorabase.hpp!! + +#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too +#define AES_ENC 0x00 +#define AES_DEC 0x01 +#define AES_MIC 0x02 +#define AES_CTR 0x04 +#define AES_MICNOAUX 0x08 +#endif +#ifndef AESkey // if AESkey is defined as macro all other values must be too +extern xref2u1_t AESkey; +extern xref2u1_t AESaux; +#endif +#ifndef os_aes +u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _oslmic_h_ diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/radio.c b/lib/arduino-lmic-master/src/lmic/radio.c similarity index 78% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/radio.c rename to lib/arduino-lmic-master/src/lmic/radio.c index 6c570545..e5b16eb3 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/radio.c +++ b/lib/arduino-lmic-master/src/lmic/radio.c @@ -1,860 +1,1028 @@ -/******************************************************************************* - * Copyright (c) 2014-2015 IBM Corporation. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Zurich Research Lab - initial API, implementation and documentation - *******************************************************************************/ - -#include "lmic.h" - -// ---------------------------------------- -// Registers Mapping -#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 LORARegFifoAddrPtr 0x0D -#define FSKRegRssiConfig 0x0E -#define LORARegFifoTxBaseAddr 0x0E -#define FSKRegRssiCollision 0x0F -#define LORARegFifoRxBaseAddr 0x0F -#define FSKRegRssiThresh 0x10 -#define LORARegFifoRxCurrentAddr 0x10 -#define FSKRegRssiValue 0x11 -#define LORARegIrqFlagsMask 0x11 -#define FSKRegRxBw 0x12 -#define LORARegIrqFlags 0x12 -#define FSKRegAfcBw 0x13 -#define LORARegRxNbBytes 0x13 -#define FSKRegOokPeak 0x14 -#define LORARegRxHeaderCntValueMsb 0x14 -#define FSKRegOokFix 0x15 -#define LORARegRxHeaderCntValueLsb 0x15 -#define FSKRegOokAvg 0x16 -#define LORARegRxPacketCntValueMsb 0x16 -#define LORARegRxpacketCntValueLsb 0x17 -#define LORARegModemStat 0x18 -#define LORARegPktSnrValue 0x19 -#define FSKRegAfcFei 0x1A -#define LORARegPktRssiValue 0x1A -#define FSKRegAfcMsb 0x1B -#define LORARegRssiValue 0x1B -#define FSKRegAfcLsb 0x1C -#define LORARegHopChannel 0x1C -#define FSKRegFeiMsb 0x1D -#define LORARegModemConfig1 0x1D -#define FSKRegFeiLsb 0x1E -#define LORARegModemConfig2 0x1E -#define FSKRegPreambleDetect 0x1F -#define LORARegSymbTimeoutLsb 0x1F -#define FSKRegRxTimeout1 0x20 -#define LORARegPreambleMsb 0x20 -#define FSKRegRxTimeout2 0x21 -#define LORARegPreambleLsb 0x21 -#define FSKRegRxTimeout3 0x22 -#define LORARegPayloadLength 0x22 -#define FSKRegRxDelay 0x23 -#define LORARegPayloadMaxLength 0x23 -#define FSKRegOsc 0x24 -#define LORARegHopPeriod 0x24 -#define FSKRegPreambleMsb 0x25 -#define LORARegFifoRxByteAddr 0x25 -#define LORARegModemConfig3 0x26 -#define FSKRegPreambleLsb 0x26 -#define FSKRegSyncConfig 0x27 -#define LORARegFeiMsb 0x28 -#define FSKRegSyncValue1 0x28 -#define LORAFeiMib 0x29 -#define FSKRegSyncValue2 0x29 -#define LORARegFeiLsb 0x2A -#define FSKRegSyncValue3 0x2A -#define FSKRegSyncValue4 0x2B -#define LORARegRssiWideband 0x2C -#define FSKRegSyncValue5 0x2C -#define FSKRegSyncValue6 0x2D -#define FSKRegSyncValue7 0x2E -#define FSKRegSyncValue8 0x2F -#define FSKRegPacketConfig1 0x30 -#define FSKRegPacketConfig2 0x31 -#define LORARegDetectOptimize 0x31 -#define FSKRegPayloadLength 0x32 -#define FSKRegNodeAdrs 0x33 -#define LORARegInvertIQ 0x33 -#define FSKRegBroadcastAdrs 0x34 -#define FSKRegFifoThresh 0x35 -#define FSKRegSeqConfig1 0x36 -#define FSKRegSeqConfig2 0x37 -#define LORARegDetectionThreshold 0x37 -#define FSKRegTimerResol 0x38 -#define FSKRegTimer1Coef 0x39 -#define LORARegSyncWord 0x39 -#define FSKRegTimer2Coef 0x3A -#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 -// #define RegAgcRef 0x43 // common -// #define RegAgcThresh1 0x44 // common -// #define RegAgcThresh2 0x45 // common -// #define RegAgcThresh3 0x46 // common -// #define RegPllHop 0x4B // common -// #define RegTcxo 0x58 // common -#ifdef CFG_sx1276_radio - #define RegPaDac 0x4D // common -#else - #define RegPaDac 0x5A // common -#endif -// #define RegPll 0x5C // common -// #define RegPllLowPn 0x5E // common -// #define RegFormerTemp 0x6C // common -// #define RegBitRateFrac 0x70 // common - -// ---------------------------------------- -// spread factors and mode for RegModemConfig2 -#define SX1272_MC2_FSK 0x00 -#define SX1272_MC2_SF7 0x70 -#define SX1272_MC2_SF8 0x80 -#define SX1272_MC2_SF9 0x90 -#define SX1272_MC2_SF10 0xA0 -#define SX1272_MC2_SF11 0xB0 -#define SX1272_MC2_SF12 0xC0 -// bandwidth for RegModemConfig1 -#define SX1272_MC1_BW_125 0x00 -#define SX1272_MC1_BW_250 0x40 -#define SX1272_MC1_BW_500 0x80 -// coding rate for RegModemConfig1 -#define SX1272_MC1_CR_4_5 0x08 -#define SX1272_MC1_CR_4_6 0x10 -#define SX1272_MC1_CR_4_7 0x18 -#define SX1272_MC1_CR_4_8 0x20 -#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive -#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 -#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 -// transmit power configuration for RegPaConfig -#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 -#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 - - -// sx1276 RegModemConfig1 -#define SX1276_MC1_BW_125 0x70 -#define SX1276_MC1_BW_250 0x80 -#define SX1276_MC1_BW_500 0x90 -#define SX1276_MC1_CR_4_5 0x02 -#define SX1276_MC1_CR_4_6 0x04 -#define SX1276_MC1_CR_4_7 0x06 -#define SX1276_MC1_CR_4_8 0x08 - -#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 - -// sx1276 RegModemConfig2 -#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 - -// sx1276 RegModemConfig3 -#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 -#define SX1276_MC3_AGCAUTO 0x04 - -// preamble for lora networks (nibbles swapped) -#define LORA_MAC_PREAMBLE 0x34 - -#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A -#ifdef CFG_sx1276_radio -#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 -#elif CFG_sx1272_radio -#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 -#endif - - - -// ---------------------------------------- -// Constants for radio registers -#define OPMODE_LORA 0x80 -#define OPMODE_MASK 0x07 -#define OPMODE_SLEEP 0x00 -#define OPMODE_STANDBY 0x01 -#define OPMODE_FSTX 0x02 -#define OPMODE_TX 0x03 -#define OPMODE_FSRX 0x04 -#define OPMODE_RX 0x05 -#define OPMODE_RX_SINGLE 0x06 -#define OPMODE_CAD 0x07 - -// ---------------------------------------- -// Bits masking the corresponding IRQs from the radio -#define IRQ_LORA_RXTOUT_MASK 0x80 -#define IRQ_LORA_RXDONE_MASK 0x40 -#define IRQ_LORA_CRCERR_MASK 0x20 -#define IRQ_LORA_HEADER_MASK 0x10 -#define IRQ_LORA_TXDONE_MASK 0x08 -#define IRQ_LORA_CDDONE_MASK 0x04 -#define IRQ_LORA_FHSSCH_MASK 0x02 -#define IRQ_LORA_CDDETD_MASK 0x01 - -#define IRQ_FSK1_MODEREADY_MASK 0x80 -#define IRQ_FSK1_RXREADY_MASK 0x40 -#define IRQ_FSK1_TXREADY_MASK 0x20 -#define IRQ_FSK1_PLLLOCK_MASK 0x10 -#define IRQ_FSK1_RSSI_MASK 0x08 -#define IRQ_FSK1_TIMEOUT_MASK 0x04 -#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 -#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 -#define IRQ_FSK2_FIFOFULL_MASK 0x80 -#define IRQ_FSK2_FIFOEMPTY_MASK 0x40 -#define IRQ_FSK2_FIFOLEVEL_MASK 0x20 -#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 -#define IRQ_FSK2_PACKETSENT_MASK 0x08 -#define IRQ_FSK2_PAYLOADREADY_MASK 0x04 -#define IRQ_FSK2_CRCOK_MASK 0x02 -#define IRQ_FSK2_LOWBAT_MASK 0x01 - -// ---------------------------------------- -// DIO function mappings D0D1D2D3 -#define MAP_DIO0_LORA_RXDONE 0x00 // 00------ -#define MAP_DIO0_LORA_TXDONE 0x40 // 01------ -#define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- -#define MAP_DIO1_LORA_NOP 0x30 // --11---- -#define MAP_DIO2_LORA_NOP 0xC0 // ----11-- - -#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) -#define MAP_DIO1_FSK_NOP 0x30 // --11---- -#define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- -#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- - - -// FSK IMAGECAL defines -#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F -#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 -#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default - -#define RF_IMAGECAL_IMAGECAL_MASK 0xBF -#define RF_IMAGECAL_IMAGECAL_START 0x40 - -#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]; - - -#ifdef CFG_sx1276_radio -#define LNA_RX_GAIN (0x20|0x1) -#elif CFG_sx1272_radio -#define LNA_RX_GAIN (0x20|0x03) -#else -#error Missing CFG_sx1272_radio/CFG_sx1276_radio -#endif - - -static void writeReg (u1_t addr, u1_t data ) { - hal_pin_nss(0); - hal_spi(addr | 0x80); - hal_spi(data); - hal_pin_nss(1); -} - -static u1_t readReg (u1_t addr) { - hal_pin_nss(0); - hal_spi(addr & 0x7F); - u1_t val = hal_spi(0x00); - hal_pin_nss(1); - return val; -} - -static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { - hal_pin_nss(0); - hal_spi(addr | 0x80); - for (u1_t i=0; i>16)); - writeReg(RegFrfMid, (u1_t)(frf>> 8)); - writeReg(RegFrfLsb, (u1_t)(frf>> 0)); -} - - - -static void configPower () { -#ifdef CFG_sx1276_radio - // no boost used for now - s1_t pw = (s1_t)LMIC.txpow; - if(pw > 15) { - pw = 15; - } else if(pw < 2) { - pw = 2; - } - // check board type for BOOST pin - writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf))); // sets PA_BOOST pin (original lmic) - //writeReg(RegPaConfig, (u1_t)(pw&0xf)); // sets RFO pin (patched lmic) - writeReg(RegPaDac, readReg(RegPaDac)|0x4); - -#elif CFG_sx1272_radio - // set PA config (2-17 dBm using PA_BOOST) - s1_t pw = (s1_t)LMIC.txpow; - if(pw > 17) { - pw = 17; - } else if(pw < 2) { - pw = 2; - } - writeReg(RegPaConfig, (u1_t)(0x80|(pw-2))); -#else -#error Missing CFG_sx1272_radio/CFG_sx1276_radio -#endif /* CFG_sx1272_radio */ -} - -static void txfsk () { - // select FSK modem (from sleep mode) - writeReg(RegOpMode, 0x10); // FSK, BT=0.5 - ASSERT(readReg(RegOpMode) == 0x10); - // enter standby mode (required for FIFO loading)) - opmode(OPMODE_STANDBY); - // 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); - writeReg(FSKRegSyncValue1, 0xC1); - writeReg(FSKRegSyncValue2, 0x94); - writeReg(FSKRegSyncValue3, 0xC1); - // configure frequency - configChannel(); - // configure output power - configPower(); - - // 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 - writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) - - // download length byte and buffer to the radio FIFO - writeReg(RegFifo, LMIC.dataLen); - writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); - - // enable antenna switch for TX - hal_pin_rxtx(1); - - // now we actually start the transmission - opmode(OPMODE_TX); -} - -static void txlora () { - // select LoRa modem (from sleep mode) - //writeReg(RegOpMode, OPMODE_LORA); - opmodeLora(); - ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); - - // enter standby mode (required for FIFO loading)) - opmode(OPMODE_STANDBY); - // configure LoRa modem (cfg1, cfg2) - configLoraModem(); - // configure frequency - configChannel(); - // configure output power - writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec - configPower(); - // set sync word - writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); - - // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP - writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); - // clear all radio IRQ flags - writeReg(LORARegIrqFlags, 0xFF); - // mask all IRQs but TxDone - writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); - - // initialize the payload size and address pointers - writeReg(LORARegFifoTxBaseAddr, 0x00); - writeReg(LORARegFifoAddrPtr, 0x00); - writeReg(LORARegPayloadLength, LMIC.dataLen); - - // download buffer to the radio FIFO - writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); - - // enable antenna switch for TX - hal_pin_rxtx(1); - - // now we actually start the transmission - opmode(OPMODE_TX); - -#if LMIC_DEBUG_LEVEL > 0 - u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 - u1_t bw = getBw(LMIC.rps); - u1_t cr = getCr(LMIC.rps); - lmic_printf("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n", - os_getTime(), LMIC.freq, LMIC.dataLen, sf, - bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), - cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), - getIh(LMIC.rps) - ); -#endif -} - -// start transmitter (buf=LMIC.frame, len=LMIC.dataLen) -static void starttx () { - ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); - if(getSf(LMIC.rps) == FSK) { // FSK modem - txfsk(); - } else { // LoRa modem - txlora(); - } - // the radio will go back to STANDBY mode as soon as the TX is finished - // the corresponding IRQ will inform us about completion. -} - -enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; - -static CONST_TABLE(u1_t, rxlorairqmask)[] = { - [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, - [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, - [RXMODE_RSSI] = 0x00, -}; - -// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) -static void rxlora (u1_t rxmode) { - // select LoRa modem (from sleep mode) - opmodeLora(); - ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); - // enter standby mode (warm up)) - opmode(OPMODE_STANDBY); - // don't use MAC settings at startup - if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan - writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); - writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); - } else { // single or continuous rx mode - // configure LoRa modem (cfg1, cfg2) - configLoraModem(); - // configure frequency - configChannel(); - } - // set LNA gain - writeReg(RegLna, LNA_RX_GAIN); - // set max payload size - writeReg(LORARegPayloadMaxLength, 64); -#if !defined(DISABLE_INVERT_IQ_ON_RX) - // use inverted I/Q signal (prevent mote-to-mote communication) - writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); -#endif - // set symbol timeout (for single rx) - writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms); - // set sync word - writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); - - // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP - writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); - // clear all radio IRQ flags - writeReg(LORARegIrqFlags, 0xFF); - // enable required radio IRQs - writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode)); - - // enable antenna switch for RX - hal_pin_rxtx(0); - - // now instruct the radio to receive - if (rxmode == RXMODE_SINGLE) { // single rx - hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time - opmode(OPMODE_RX_SINGLE); - } else { // continous rx (scan or rssi) - opmode(OPMODE_RX); - } - -#if LMIC_DEBUG_LEVEL > 0 - if (rxmode == RXMODE_RSSI) { - lmic_printf("RXMODE_RSSI\n"); - } else { - u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 - u1_t bw = getBw(LMIC.rps); - u1_t cr = getCr(LMIC.rps); - lmic_printf("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n", - os_getTime(), - rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), - LMIC.freq, sf, - bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), - cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), - getIh(LMIC.rps) - ); - } -#endif -} - -static void rxfsk (u1_t rxmode) { - // only single rx (no continuous scanning, no noise sampling) - ASSERT( rxmode == RXMODE_SINGLE ); - // select FSK modem (from sleep mode) - //writeReg(RegOpMode, 0x00); // (not LoRa) - opmodeFSK(); - ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); - // enter standby mode (warm up)) - opmode(OPMODE_STANDBY); - // configure frequency - configChannel(); - // set LNA gain - //writeReg(RegLna, 0x20|0x03); // max gain, boost enable - writeReg(RegLna, LNA_RX_GAIN); - // configure receiver - writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? - // set receiver bandwidth - writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb - // set AFC bandwidth - 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); - - // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut - writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); - - // enable antenna switch for RX - hal_pin_rxtx(0); - - // now instruct the radio to receive - hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time - opmode(OPMODE_RX); // no single rx mode available in FSK -} - -static void startrx (u1_t rxmode) { - ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); - if(getSf(LMIC.rps) == FSK) { // FSK modem - rxfsk(rxmode); - } else { // LoRa modem - rxlora(rxmode); - } - // the radio will go back to STANDBY mode as soon as the RX is finished - // or timed out, and the corresponding IRQ will inform us about completion. -} - -// get random seed from wideband noise rssi -void radio_init () { - hal_disableIRQs(); - - // manually reset radio -#ifdef CFG_sx1276_radio - hal_pin_rst(0); // drive RST pin low -#else - hal_pin_rst(1); // drive RST pin high -#endif - hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us - hal_pin_rst(2); // configure RST pin floating! - hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms - - opmode(OPMODE_SLEEP); - - // some sanity checks, e.g., read version number - u1_t v = readReg(RegVersion); -#ifdef CFG_sx1276_radio - ASSERT(v == 0x12 ); -#elif CFG_sx1272_radio - ASSERT(v == 0x22); -#else -#error Missing CFG_sx1272_radio/CFG_sx1276_radio -#endif - // seed 15-byte randomness via noise rssi - rxlora(RXMODE_RSSI); - while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx - for(int i=1; i<16; i++) { - for(int j=0; j<8; j++) { - u1_t b; // wait for two non-identical subsequent least-significant bits - while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); - randbuf[i] = (randbuf[i] << 1) | b; - } - } - randbuf[0] = 16; // set initial index - -#ifdef CFG_sx1276mb1_board - // chain calibration - writeReg(RegPaConfig, 0); - - // Launch Rx chain calibration for LF band - writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); - while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } - - // Sets a Frequency in HF band - u4_t frf = 868000000; - writeReg(RegFrfMsb, (u1_t)(frf>>16)); - writeReg(RegFrfMid, (u1_t)(frf>> 8)); - writeReg(RegFrfLsb, (u1_t)(frf>> 0)); - - // Launch Rx chain calibration for HF band - writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); - while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } -#endif /* CFG_sx1276mb1_board */ - - opmode(OPMODE_SLEEP); - - hal_enableIRQs(); -} - -// return next random byte derived from seed buffer -// (buf[0] holds index of next byte to be returned) -u1_t radio_rand1 () { - u1_t i = randbuf[0]; - ASSERT( i != 0 ); - if( i==16 ) { - os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key - i = 0; - } - u1_t v = randbuf[i++]; - randbuf[0] = i; - return v; -} - -u1_t radio_rssi () { - hal_disableIRQs(); - u1_t r = readReg(LORARegRssiValue); - hal_enableIRQs(); - return r; -} - -static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = { - [FSK] = us2osticks(0), // ( 0 ticks) - [SF7] = us2osticks(0), // ( 0 ticks) - [SF8] = us2osticks(1648), // ( 54 ticks) - [SF9] = us2osticks(3265), // ( 107 ticks) - [SF10] = us2osticks(7049), // ( 231 ticks) - [SF11] = us2osticks(13641), // ( 447 ticks) - [SF12] = us2osticks(31189), // (1022 ticks) -}; - -// called by hal ext IRQ handler -// (radio goes to stanby mode after tx/rx operations) -void radio_irq_handler (u1_t dio) { - ostime_t now = os_getTime(); - if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem - u1_t flags = readReg(LORARegIrqFlags); -#if LMIC_DEBUG_LEVEL > 1 - lmic_printf("%lu: irq: dio: 0x%x flags: 0x%x\n", now, dio, flags); -#endif - if( flags & IRQ_LORA_TXDONE_MASK ) { - // save exact tx time - LMIC.txend = now - us2osticks(43); // TXDONE FIXUP - } else if( flags & IRQ_LORA_RXDONE_MASK ) { - // save exact rx time - if(getBw(LMIC.rps) == BW125) { - now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps)); - } - LMIC.rxtime = now; - // read the PDU and inform the MAC that we received something - LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ? - readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); - // set FIFO read address pointer - writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); - // now read the FIFO - readBuf(RegFifo, LMIC.frame, LMIC.dataLen); - // read rx quality parameters - //LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 - LMIC.snr = ((s1_t)readReg(LORARegPktSnrValue)) / 4; - //LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63) - LMIC.rssi = readReg(LORARegPktRssiValue) - 157; // RFI_HF for 868 and 915MHZ band - if (LMIC.snr < 0) - LMIC.rssi += LMIC.snr; - } else if( flags & IRQ_LORA_RXTOUT_MASK ) { - // indicate timeout - LMIC.dataLen = 0; - } - // mask all radio IRQs - writeReg(LORARegIrqFlagsMask, 0xFF); - // clear radio IRQ flags - writeReg(LORARegIrqFlags, 0xFF); - } else { // FSK modem - u1_t flags1 = readReg(FSKRegIrqFlags1); - u1_t flags2 = readReg(FSKRegIrqFlags2); - if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { - // save exact tx time - LMIC.txend = now; - } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { - // save exact rx time - LMIC.rxtime = now; - // read the PDU and inform the MAC that we received something - LMIC.dataLen = readReg(FSKRegPayloadLength); - // now read the FIFO - readBuf(RegFifo, LMIC.frame, LMIC.dataLen); - // read rx quality parameters - LMIC.snr = 0; // determine snr - LMIC.rssi = 0; // determine rssi - } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { - // indicate timeout - LMIC.dataLen = 0; - } else { - ASSERT(0); - } - } - // go from stanby to sleep - opmode(OPMODE_SLEEP); - // run os job (use preset func ptr) - os_setCallback(&LMIC.osjob, LMIC.osjob.func); -} - -void os_radio (u1_t mode) { - hal_disableIRQs(); - switch (mode) { - case RADIO_RST: - // put radio to sleep - opmode(OPMODE_SLEEP); - break; - - case RADIO_TX: - // transmit frame now - starttx(); // buf=LMIC.frame, len=LMIC.dataLen - break; - - case RADIO_RX: - // receive frame now (exactly at rxtime) - startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms - break; - - case RADIO_RXON: - // start scanning for beacon now - startrx(RXMODE_SCAN); // buf=LMIC.frame - break; - } - hal_enableIRQs(); -} +/* + * Copyright (c) 2014-2016 IBM Corporation. + * Copyright (c) 2016-2018 MCCI Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LMIC_DR_LEGACY 0 + +#include "lmic.h" + +// ---------------------------------------- +// Registers Mapping +#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 LORARegFifoAddrPtr 0x0D +#define FSKRegRssiConfig 0x0E +#define LORARegFifoTxBaseAddr 0x0E +#define FSKRegRssiCollision 0x0F +#define LORARegFifoRxBaseAddr 0x0F +#define FSKRegRssiThresh 0x10 +#define LORARegFifoRxCurrentAddr 0x10 +#define FSKRegRssiValue 0x11 +#define LORARegIrqFlagsMask 0x11 +#define FSKRegRxBw 0x12 +#define LORARegIrqFlags 0x12 +#define FSKRegAfcBw 0x13 +#define LORARegRxNbBytes 0x13 +#define FSKRegOokPeak 0x14 +#define LORARegRxHeaderCntValueMsb 0x14 +#define FSKRegOokFix 0x15 +#define LORARegRxHeaderCntValueLsb 0x15 +#define FSKRegOokAvg 0x16 +#define LORARegRxPacketCntValueMsb 0x16 +#define LORARegRxpacketCntValueLsb 0x17 +#define LORARegModemStat 0x18 +#define LORARegPktSnrValue 0x19 +#define FSKRegAfcFei 0x1A +#define LORARegPktRssiValue 0x1A +#define FSKRegAfcMsb 0x1B +#define LORARegRssiValue 0x1B +#define FSKRegAfcLsb 0x1C +#define LORARegHopChannel 0x1C +#define FSKRegFeiMsb 0x1D +#define LORARegModemConfig1 0x1D +#define FSKRegFeiLsb 0x1E +#define LORARegModemConfig2 0x1E +#define FSKRegPreambleDetect 0x1F +#define LORARegSymbTimeoutLsb 0x1F +#define FSKRegRxTimeout1 0x20 +#define LORARegPreambleMsb 0x20 +#define FSKRegRxTimeout2 0x21 +#define LORARegPreambleLsb 0x21 +#define FSKRegRxTimeout3 0x22 +#define LORARegPayloadLength 0x22 +#define FSKRegRxDelay 0x23 +#define LORARegPayloadMaxLength 0x23 +#define FSKRegOsc 0x24 +#define LORARegHopPeriod 0x24 +#define FSKRegPreambleMsb 0x25 +#define LORARegFifoRxByteAddr 0x25 +#define LORARegModemConfig3 0x26 +#define FSKRegPreambleLsb 0x26 +#define FSKRegSyncConfig 0x27 +#define LORARegFeiMsb 0x28 +#define FSKRegSyncValue1 0x28 +#define LORAFeiMib 0x29 +#define FSKRegSyncValue2 0x29 +#define LORARegFeiLsb 0x2A +#define FSKRegSyncValue3 0x2A +#define FSKRegSyncValue4 0x2B +#define LORARegRssiWideband 0x2C +#define FSKRegSyncValue5 0x2C +#define FSKRegSyncValue6 0x2D +#define FSKRegSyncValue7 0x2E +#define FSKRegSyncValue8 0x2F +#define FSKRegPacketConfig1 0x30 +#define FSKRegPacketConfig2 0x31 +#define LORARegDetectOptimize 0x31 +#define FSKRegPayloadLength 0x32 +#define FSKRegNodeAdrs 0x33 +#define LORARegInvertIQ 0x33 +#define FSKRegBroadcastAdrs 0x34 +#define FSKRegFifoThresh 0x35 +#define FSKRegSeqConfig1 0x36 +#define FSKRegSeqConfig2 0x37 +#define LORARegDetectionThreshold 0x37 +#define FSKRegTimerResol 0x38 +#define FSKRegTimer1Coef 0x39 +#define LORARegSyncWord 0x39 +#define FSKRegTimer2Coef 0x3A +#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 +// #define RegAgcRef 0x43 // common +// #define RegAgcThresh1 0x44 // common +// #define RegAgcThresh2 0x45 // common +// #define RegAgcThresh3 0x46 // common +// #define RegPllHop 0x4B // common +#define RegPaDac 0x4D // common +// #define RegTcxo 0x58 // common +// #define RegPll 0x5C // common +// #define RegPllLowPn 0x5E // common +// #define RegFormerTemp 0x6C // common +// #define RegBitRateFrac 0x70 // common + +// ---------------------------------------- +// spread factors and mode for RegModemConfig2 +#define SX1272_MC2_FSK 0x00 +#define SX1272_MC2_SF7 0x70 +#define SX1272_MC2_SF8 0x80 +#define SX1272_MC2_SF9 0x90 +#define SX1272_MC2_SF10 0xA0 +#define SX1272_MC2_SF11 0xB0 +#define SX1272_MC2_SF12 0xC0 +// bandwidth for RegModemConfig1 +#define SX1272_MC1_BW_125 0x00 +#define SX1272_MC1_BW_250 0x40 +#define SX1272_MC1_BW_500 0x80 +// coding rate for RegModemConfig1 +#define SX1272_MC1_CR_4_5 0x08 +#define SX1272_MC1_CR_4_6 0x10 +#define SX1272_MC1_CR_4_7 0x18 +#define SX1272_MC1_CR_4_8 0x20 +#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive +#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 +#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 +// transmit power configuration for RegPaConfig +#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 +#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 + + +// sx1276 RegModemConfig1 +#define SX1276_MC1_BW_125 0x70 +#define SX1276_MC1_BW_250 0x80 +#define SX1276_MC1_BW_500 0x90 +#define SX1276_MC1_CR_4_5 0x02 +#define SX1276_MC1_CR_4_6 0x04 +#define SX1276_MC1_CR_4_7 0x06 +#define SX1276_MC1_CR_4_8 0x08 + +#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 + +// sx1276 RegModemConfig2 +#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 + +// sx1276 RegModemConfig3 +#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 +#define SX1276_MC3_AGCAUTO 0x04 + +// preamble for lora networks (nibbles swapped) +#define LORA_MAC_PREAMBLE 0x34 + +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A +#ifdef CFG_sx1276_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 +#elif CFG_sx1272_radio +#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 +#endif + +//----------------------------------------- +// 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 2.5.2 (but note that we ought to ask Semtech to confirm, because +// datasheet is unclear). +#define SX127X_RX_POWER_UP us2osticks(500) // delay this long to let the receiver power up. + +// ---------------------------------------- +// Constants for radio registers +#define OPMODE_LORA 0x80 +#define OPMODE_MASK 0x07 +#define OPMODE_SLEEP 0x00 +#define OPMODE_STANDBY 0x01 +#define OPMODE_FSTX 0x02 +#define OPMODE_TX 0x03 +#define OPMODE_FSRX 0x04 +#define OPMODE_RX 0x05 +#define OPMODE_RX_SINGLE 0x06 +#define OPMODE_CAD 0x07 + +// ---------------------------------------- +// Bits masking the corresponding IRQs from the radio +#define IRQ_LORA_RXTOUT_MASK 0x80 +#define IRQ_LORA_RXDONE_MASK 0x40 +#define IRQ_LORA_CRCERR_MASK 0x20 +#define IRQ_LORA_HEADER_MASK 0x10 +#define IRQ_LORA_TXDONE_MASK 0x08 +#define IRQ_LORA_CDDONE_MASK 0x04 +#define IRQ_LORA_FHSSCH_MASK 0x02 +#define IRQ_LORA_CDDETD_MASK 0x01 + +#define IRQ_FSK1_MODEREADY_MASK 0x80 +#define IRQ_FSK1_RXREADY_MASK 0x40 +#define IRQ_FSK1_TXREADY_MASK 0x20 +#define IRQ_FSK1_PLLLOCK_MASK 0x10 +#define IRQ_FSK1_RSSI_MASK 0x08 +#define IRQ_FSK1_TIMEOUT_MASK 0x04 +#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 +#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 +#define IRQ_FSK2_FIFOFULL_MASK 0x80 +#define IRQ_FSK2_FIFOEMPTY_MASK 0x40 +#define IRQ_FSK2_FIFOLEVEL_MASK 0x20 +#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 +#define IRQ_FSK2_PACKETSENT_MASK 0x08 +#define IRQ_FSK2_PAYLOADREADY_MASK 0x04 +#define IRQ_FSK2_CRCOK_MASK 0x02 +#define IRQ_FSK2_LOWBAT_MASK 0x01 + +// ---------------------------------------- +// DIO function mappings D0D1D2D3 +#define MAP_DIO0_LORA_RXDONE 0x00 // 00------ +#define MAP_DIO0_LORA_TXDONE 0x40 // 01------ +#define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- +#define MAP_DIO1_LORA_NOP 0x30 // --11---- +#define MAP_DIO2_LORA_NOP 0xC0 // ----11-- + +#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) +#define MAP_DIO1_FSK_NOP 0x30 // --11---- +#define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- +#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- + + +// FSK IMAGECAL defines +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#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]; + + +#ifdef CFG_sx1276_radio +#define LNA_RX_GAIN (0x20|0x1) +#elif CFG_sx1272_radio +#define LNA_RX_GAIN (0x20|0x03) +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + + +static void writeReg (u1_t addr, u1_t data ) { + hal_pin_nss(0); + hal_spi(addr | 0x80); + hal_spi(data); + hal_pin_nss(1); +} + +static u1_t readReg (u1_t addr) { + hal_pin_nss(0); + hal_spi(addr & 0x7F); + u1_t val = hal_spi(0x00); + hal_pin_nss(1); + return val; +} + +static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { + hal_pin_nss(0); + hal_spi(addr | 0x80); + for (u1_t i=0; i>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); +} + + + +static void configPower () { +#ifdef CFG_sx1276_radio + // PA_BOOST output is assumed but not 20 dBm. + s1_t pw = (s1_t)LMIC.txpow; + if(pw > 17) { + pw = 17; + } else if(pw < 2) { + pw = 2; + } + // 0x80 forces use of PA_BOOST; but we don't + // turn on 20 dBm mode. So powers are: + // 0000 => 2dBm, 0001 => 3dBm, ... 1111 => 17dBm + // But we also enforce that the high-power mode + // is off by writing RegPaDac. + writeReg(RegPaConfig, (u1_t)(0x80|(pw - 2))); + writeReg(RegPaDac, readReg(RegPaDac)|0x4); + +#elif CFG_sx1272_radio + // set PA config (2-17 dBm using PA_BOOST) + s1_t pw = (s1_t)LMIC.txpow; + if(pw > 17) { + pw = 17; + } else if(pw < 2) { + pw = 2; + } + writeReg(RegPaConfig, (u1_t)(0x80|(pw-2))); +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif /* CFG_sx1272_radio */ +} + +static void txfsk () { + // select FSK modem (from sleep mode) + writeReg(RegOpMode, 0x10); // FSK, BT=0.5 + ASSERT(readReg(RegOpMode) == 0x10); + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // 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); + writeReg(FSKRegSyncValue1, 0xC1); + writeReg(FSKRegSyncValue2, 0x94); + writeReg(FSKRegSyncValue3, 0xC1); + // configure frequency + configChannel(); + // configure output power + configPower(); + + // 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 + writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) + + // download length byte and buffer to the radio FIFO + writeReg(RegFifo, LMIC.dataLen); + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + opmode(OPMODE_TX); +} + +static void txlora () { + // select LoRa modem (from sleep mode) + //writeReg(RegOpMode, OPMODE_LORA); + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + + // enter standby mode (required for FIFO loading)) + opmode(OPMODE_STANDBY); + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + // configure output power + writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec + configPower(); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // mask all IRQs but TxDone + writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); + + // initialize the payload size and address pointers + writeReg(LORARegFifoTxBaseAddr, 0x00); + writeReg(LORARegFifoAddrPtr, 0x00); + writeReg(LORARegPayloadLength, LMIC.dataLen); + + // download buffer to the radio FIFO + writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); + + // enable antenna switch for TX + hal_pin_rxtx(1); + + // now we actually start the transmission + opmode(OPMODE_TX); + +#if LMIC_DEBUG_LEVEL > 0 + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), LMIC.freq, LMIC.dataLen, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); +#endif +} + +// start transmitter (buf=LMIC.frame, len=LMIC.dataLen) +static void starttx () { + u1_t const rOpMode = readReg(RegOpMode); + + // originally, this code ASSERT()ed, but asserts are both bad and + // blunt instruments. If we see that we're not in sleep mode, + // force sleep (because we might have to switch modes) + if ((rOpMode & OPMODE_MASK) != OPMODE_SLEEP) { +#if LMIC_DEBUG_LEVEL > 0 + LMIC_DEBUG_PRINTF("?%s: OPMODE != OPMODE_SLEEP: %#02x\n", __func__, rOpMode); +#endif + opmode(OPMODE_SLEEP); + hal_waitUntil(os_getTime() + ms2osticks(1)); + } + + if (LMIC.lbt_ticks > 0) { + oslmic_radio_rssi_t rssi; + radio_monitor_rssi(LMIC.lbt_ticks, &rssi); +#if LMIC_X_DEBUG_LEVEL > 0 + LMIC_X_DEBUG_PRINTF("LBT rssi max:min=%d:%d %d times in %d\n", rssi.max_rssi, rssi.min_rssi, rssi.n_rssi, LMIC.lbt_ticks); +#endif + + if (rssi.max_rssi >= LMIC.lbt_dbmax) { + // complete the request by scheduling the job + os_setCallback(&LMIC.osjob, LMIC.osjob.func); + return; + } + } + + if(getSf(LMIC.rps) == FSK) { // FSK modem + txfsk(); + } else { // LoRa modem + txlora(); + } + // the radio will go back to STANDBY mode as soon as the TX is finished + // the corresponding IRQ will inform us about completion. +} + +enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; + +static CONST_TABLE(u1_t, rxlorairqmask)[] = { + [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, + [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, + [RXMODE_RSSI] = 0x00, +}; + +// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) +static void rxlora (u1_t rxmode) { + // select LoRa modem (from sleep mode) + opmodeLora(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // don't use MAC settings at startup + if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan + writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); + writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); + } else { // single or continuous rx mode + // configure LoRa modem (cfg1, cfg2) + configLoraModem(); + // configure frequency + configChannel(); + } + // set LNA gain + writeReg(RegLna, LNA_RX_GAIN); + // set max payload size + writeReg(LORARegPayloadMaxLength, 64); +#if !defined(DISABLE_INVERT_IQ_ON_RX) + // use inverted I/Q signal (prevent mote-to-mote communication) + + // XXX: use flag to switch on/off inversion + if (LMIC.noRXIQinversion) { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6)); + } else { + writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); + } +#endif + // set symbol timeout (for single rx) + writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms); + // set sync word + writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); + + // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP + writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); + // clear all radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + // enable required radio IRQs + writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode)); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + // now instruct the radio to receive + if (rxmode == RXMODE_SINGLE) { // single rx + hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX_SINGLE); +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now = os_getTime(); + LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %lu\n", now - LMIC.rxtime); +#endif + } else { // continous rx (scan or rssi) + opmode(OPMODE_RX); + } + +#if LMIC_DEBUG_LEVEL > 0 + if (rxmode == RXMODE_RSSI) { + LMIC_DEBUG_PRINTF("RXMODE_RSSI\n"); + } else { + u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 + u1_t bw = getBw(LMIC.rps); + u1_t cr = getCr(LMIC.rps); + LMIC_DEBUG_PRINTF("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n", + os_getTime(), + rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), + LMIC.freq, sf, + bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), + cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), + getIh(LMIC.rps) + ); + } +#endif +} + +static void rxfsk (u1_t rxmode) { + // only single rx (no continuous scanning, no noise sampling) + ASSERT( rxmode == RXMODE_SINGLE ); + // select FSK modem (from sleep mode) + //writeReg(RegOpMode, 0x00); // (not LoRa) + opmodeFSK(); + ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); + // enter standby mode (warm up)) + opmode(OPMODE_STANDBY); + // configure frequency + configChannel(); + // set LNA gain + //writeReg(RegLna, 0x20|0x03); // max gain, boost enable + writeReg(RegLna, LNA_RX_GAIN); + // configure receiver + writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? + // set receiver bandwidth + writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb + // set AFC bandwidth + 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); + + // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut + writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); + + // enable antenna switch for RX + hal_pin_rxtx(0); + + // now instruct the radio to receive + hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time + opmode(OPMODE_RX); // no single rx mode available in FSK +} + +static void startrx (u1_t rxmode) { + ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); + if(getSf(LMIC.rps) == FSK) { // FSK modem + rxfsk(rxmode); + } else { // LoRa modem + rxlora(rxmode); + } + // the radio will go back to STANDBY mode as soon as the RX is finished + // or timed out, and the corresponding IRQ will inform us about completion. +} + +// get random seed from wideband noise rssi +int radio_init () { + hal_disableIRQs(); + + // manually reset radio +#ifdef CFG_sx1276_radio + hal_pin_rst(0); // drive RST pin low +#else + hal_pin_rst(1); // drive RST pin high +#endif + hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us + hal_pin_rst(2); // configure RST pin floating! + hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms + + opmode(OPMODE_SLEEP); + + // some sanity checks, e.g., read version number + u1_t v = readReg(RegVersion); +#ifdef CFG_sx1276_radio + if(v != 0x12 ) + return 0; +#elif CFG_sx1272_radio + if(v != 0x22) + return 0; +#else +#error Missing CFG_sx1272_radio/CFG_sx1276_radio +#endif + // seed 15-byte randomness via noise rssi + rxlora(RXMODE_RSSI); + while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx + for(int i=1; i<16; i++) { + for(int j=0; j<8; j++) { + u1_t b; // wait for two non-identical subsequent least-significant bits + while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); + randbuf[i] = (randbuf[i] << 1) | b; + } + } + randbuf[0] = 16; // set initial index + +#ifdef CFG_sx1276mb1_board + // chain calibration + writeReg(RegPaConfig, 0); + + // Launch Rx chain calibration for LF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } + + // Sets a Frequency in HF band + u4_t frf = 868000000; + writeReg(RegFrfMsb, (u1_t)(frf>>16)); + writeReg(RegFrfMid, (u1_t)(frf>> 8)); + writeReg(RegFrfLsb, (u1_t)(frf>> 0)); + + // Launch Rx chain calibration for HF band + writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); + while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } +#endif /* CFG_sx1276mb1_board */ + + opmode(OPMODE_SLEEP); + + hal_enableIRQs(); + return 1; +} + +// return next random byte derived from seed buffer +// (buf[0] holds index of next byte to be returned) +u1_t radio_rand1 () { + u1_t i = randbuf[0]; + ASSERT( i != 0 ); + if( i==16 ) { + os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key + i = 0; + } + u1_t v = randbuf[i++]; + randbuf[0] = i; + return v; +} + +u1_t radio_rssi () { + hal_disableIRQs(); + u1_t r = readReg(LORARegRssiValue); + hal_enableIRQs(); + return r; +} + +// monitor rssi for specified number of ostime_t ticks, and return statistics +// This puts the radio into RX continuous mode, waits long enough for the +// oscillators to start and the PLL to lock, and then measures for the specified +// period of time. The radio is then returned to idle. +// +// RSSI returned is expressed in units of dB, and is offset according to the +// current radio setting per section 5.5.5 of Semtech 1276 datasheet. +void radio_monitor_rssi(ostime_t nTicks, oslmic_radio_rssi_t *pRssi) { + uint8_t rssiMax, rssiMin; + uint16_t rssiSum; + uint16_t rssiN; + + int rssiAdjust; + ostime_t tBegin; + int notDone; + + rxlora(RXMODE_SCAN); + + // while we're waiting for the PLLs to spin up, determine which + // band we're in and choose the base RSSI. + if (LMIC.freq > SX127X_FREQ_LF_MAX) { + rssiAdjust = SX127X_RSSI_ADJUST_HF; + } else { + rssiAdjust = SX127X_RSSI_ADJUST_LF; + } + rssiAdjust += hal_getRssiCal(); + + // zero the results + rssiMax = 255; + rssiMin = 0; + rssiSum = 0; + rssiN = 0; + + // wait for PLLs + hal_waitUntil(os_getTime() + SX127X_RX_POWER_UP); + + // scan for the desired time. + tBegin = os_getTime(); + rssiMax = 0; + + /* XXX(tanupoo) + * In this loop, micros() in os_getTime() returns a past time sometimes. + * At least, it happens on Dragino LoRa Mini. + * the return value of micros() looks not to be stable in IRQ disabled. + * Once it happens, this loop never exit infinitely. + * In order to prevent it, it enables IRQ before calling os_getTime(), + * disable IRQ again after that. + */ + do { + ostime_t now; + + u1_t rssiNow = readReg(LORARegRssiValue); + + if (rssiMax < rssiNow) + rssiMax = rssiNow; + if (rssiNow < rssiMin) + rssiMin = rssiNow; + rssiSum += rssiNow; + ++rssiN; + // TODO(tmm@mcci.com) move this to os_getTime(). + hal_enableIRQs(); + now = os_getTime(); + hal_disableIRQs(); + notDone = now - (tBegin + nTicks) < 0; + } while (notDone); + + // put radio back to sleep + opmode(OPMODE_SLEEP); + + // compute the results + pRssi->max_rssi = (s2_t) (rssiMax + rssiAdjust); + pRssi->min_rssi = (s2_t) (rssiMin + rssiAdjust); + pRssi->mean_rssi = (s2_t) (rssiAdjust + ((rssiSum + (rssiN >> 1)) / rssiN)); + pRssi->n_rssi = rssiN; +} + +static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = { + [FSK] = us2osticks(0), // ( 0 ticks) + [SF7] = us2osticks(0), // ( 0 ticks) + [SF8] = us2osticks(1648), // ( 54 ticks) + [SF9] = us2osticks(3265), // ( 107 ticks) + [SF10] = us2osticks(7049), // ( 231 ticks) + [SF11] = us2osticks(13641), // ( 447 ticks) + [SF12] = us2osticks(31189), // (1022 ticks) +}; + +// called by hal ext IRQ handler +// (radio goes to stanby mode after tx/rx operations) +void radio_irq_handler (u1_t dio) { + radio_irq_handler_v2(dio, os_getTime()); +} + +void radio_irq_handler_v2 (u1_t dio, ostime_t now) { +#if CFG_TxContinuousMode + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + u1_t p = readReg(LORARegFifoAddrPtr); + writeReg(LORARegFifoAddrPtr, 0x00); + u1_t s = readReg(RegOpMode); + u1_t c = readReg(LORARegModemConfig2); + opmode(OPMODE_TX); + return; +#else /* ! CFG_TxContinuousMode */ + +#if LMIC_DEBUG_LEVEL > 0 + ostime_t const entry = now; +#endif + if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem + u1_t flags = readReg(LORARegIrqFlags); + LMIC_X_DEBUG_PRINTF("IRQ=%02x\n", flags); + if( flags & IRQ_LORA_TXDONE_MASK ) { + // save exact tx time + LMIC.txend = now - us2osticks(43); // TXDONE FIXUP + } else if( flags & IRQ_LORA_RXDONE_MASK ) { + // save exact rx time + if(getBw(LMIC.rps) == BW125) { + now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps)); + } + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ? + readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); + // set FIFO read address pointer + writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 + LMIC.rssi = readReg(LORARegPktRssiValue); + LMIC_X_DEBUG_PRINTF("RX snr=%u rssi=%d\n", LMIC.snr/4, SX127X_RSSI_ADJUST_HF + LMIC.rssi); + LMIC.rssi = LMIC.rssi - 125 + 64; // RSSI [dBm] (-196...+63) + } else if( flags & IRQ_LORA_RXTOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; +#if LMIC_DEBUG_LEVEL > 0 + ostime_t now2 = os_getTime(); + LMIC_DEBUG_PRINTF("rxtimeout: entry: %lu rxtime: %lu entry-rxtime: %lu now-entry: %lu rxtime-txend: %lu\n", entry, LMIC.rxtime, entry - LMIC.rxtime, now2 - entry, LMIC.rxtime-LMIC.txend); +#endif + } + // mask all radio IRQs + writeReg(LORARegIrqFlagsMask, 0xFF); + // clear radio IRQ flags + writeReg(LORARegIrqFlags, 0xFF); + } else { // FSK modem + u1_t flags1 = readReg(FSKRegIrqFlags1); + u1_t flags2 = readReg(FSKRegIrqFlags2); + if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { + // save exact tx time + LMIC.txend = now; + } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { + // save exact rx time + LMIC.rxtime = now; + // read the PDU and inform the MAC that we received something + LMIC.dataLen = readReg(FSKRegPayloadLength); + // now read the FIFO + readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read rx quality parameters + LMIC.snr = 0; // determine snr + LMIC.rssi = 0; // determine rssi + } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { + // indicate timeout + LMIC.dataLen = 0; + } else { + ASSERT(0); + } + } + // go from stanby to sleep + opmode(OPMODE_SLEEP); + // run os job (use preset func ptr) + os_setCallback(&LMIC.osjob, LMIC.osjob.func); +#endif /* ! CFG_TxContinuousMode */ +} + +void os_radio (u1_t mode) { + hal_disableIRQs(); + switch (mode) { + case RADIO_RST: + // put radio to sleep + opmode(OPMODE_SLEEP); + break; + + case RADIO_TX: + // transmit frame now + starttx(); // buf=LMIC.frame, len=LMIC.dataLen + break; + + case RADIO_RX: + // receive frame now (exactly at rxtime) + startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms + break; + + case RADIO_RXON: + // start scanning for beacon now + startrx(RXMODE_SCAN); // buf=LMIC.frame + break; + } + hal_enableIRQs(); +} diff --git a/platformio.ini b/platformio.ini index e23b7612..e0ccb21c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,9 @@ [platformio] env_default = generic ;env_default = ebox +;env_default = eboxtube ;env_default = heltec +;env_default = heltecv2 ;env_default = ttgov1 ;env_default = ttgov2 ;env_default = ttgov21old @@ -21,13 +23,13 @@ env_default = generic ;env_default = lolin32lora ;env_default = lolin32lite ;env_default = octopus32 -;env_default = ebox, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new +;env_default = ebox, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new ; description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around. [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.6.1 +release_version = 1.6.51 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 0 @@ -41,6 +43,8 @@ board_build.partitions = min_spiffs.csv monitor_speed = 115200 lib_deps_all = ArduinoJson@^5.13.1 +lib_deps_lora = +; MCCI LoRaWAN LMIC library@^2.2.2 lib_deps_display = U8g2@>=2.23.16 lib_deps_rgbled = @@ -49,11 +53,10 @@ lib_deps_gps = TinyGPSPlus@>=1.0.2 Time@>=1.5 build_flags = -; override lora settings from LMiC library in lmic/config.h and use main.h instead - -D_lmic_config_h_ - -include "src/paxcounter.conf" -include "src/hal/${PIOENV}.h" + -include "src/paxcounter.conf" -w + '-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h' '-DCORE_DEBUG_LEVEL=${common.debug_level}' '-DBINTRAY_PACKAGE="${PIOENV}"' '-DPROGVERSION="${common.release_version}"' @@ -61,11 +64,27 @@ build_flags = [env:ebox] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 115200 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} +build_flags = + ${common.build_flags} +upload_protocol = ${common.upload_protocol} +extra_scripts = ${common.extra_scripts} +monitor_speed = ${common.monitor_speed} + +[env:eboxtube] +platform = ${common.platform_espressif32} +framework = arduino +board = heltec_wifi_lora_32 +board_build.partitions = ${common.board_build.partitions} +upload_speed = 115200 +lib_deps = + ${common.lib_deps_all} + ${common.lib_deps_lora} build_flags = ${common.build_flags} upload_protocol = ${common.upload_protocol} @@ -80,6 +99,23 @@ board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} + ${common.lib_deps_display} +build_flags = + ${common.build_flags} +upload_protocol = ${common.upload_protocol} +extra_scripts = ${common.extra_scripts} +monitor_speed = ${common.monitor_speed} + +[env:heltecv2] +platform = ${common.platform_espressif32} +framework = arduino +board = heltec_wifi_lora_32 +board_build.partitions = ${common.board_build.partitions} +upload_speed = 921600 +lib_deps = + ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = ${common.build_flags} @@ -90,11 +126,12 @@ monitor_speed = ${common.monitor_speed} [env:ttgov1] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = ttgo-lora32-v1 board_build.partitions = ${common.board_build.partitions} upload_speed = 115200 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = ${common.build_flags} @@ -105,11 +142,12 @@ monitor_speed = ${common.monitor_speed} [env:ttgov2] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = ttgo-lora32-v1 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = ${common.build_flags} @@ -120,11 +158,12 @@ monitor_speed = ${common.monitor_speed} [env:ttgov21old] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = ${common.build_flags} @@ -135,11 +174,12 @@ monitor_speed = ${common.monitor_speed} [env:ttgov21new] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = ${common.build_flags} @@ -150,11 +190,12 @@ monitor_speed = ${common.monitor_speed} [env:ttgobeam] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_gps} build_flags = ${common.build_flags} @@ -166,11 +207,12 @@ monitor_speed = ${common.monitor_speed} [env:fipy] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} build_flags = ${common.build_flags} @@ -181,11 +223,12 @@ monitor_speed = ${common.monitor_speed} [env:lopy] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} ${common.lib_deps_gps} build_flags = @@ -197,11 +240,12 @@ monitor_speed = ${common.monitor_speed} [env:lopy4] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} ${common.lib_deps_gps} build_flags = @@ -219,6 +263,7 @@ board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} build_flags = ${common.build_flags} @@ -234,6 +279,7 @@ board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} build_flags = ${common.build_flags} @@ -259,12 +305,14 @@ monitor_speed = ${common.monitor_speed} [env:octopus32] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = featheresp32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} + ${common.lib_deps_display} build_flags = ${common.build_flags} upload_protocol = ${common.upload_protocol} @@ -274,11 +322,12 @@ monitor_speed = ${common.monitor_speed} [env:generic] platform = ${common.platform_espressif32} framework = arduino -board = esp32dev +board = heltec_wifi_lora_32 board_build.partitions = ${common.board_build.partitions} upload_speed = 921600 lib_deps = ${common.lib_deps_all} + ${common.lib_deps_lora} ${common.lib_deps_rgbled} ${common.lib_deps_gps} ${common.lib_deps_display} diff --git a/src/display.cpp b/src/display.cpp index 842f0249..5e35f871 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -4,7 +4,7 @@ #include "globals.h" #include // needed for reading ESP32 chip attributes -HAS_DISPLAY u8x8(OLED_RST, I2C_SCL, I2C_SDA); +HAS_DISPLAY u8x8(MY_OLED_RST, MY_OLED_SCL, MY_OLED_SDA); // helper string for converting LoRa spread factor values #if defined(CFG_eu868) diff --git a/src/gps.cpp b/src/gpsread.cpp similarity index 100% rename from src/gps.cpp rename to src/gpsread.cpp diff --git a/src/hal/ebox.h b/src/hal/ebox.h index 6b8d0016..1f6fe3ad 100644 --- a/src/hal/ebox.h +++ b/src/hal/ebox.h @@ -1,21 +1,26 @@ +#ifndef _EBOX_H +#define _EBOX_H + +#include + // Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 -#define HAS_LED GPIO_NUM_23 // blue LED on board -#define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board +#define HAS_LED (23) // blue LED on board +#define HAS_BUTTON (0) // button "PROG" on board #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN -// non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/hal/eboxtube.h b/src/hal/eboxtube.h index 9c66dbc3..858b7b16 100644 --- a/src/hal/eboxtube.h +++ b/src/hal/eboxtube.h @@ -1,22 +1,27 @@ +#ifndef _EBOXTUBE_H +#define _EBOXTUBE_H + +#include + // Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 -#define HAS_LED GPIO_NUM_22 // Green LED on board -#define HAS_RGB_LED GPIO_NUM_2 // WS2812B RGB LED on board -#define HAS_BUTTON GPIO_NUM_0 // button "FLASH" on board +#define HAS_LED (22) // Green LED on board +#define HAS_RGB_LED (2) // WS2812B RGB LED on board +#define HAS_BUTTON (0) // button "FLASH" on board #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 (32) -// non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/hal/fipy.h b/src/hal/fipy.h index d5153b98..173c1bee 100644 --- a/src/hal/fipy.h +++ b/src/hal/fipy.h @@ -1,3 +1,8 @@ +#ifndef _FIPY_H +#define _FIPY_H + +#include + // Hardware related definitions for Pycom FiPy Board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -8,16 +13,18 @@ #define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 #define BOARD_HAS_PSRAM // use extra 4MB extern RAM -// Hardware pin definitions for Pycom FiPy board -#define PIN_SPI_SS GPIO_NUM_18 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST LMIC_UNUSED_PIN -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // workaround -#define DIO2 LMIC_UNUSED_PIN +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (23) // LoRa IRQ +#define LORA_IO1 (23) // Pin tied via diode to DIO0 +#define LORA_IO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) #define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna #define WIFI_ANTENNA 0 // 0 = internal, 1 = external + +#endif diff --git a/src/hal/generic.h b/src/hal/generic.h index f25a8219..7e2256df 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -1,3 +1,8 @@ +#ifndef _GENERIC_H +#define _GENERIC_H + +#include + // Hardware related definitions for generic ESP32 boards #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa @@ -13,30 +18,32 @@ #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board -#define HAS_LED GPIO_NUM_21 // on board LED -#define HAS_BUTTON GPIO_NUM_39 // on board button -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 +#define HAS_LED (21) // on board LED +#define HAS_BUTTON (39) // on board button +#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 #define BOARD_HAS_PSRAM // use extra 4MB extern RAM #define HAS_GPS 1 // use if board has GPS #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration -// pin definitions for SPI interface of LoRa chip -#define PIN_SPI_SS GPIO_NUM_18 // SPI Chip Select -#define PIN_SPI_MOSI GPIO_NUM_27 // SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // SPI Clock -#define RST LMIC_UNUSED_PIN // LoRa Reset (if wired) -#define DIO0 GPIO_NUM_26 // LoRa IO0 -#define DIO1 GPIO_NUM_32 // LoRa IO1 -#define DIO2 LMIC_UNUSED_PIN // LoRa IO2 (not needed) +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) +#define MY_OLED_RST (16) -// pin definitions for I2C interface of OLED Display -#define OLED_RST GPIO_NUM_16 // SSD1306 RST -#define I2C_SDA GPIO_NUM_4 // SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_15 // SD1306 D0 +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN // I2C config for Microchip 24AA02E64 DEVEUI unique address #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 -#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits \ No newline at end of file +#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits + +#endif \ No newline at end of file diff --git a/src/hal/heltec.h b/src/hal/heltec.h index 44316c63..e33f1e34 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -1,3 +1,8 @@ +#ifndef _HELTEC_H +#define _HELTEC_H + +#include + // Hardware related definitions for Heltec LoRa-32 Board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -5,22 +10,22 @@ #define CFG_sx1276_radio 1 #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board -#define HAS_LED GPIO_NUM_25 // white LED on board -#define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board +#define HAS_LED (25) // white LED on board +#define HAS_BUTTON (0) // button "PROG" on board -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) +#define MY_OLED_RST (16) -// non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN -// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display -#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 RST -#define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 +#endif diff --git a/src/hal/heltecv2.h b/src/hal/heltecv2.h index 99eb6ef9..a97a6d3c 100644 --- a/src/hal/heltecv2.h +++ b/src/hal/heltecv2.h @@ -1,3 +1,8 @@ +#ifndef _HELTECV2_H +#define _HELTECV2_H + +#include + // Hardware related definitions for Heltec V2 LoRa-32 Board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -5,22 +10,22 @@ #define CFG_sx1276_radio 1 #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board -#define HAS_LED GPIO_NUM_25 // white LED on board -#define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board +#define HAS_LED (25) // white LED on board +#define HAS_BUTTON (0) // button "PROG" on board -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for I2C interface of OLED Display +#define OLED_SDA (4) +#define OLED_SCL (15) +#define OLED_RST (16) -// non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO18 -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_34 // ESP32 GPIO33 -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 GPIO_NUM_35 // 32 ESP32 GPIO32 -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (34) +#define LORA_IO2 (35) -// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display -#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 -- SD1306 RST -#define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 -- SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 -- SD1306 D0 \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/hal/lolin32lite.h b/src/hal/lolin32lite.h index 7b400cf5..9e9f8551 100644 --- a/src/hal/lolin32lite.h +++ b/src/hal/lolin32lite.h @@ -1,3 +1,8 @@ +#ifndef _LOLINLITE_H +#define _LOLINLITE_H + +#include + // Hardware related definitions for lolin32lite (without LoRa shield) #define CFG_sx1272_radio 1 // dummy @@ -5,4 +10,6 @@ #define HAS_LED 22 // on board LED on GPIO22 #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW -#define HAS_SPI 1 // comment out if device shall not send data via SPI \ No newline at end of file +#define HAS_SPI 1 // comment out if device shall not send data via SPI + +#endif \ No newline at end of file diff --git a/src/hal/lolin32litelora.h b/src/hal/lolin32litelora.h index 6427dbcd..cdb925f9 100644 --- a/src/hal/lolin32litelora.h +++ b/src/hal/lolin32litelora.h @@ -1,3 +1,8 @@ +#ifndef _LOLINLITELORA_H +#define _LOLINLITELORA_H + +#include + // Hardware related definitions for lolin32 lite with loraNode32 shield // See https://github.com/hallard/LoLin32-Lite-Lora @@ -16,24 +21,23 @@ #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 // RFM95 module -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (25) +#define LORA_IO0 (27) +#define LORA_IO1 (26) +#define LORA_IO2 LMIC_UNUSED_PIN -// non arduino pin definitions -#define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) -#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) - -// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display -#define OLED_RST U8X8_PIN_NONE // Not reset pin -#define I2C_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA -#define I2C_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (14) +#define MY_OLED_SCL (12) +#define MY_OLED_RST U8X8_PIN_NONE // I2C config for Microchip 24AA02E64 DEVEUI unique address #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits + +#endif diff --git a/src/hal/lolin32lora.h b/src/hal/lolin32lora.h index 7964a561..9d2d5af6 100644 --- a/src/hal/lolin32lora.h +++ b/src/hal/lolin32lora.h @@ -1,3 +1,8 @@ +#ifndef _LOLINLORA_H +#define _LOLINLORA_H + +#include + // Hardware related definitions for lolin32 with loraNode32 shield // See https://github.com/hallard/LoLin32-Lora @@ -17,24 +22,24 @@ #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 // RFM95 module -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (25) +#define LORA_IO0 (27) +#define LORA_IO1 (26) +#define LORA_IO2 LMIC_UNUSED_PIN +#define LORA_IO5 LMIC_UNUSED_PIN -// non arduino pin definitions -#define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) -#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) - -// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display -#define OLED_RST U8X8_PIN_NONE // Not reset pin -#define I2C_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA -#define I2C_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE // I2C config for Microchip 24AA02E64 DEVEUI unique address #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits + +#endif diff --git a/src/hal/lopy.h b/src/hal/lopy.h index b4a6a796..bf759ec8 100644 --- a/src/hal/lopy.h +++ b/src/hal/lopy.h @@ -1,23 +1,28 @@ +#ifndef _LOPY_H +#define _LOPY_H + +#include + // Hardware related definitions for Pycom LoPy Board (NOT LoPy4) #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1272_radio 1 #define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 +#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 -// Hardware pin definitions for Pycom LoPy board -#define PIN_SPI_SS GPIO_NUM_17 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST GPIO_NUM_18 -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // Pin tied via diode to DIO0 -#define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0 +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) // GPIO5 - SX1276 SCK +#define LORA_CS (17) // GPIO17 - SX1276 CS +#define LORA_MISO (19) // GPIO19 - SX1276 MISO +#define LORA_MOSI (27) // GPIO27 - SX1276 MOSI +#define LORA_RST (18) // GPIO18 - SX1276 RESET +#define LORA_IO0 (23) // LoRa IRQ +#define LORA_IO1 (23) // Pin tied via diode to DIO0 +#define LORA_IO2 (23) // Pin tied via diode to DIO0 // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH GPIO_NUM_16 // pin for switching wifi antenna +#define HAS_ANTENNA_SWITCH (16) // pin for switching wifi antenna #define WIFI_ANTENNA 0 // 0 = internal, 1 = external // uncomment this only if your LoPy runs on a PYTRACK BOARD @@ -26,10 +31,12 @@ //#define GPS_ADDR 0x10 // uncomment this only if your LoPy runs on a EXPANSION BOARD -//#define HAS_LED GPIO_NUM_12 // use if LoPy is on Expansion Board, this has a user LED +//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED //#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED -//#define HAS_BUTTON GPIO_NUM_13 // user button on expansion board +//#define HAS_BUTTON (13) // user button on expansion board //#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown //#define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 //#define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0 //#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0 + +#endif diff --git a/src/hal/lopy4.h b/src/hal/lopy4.h index db98eba9..79e80a36 100644 --- a/src/hal/lopy4.h +++ b/src/hal/lopy4.h @@ -1,24 +1,29 @@ +#ifndef _LOPY4_H +#define _LOPY4_H + +#include + // Hardware related definitions for Pycom LoPy4 Board #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 //#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 (P2) +#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2) #define BOARD_HAS_PSRAM // use extra 4MB extern RAM -// Hardware pin definitions for Pycom LoPy4 board -#define PIN_SPI_SS GPIO_NUM_18 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST LMIC_UNUSED_PIN -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // Pin tied via diode to DIO0 -#define DIO2 GPIO_NUM_23 // Pin tied via diode to DIO0 +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (23) // LoRa IRQ +#define LORA_IO1 (23) // Pin tied via diode to DIO0 +#define LORA_IO2 (23) // Pin tied via diode to DIO0 // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna (P12) +#define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12) #define WIFI_ANTENNA 0 // 0 = internal, 1 = external // uncomment this only if your LoPy runs on a PYTRACK BOARD @@ -27,10 +32,12 @@ //#define GPS_ADDR 0x10 // uncomment this only if your LoPy runs on a EXPANSION BOARD -#define HAS_LED GPIO_NUM_12 // use if LoPy is on Expansion Board, this has a user LED +#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED #define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED -#define HAS_BUTTON GPIO_NUM_13 // user button on expansion board +#define HAS_BUTTON (13) // user button on expansion board #define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown #define HAS_BATTERY_PROBE ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0 -//#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0 \ No newline at end of file +//#define BATT_FACTOR 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0 + +#endif \ No newline at end of file diff --git a/src/hal/octopus32.h b/src/hal/octopus32.h index a448916c..5bf96f31 100644 --- a/src/hal/octopus32.h +++ b/src/hal/octopus32.h @@ -1,3 +1,8 @@ +#ifndef _OCTOPUS_H +#define _OCTOPUS_H + +#include + // Hardware related definitions for #IoT Octopus32 with the Adafruit LoRaWAN Wing // You can use this configuration also with the Adafruit ESP32 Feather + the LoRaWAN Wing // In this config we use the Adafruit OLED Wing which is only 128x32 pixel, need to find a smaller font @@ -15,27 +20,21 @@ #define HAS_SPI 1 // comment out if device shall not send data via SPI #define CFG_sx1276_radio 1 // RFM95 module -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 14 //14 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 18 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 5 // ESP32 GPIO18 (Pin18) -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (14) +#define LORA_MISO (19) +#define LORA_MOSI (18) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (33) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN -//GPIO_NUM_ -// non arduino pin definitions -#define RST LMIC_UNUSED_PIN // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 33 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 33 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) -#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) +// Pins for I2C interface of OLED Display +#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // U8X8_SSD1306_128X32_UNIVISION_SW_I2C // +//#define DISPLAY_FLIP 1 // uncomment this for rotated display +#define MY_OLED_SDA (23) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE -// Hardware pin definitions for LoRaNode32 Board with OLED I2C Display -#define OLED_RST U8X8_PIN_NONE // Not reset pin -#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // U8X8_SSD1306_128X32_UNIVISION_SW_I2C // -//#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define I2C_SDA 23 //21 // ESP32 GPIO14 (Pin14) -- OLED SDA -#define I2C_SCL 22 //22 // ESP32 GPIO12 (Pin12) -- OLED SCL - -// I2C config for Microchip 24AA02E64 DEVEUI unique address -//#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 -//#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits +#endif diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index 7167fbfa..3924d469 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -1,3 +1,8 @@ +#ifndef _TTGOBEAM_H +#define _TTGOBEAM_H + +#include + // Hardware related definitions for TTGO T-Beam board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -14,15 +19,15 @@ #define HAS_GPS 1 // use on board GPS #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (26) +#define LORA_IO1 (32) // !! NEEDS EXTERNAL WIRING !! +//#define LORA_IO1 (33) // for T-Beam T22_V05 and T22_V07, other versions may need external wiring +#define LORA_IO2 LMIC_UNUSED_PIN -// non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 GPIO_NUM_32 // Lora1 <-> HPD13A IO1 // !! NEEDS EXTERNAL WIRING !! -//#define DIO1 GPIO_NUM_33 // Lora1 <-> HPD13A IO1 // for T-Beam T22_V05 and T22_V07, other versions may need external wiring -#define DIO2 LMIC_UNUSED_PIN // Lora2 <-> HPD13A IO2 // not needed for LoRa +#endif diff --git a/src/hal/ttgov1.h b/src/hal/ttgov1.h index f64fe380..d90adef3 100644 --- a/src/hal/ttgov1.h +++ b/src/hal/ttgov1.h @@ -1,3 +1,8 @@ +#ifndef _TTGOV1_H +#define _TTGOV1_H + +#include + // Hardware related definitions for TTGOv1 board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -6,23 +11,23 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED GPIO_NUM_2 // white LED on board +#define HAS_LED (2) // white LED on board #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW -#define HAS_BUTTON GPIO_NUM_0 // button "PRG" on board +#define HAS_BUTTON (0) // button "PRG" on board -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) +#define MY_OLED_RST (16) -// non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (14) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN -// Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display -#define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset -#define I2C_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 Data -#define I2C_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 Clock +#endif diff --git a/src/hal/ttgov2.h b/src/hal/ttgov2.h index 0df3146e..62fc2493 100644 --- a/src/hal/ttgov2.h +++ b/src/hal/ttgov2.h @@ -1,3 +1,8 @@ +#ifndef _TTGOV2_H +#define _TTGOV2_H + +#include + // Hardware related definitions for TTGO V2 Board #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -11,46 +16,19 @@ // disable brownout detection (needed on TTGOv2 for battery powered operation) #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE -// non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 wired on PCB to HPD13A -#define DIO1 GPIO_NUM_33 // HPDIO1 on pcb, needs to be wired external to GPIO33 -#define DIO2 LMIC_UNUSED_PIN // 32 HPDIO2 on pcb, needs to be wired external to GPIO32 (not necessary for LoRa, only FSK) +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 LMIC_UNUSED_PIN -// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display -#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0 - -/* source: -https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-2/11973 - -TTGO LoRa32 V2: -ESP32 LoRa (SPI) Display (I2C) LED ------------ ---------- ------------- ------------------ -GPIO5 SCK SCK -GPIO27 MOSI MOSI -GPIO19 MISO MISO -GPIO18 SS NSS -EN RST RST -GPIO26 DIO0 -GPIO33 DIO1 (see #1) -GPIO32 DIO2 (see #2) -GPIO22 SCL SCL -GPIO21 SDA SDA -GPIO22 useless (see #3) - -#1 Required (used by LMIC for LoRa). -Not on-board wired to any GPIO. Must be manually wired. <<-- necessary for paxcounter - -#2 Optional (used by LMIC for FSK but not for LoRa). <<-- NOT necessary for paxcounter -Not on-board wired to any GPIO. When needed: must be manually wired. - -#3 GPIO22 is already used for SCL therefore LED cannot be used without conflicting with I2C and display. -*/ \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index 5cadc961..2345ea19 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -1,3 +1,8 @@ +#ifndef _TTGOV21NEW_H +#define _TTGOV21NEW_H + +#include + /* Hardware related definitions for TTGO V2.1 Board // ATTENTION: check your board version! // This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older @@ -8,23 +13,23 @@ #define CFG_sx1276_radio 1 // HPD13A LoRa SoC #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C -#define HAS_LED GPIO_NUM_25 // green on board LED +#define HAS_LED (25) // green on board LED #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE -// non arduino pin definitions -#define RST GPIO_NUM_23 // ESP32 GPIO23 <-> HPD13A RESET -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 -#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST (23) +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 (32) -// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display -#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0 \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/hal/ttgov21old.h b/src/hal/ttgov21old.h index 25271b80..76c855a3 100644 --- a/src/hal/ttgov21old.h +++ b/src/hal/ttgov21old.h @@ -1,3 +1,9 @@ +#ifndef _TTGOV21OLD_H +#define _TTGOV21OLD_H + +#include + + /* Hardware related definitions for TTGO V2.1 Board // ATTENTION: check your board version! // This settings are for boards without label on pcb, or labeled v1.5 on pcb @@ -14,20 +20,19 @@ //#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7 //#define BATT_FACTOR 2 // voltage divider 100k/100k on board -// re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +// Pins for I2C interface of OLED Display +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) +#define MY_OLED_RST U8X8_PIN_NONE -// non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN (old board) -//#define RST GPIO_NUM_12 // (boards labeled v1.5) -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 -#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 +// Pins for LORA chip SPI interface, reset line and interrupt lines +#define LORA_SCK (5) +#define LORA_CS (18) +#define LORA_MISO (19) +#define LORA_MOSI (27) +#define LORA_RST LMIC_UNUSED_PIN +#define LORA_IO0 (26) +#define LORA_IO1 (33) +#define LORA_IO2 (32) -// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display -#define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define I2C_SDA GPIO_NUM_21 // ESP32 GPIO21 -- SD1306 D1+D2 -#define I2C_SCL GPIO_NUM_22 // ESP32 GPIO22 -- SD1306 D0 +#endif \ No newline at end of file diff --git a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/config.h b/src/lmic_config.h similarity index 71% rename from lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/config.h rename to src/lmic_config.h index 44c05ed0..ccda75e5 100644 --- a/lib/arduino-lmic-1.5.0-arduino-2-tweaked/src/lmic/config.h +++ b/src/lmic_config.h @@ -1,83 +1,95 @@ -#ifndef _lmic_config_h_ -#define _lmic_config_h_ - -// In the original LMIC code, these config values were defined on the -// gcc commandline. Since Arduino does not allow easily modifying the -// compiler commandline, use this file instead. - -#define CFG_eu868 1 -//#define CFG_us915 1 -// This is the SX1272/SX1273 radio, which is also used on the HopeRF -// RFM92 boards. -//#define CFG_sx1272_radio 1 -// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on -// the HopeRF RFM95 boards. -#define CFG_sx1276_radio 1 - -// 16 μs per tick -// LMIC requires ticks to be 15.5μs - 100 μs long -#define US_PER_OSTICK_EXPONENT 4 -#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) -#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) - -// Set this to 1 to enable some basic debug output (using printf) about -// RF settings used during transmission and reception. Set to 2 to -// enable more verbose output. Make sure that printf is actually -// configured (e.g. on AVR it is not by default), otherwise using it can -// cause crashing. -#define LMIC_DEBUG_LEVEL 0 - -// Enable this to allow using printf() to print to the given serial port -// (or any other Print object). This can be easy for debugging. The -// current implementation only works on AVR, though. -//#define LMIC_PRINTF_TO Serial - -// Any runtime assertion failures are printed to this serial port (or -// any other Print object). If this is unset, any failures just silently -// halt execution. -#define LMIC_FAILURE_TO Serial - -// Uncomment this to disable all code related to joining -//#define DISABLE_JOIN -// Uncomment this to disable all code related to ping -//#define DISABLE_PING -// Uncomment this to disable all code related to beacon tracking. -// Requires ping to be disabled too -//#define DISABLE_BEACONS - -// Uncomment these 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 -// Class B -//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING -//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON - -// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the -// same on RX. This ensures that gateways can talk to nodes and vice -// versa, but gateways will not hear other gateways and nodes will not -// hear other nodes. By uncommenting this macro, this inversion is -// disabled and this node can hear other nodes. If two nodes both have -// this macro set, they can talk to each other (but they can no longer -// hear gateways). This should probably only be used when debugging -// and/or when talking to the radio directly (e.g. like in the "raw" -// example). -//#define DISABLE_INVERT_IQ_ON_RX - -// This allows choosing between multiple included AES implementations. -// Make sure exactly one of these is uncommented. -// -// This selects the original AES implementation included LMIC. This -// implementation is optimized for speed on 32-bit processors using -// fairly big lookup tables, but it takes up big amounts of flash on the -// AVR architecture. -// #define USE_ORIGINAL_AES -// -// This selects the AES implementation written by Ideetroon for their -// own LoRaWAN library. It also uses lookup tables, but smaller -// byte-oriented ones, making it use a lot less flash space (but it is -// also about twice as slow as the original). -#define USE_IDEETRON_AES - -#endif // _lmic_config_h_ +// COUNTRY AND PROJECT SPECIFIC DEFINITIONS FOR LMIC STACK + +// COUNTRY SETTINGS +// --> please check with you local regulations for ISM band frequency use! +// +// CFG_eu868 EU 863-870 MHz +// CFG_us915 US 902-928 MHz +// CFG_au921 Australia 915-928 MHz +// CFG_as923 Asia 923 MHz +// CFG_in866 India 865-867 MHz + +#define CFG_eu868 1 +//#define CFG_us915 1 +//#define CFG_in866 1 +//#define CFG_au921 1 +//#define CFG_as923 1 +//#define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */ + +// LMIC LORAWAN STACK SETTINGS +// --> adapt to your device only if necessary + +//#define LMIC_USE_INTERRUPTS + +// 16 μs per tick +// LMIC requires ticks to be 15.5μs - 100 μs long +#define US_PER_OSTICK_EXPONENT 4 +#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) + +// This tells LMIC to make the receive windows bigger, in case your clock is +// faster or slower. This causes the transceiver to be earlier switched on, +// so consuming more power. You may sharpen (reduce) this value if you are +// limited on battery. +#define CLOCK_ERROR_PROCENTAGE 20 + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +//#define LMIC_DEBUG_LEVEL 1 + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Any runtime assertion failures are printed to this serial port (or +// any other Print object). If this is unset, any failures just silently +// halt execution. +#define LMIC_FAILURE_TO Serial + +// Uncomment this to disable all code related to joining +//#define DISABLE_JOIN +// Uncomment this to disable all code related to ping +#define DISABLE_PING +// Uncomment this to disable all code related to beacon tracking. +// Requires ping to be disabled too +#define DISABLE_BEACONS + +// Uncomment these 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 +// Class B +//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by +// DISABLE_PING #define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical +// disabled by DISABLE_BEACON + +// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the +// same on RX. This ensures that gateways can talk to nodes and vice +// versa, but gateways will not hear other gateways and nodes will not +// hear other nodes. By uncommenting this macro, this inversion is +// disabled and this node can hear other nodes. If two nodes both have +// this macro set, they can talk to each other (but they can no longer +// hear gateways). This should probably only be used when debugging +// and/or when talking to the radio directly (e.g. like in the "raw" +// example). +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +#define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +// #define USE_IDEETRON_AES diff --git a/src/lorawan.cpp b/src/lorawan.cpp index d7e85d36..9cc871e2 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -10,13 +10,25 @@ osjob_t sendjob; QueueHandle_t LoraSendQueue; // LMIC enhanced Pin mapping -const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI, - .miso = PIN_SPI_MISO, - .sck = PIN_SPI_SCK, - .nss = PIN_SPI_SS, - .rxtx = LMIC_UNUSED_PIN, - .rst = RST, - .dio = {DIO0, DIO1, DIO2}}; +const lmic_pinmap lmic_pins = { + .nss = LORA_CS, + .rxtx = LMIC_UNUSED_PIN, + .rst = LORA_RST, + .dio = {LORA_IO0, LORA_IO1, LORA_IO2}, + .mosi = LORA_MOSI, + .miso = LORA_MISO, + .sck = LORA_SCK + // optional: set polarity of rxtx pin. + //.rxtx_rx_active = 0, + // optional: set RSSI cal for listen-before-talk + // this value is in dB, and is added to RSSI + // measured prior to decision. + // Must include noise guardband! Ignored in US, + // EU, IN, other markets where LBT is not required. + //.rssi_cal = 0, + // optional: override LMIC_SPI_FREQ if non-zero + //.spi_freq = 0, +}; // DevEUI generator using devices's MAC address void gen_lora_deveui(uint8_t *pdeveui) { @@ -100,7 +112,7 @@ void get_hard_deveui(uint8_t *pdeveui) { uint8_t i2c_ret; // Init this just in case, no more to 100KHz - Wire.begin(I2C_SDA, I2C_SCL, 100000); + Wire.begin(SDA, SCL, 100000); Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); Wire.write(MCP_24AA02E64_MAC_ADDRESS); i2c_ret = Wire.endTransmission(); @@ -149,82 +161,63 @@ void showLoraKeys(void) { void onEvent(ev_t ev) { char buff[24] = ""; - switch (ev) { + case EV_SCAN_TIMEOUT: strcpy_P(buff, PSTR("SCAN TIMEOUT")); break; + case EV_BEACON_FOUND: - strcpy_P(buff, PSTR("BEACON FOUND")); + strcpy_P(buff, PSTR("BEACON_FOUND")); break; + case EV_BEACON_MISSED: - strcpy_P(buff, PSTR("BEACON MISSED")); + strcpy_P(buff, PSTR("BEACON_MISSED")); break; + case EV_BEACON_TRACKED: - strcpy_P(buff, PSTR("BEACON TRACKED")); + strcpy_P(buff, PSTR("BEACON_TRACKED")); break; + case EV_JOINING: strcpy_P(buff, PSTR("JOINING")); break; - case EV_LOST_TSYNC: - strcpy_P(buff, PSTR("LOST TSYNC")); - break; - case EV_RESET: - strcpy_P(buff, PSTR("RESET")); - break; - case EV_RXCOMPLETE: - strcpy_P(buff, PSTR("RX COMPLETE")); - break; - case EV_LINK_DEAD: - strcpy_P(buff, PSTR("LINK DEAD")); - break; - case EV_LINK_ALIVE: - strcpy_P(buff, PSTR("LINK ALIVE")); - break; - case EV_RFU1: - strcpy_P(buff, PSTR("RFUI")); - break; - case EV_JOIN_FAILED: - strcpy_P(buff, PSTR("JOIN FAILED")); - break; - case EV_REJOIN_FAILED: - strcpy_P(buff, PSTR("REJOIN FAILED")); - break; case EV_JOINED: - strcpy_P(buff, PSTR("JOINED")); sprintf(display_line6, " "); // clear previous lmic status - // set data rate adaptation according to saved setting LMIC_setAdrMode(cfg.adrmode); - // set cyclic lmic link check to off if no ADR because is not supported by // ttn (but enabled by lmic after join) LMIC_setLinkCheckMode(cfg.adrmode); - // Set data rate and transmit power (note: txpower seems to be ignored by // the library) switch_lora(cfg.lorasf, cfg.txpower); - // kickoff first send job os_setCallback(&sendjob, lora_send); - // show effective LoRa parameters after join ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, cfg.txpower); break; - case EV_TXCOMPLETE: + case EV_JOIN_FAILED: + strcpy_P(buff, PSTR("JOIN_FAILED")); + break; + case EV_REJOIN_FAILED: + strcpy_P(buff, PSTR("REJOIN_FAILED")); + break; + + case EV_TXCOMPLETE: strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK") : PSTR("TX COMPLETE")); sprintf(display_line6, " "); // clear previous lmic status if (LMIC.dataLen) { ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d", - LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr); - sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, + LMIC.dataLen, (signed char)LMIC.rssi, (signed char)LMIC.snr); + sprintf(display_line6, "RSSI %d SNR %d", (signed char)LMIC.rssi, (signed char)LMIC.snr); // check if command is received on command port, then call interpreter @@ -234,6 +227,49 @@ void onEvent(ev_t ev) { } break; + case EV_LOST_TSYNC: + strcpy_P(buff, PSTR("LOST_TSYNC")); + break; + + case EV_RESET: + strcpy_P(buff, PSTR("RESET")); + break; + + case EV_RXCOMPLETE: + // data received in ping slot + strcpy_P(buff, PSTR("RX COMPLETE")); + break; + + case EV_LINK_DEAD: + strcpy_P(buff, PSTR("LINK DEAD")); + break; + + case EV_LINK_ALIVE: + strcpy_P(buff, PSTR("LINK ALIVE")); + break; + + case EV_TXSTART: + strcpy_P(buff, PSTR("TX START")); + break; + + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_SCAN_FOUND: + || Serial.println(F("EV_SCAN_FOUND")); + || break; + */ + + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_RFU1: + || Serial.println(F("EV_RFU1")); + || break; + */ + default: sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev); break; @@ -244,8 +280,7 @@ void onEvent(ev_t ev) { ESP_LOGI(TAG, "EV_%s", buff); sprintf(display_line7, buff); } - -} // onEvent() +} // helper function to assign LoRa datarates to numeric spreadfactor values void switch_lora(uint8_t sf, uint8_t tx) { @@ -306,7 +341,7 @@ void lora_send(osjob_t *job) { LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize, (cfg.countermode & 0x02)); ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize); - sprintf(display_line7, "PACKET QUEUED"); + //sprintf(display_line7, "PACKET QUEUED"); } } // reschedule job every 0,5 - 1 sec. including a bit of random to prevent diff --git a/src/main.cpp b/src/main.cpp index c1648a8a..14c03811 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,10 +166,7 @@ void setup() { ESP_LOGI(TAG, "Starting LMIC..."); os_init(); // initialize lmic run-time environment on core 1 LMIC_reset(); // initialize lmic MAC - LMIC_setClockError(MAX_CLOCK_ERROR * 1 / - 100); // This tells LMIC to make the receive windows - // bigger, in case your clock is 1% faster or slower. - + LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100); LMIC_startJoining(); // start joining #endif diff --git a/src/ota.cpp b/src/ota.cpp index 65ff97c4..465838f5 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -250,7 +250,7 @@ void do_ota_update() { // check whether we have everything for OTA update if (contentLength && isValidContentType) { - size_t written, current, size; + size_t written = 0; if (Update.begin(contentLength)) { #ifdef HAS_DISPLAY @@ -270,7 +270,7 @@ void do_ota_update() { if (written == contentLength) { ESP_LOGI(TAG, "Written %u bytes successfully", written); - snprintf(buf, 17, "%u kB Done!", (uint16_t)(written / 1024)); + snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024)); display(4, "OK", buf); break; } else { @@ -329,7 +329,7 @@ void display(const uint8_t row, const std::string status, #ifdef HAS_DISPLAY // callback function to show download progress while streaming data -void show_progress(size_t current, size_t size) { +void show_progress (unsigned long current, unsigned long size) { char buf[17]; snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size); display(4, "**", buf); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 8c5749d5..e048043f 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -72,82 +72,4 @@ #define OTA_MIN_BATT 3700 // minimum battery level vor OTA [millivolt] // LMIC settings -// define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored -// define hardware specifics settings in platformio.ini as build_flag for hardware environment - -// Select frequency band here according to national regulations -#define CFG_eu868 1 -//#define CFG_us915 1 - -// This is the SX1272/SX1273 radio, which is also used on the HopeRF -// RFM92 boards. -//#define CFG_sx1272_radio 1 -// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on -// the HopeRF RFM95 boards. -//#define CFG_sx1276_radio 1 - -// 16 μs per tick -// LMIC requires ticks to be 15.5μs - 100 μs long -#define US_PER_OSTICK_EXPONENT 4 -#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) -#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) - -// Set this to 1 to enable some basic debug output (using printf) about -// RF settings used during transmission and reception. Set to 2 to -// enable more verbose output. Make sure that printf is actually -// configured (e.g. on AVR it is not by default), otherwise using it can -// cause crashing. -//#define LMIC_DEBUG_LEVEL 1 - -// Enable this to allow using printf() to print to the given serial port -// (or any other Print object). This can be easy for debugging. The -// current implementation only works on AVR, though. -//#define LMIC_PRINTF_TO Serial - -// Any runtime assertion failures are printed to this serial port (or -// any other Print object). If this is unset, any failures just silently -// halt execution. -#define LMIC_FAILURE_TO Serial - -// Uncomment this to disable all code related to joining -//#define DISABLE_JOIN -// Uncomment this to disable all code related to ping -#define DISABLE_PING -// Uncomment this to disable all code related to beacon tracking. -// Requires ping to be disabled too -#define DISABLE_BEACONS - -// Uncomment these 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 -// Class B -//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING -//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON - -// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the -// same on RX. This ensures that gateways can talk to nodes and vice -// versa, but gateways will not hear other gateways and nodes will not -// hear other nodes. By uncommenting this macro, this inversion is -// disabled and this node can hear other nodes. If two nodes both have -// this macro set, they can talk to each other (but they can no longer -// hear gateways). This should probably only be used when debugging -// and/or when talking to the radio directly (e.g. like in the "raw" -// example). -//#define DISABLE_INVERT_IQ_ON_RX - -// This allows choosing between multiple included AES implementations. -// Make sure exactly one of these is uncommented. -// -// This selects the original AES implementation included LMIC. This -// implementation is optimized for speed on 32-bit processors using -// fairly big lookup tables, but it takes up big amounts of flash on the -// AVR architecture. -#define USE_ORIGINAL_AES -// -// This selects the AES implementation written by Ideetroon for their -// own LoRaWAN library. It also uses lookup tables, but smaller -// byte-oriented ones, making it use a lot less flash space (but it is -// also about twice as slow as the original). -// #define USE_IDEETRON_AES +// moved to src/lmic_config.h \ No newline at end of file diff --git a/src/spi.cpp b/src/spisend.cpp similarity index 100% rename from src/spi.cpp rename to src/spisend.cpp