├── README.md ├── doc ├── LMiC-v1.6.docx ├── LMiC-v1.6.pdf ├── README.txt └── release-notes.txt ├── examples ├── raw │ └── raw.ino ├── ttn-abp │ └── ttn-abp.ino └── ttn-otaa │ └── ttn-otaa.ino ├── library.properties ├── project_config └── lmic_project_config.h └── src ├── aes ├── ideetron │ └── AES-128_V10.cpp ├── lmic.c └── other.c ├── hal ├── hal.cpp └── hal.h ├── lmic.h └── lmic ├── config.h ├── hal.h ├── lmic.c ├── lmic.h ├── lorabase.h ├── oslmic.c ├── oslmic.h └── radio.c /README.md: -------------------------------------------------------------------------------- 1 | Arduino-LMIC library 2 | ==================== 3 | This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly 4 | modified to run in the Arduino environment, allowing using the SX1272, 5 | SX1276 tranceivers and compatible modules (such as some HopeRF RFM9x 6 | modules). 7 | 8 | This library mostly exposes the functions defined by LMIC, it makes no 9 | attempt to wrap them in a higher level API that is more in the Arduino 10 | style. To find out how to use the library itself, see the examples, or 11 | see the PDF file in the doc subdirectory. 12 | 13 | This library requires Arduino IDE version 1.6.6 or above, since it 14 | requires C99 mode to be enabled by default. 15 | 16 | Installing 17 | ---------- 18 | To install this library: 19 | 20 | - install it using the Arduino Library manager ("Sketch" -> "Include 21 | Library" -> "Manage Libraries..."), or 22 | - download a zipfile from github using the "Download ZIP" button and 23 | install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP 24 | Library..." 25 | - clone this git repository into your sketchbook/libraries folder. 26 | 27 | For more info, see https://www.arduino.cc/en/Guide/Libraries 28 | 29 | Features 30 | -------- 31 | The LMIC library provides a fairly complete LoRaWAN Class A and Class B 32 | implementation, supporting the EU-868 and US-915 bands. Only a limited 33 | number of features was tested using this port on Arduino hardware, so be 34 | careful when using any of the untested features. 35 | 36 | What certainly works: 37 | - Sending packets uplink, taking into account duty cycling. 38 | - Encryption and message integrity checking. 39 | - Receiving downlink packets in the RX2 window. 40 | - Custom frequencies and datarate settings. 41 | - Over-the-air activation (OTAA / joining). 42 | 43 | What has not been tested: 44 | - Receiving downlink packets in the RX1 window. 45 | - Receiving and processing MAC commands. 46 | - Class B operation. 47 | 48 | If you try one of these untested features and it works, be sure to let 49 | us know (creating a github issue is probably the best way for that). 50 | 51 | Configuration 52 | ------------- 53 | A number of features can be enabled or disabled at compile time. 54 | This is done by adding the desired settings to the file 55 | `project_settings/lmic_project_config.h`. The `project_settings` 56 | directory is the only directory that contains files that you 57 | should edit to match your project; we organize things this way 58 | so that your local changes are more clearly separated from 59 | the distribution files. The Arduino environment doesn't give 60 | us a better way to do this. 61 | 62 | The following configuration variables are available. 63 | 64 | ### Selecting the radio frequency 65 | 66 | You should define one of the following variables. If you don't, 67 | the library assumes eu868. The library changes configuration pretty substantially 68 | between eu868 and us915. Some of the differences are listed below. 69 | 70 | #### `#define CFG_eu868 1` 71 | Configure the library for EU868 operation. Adds the API `LMIC_setupBand()`, and 72 | the constants `MAX_CHANNELS`, `MAX_BANDS`, `LIMIT_CHANNELS`, `BAND_MILLI`, 73 | `BAND_CENTI`, `BAND_DECI`, and `BAND_AUX`. 74 | 75 | #### `#define CFG_us915 1` 76 | Configure the library for US915 operation. Adds the APIs `LMIC_enableChannel()`, 77 | `LMIC_enableSubBand()`, `LMIC_disableSubBand()`, and `LMIC_selectSubBand()`. Adds the 78 | constants `MAX_XCHANNELS` and `MAX_TXPOW_125kHz`. 79 | 80 | ### Selecting the target radio transceiver 81 | 82 | You should define one of the following variables. If you don't, the library assumes 83 | sx1276. There is a runtime check to make sure the actual transceiver matches the library 84 | configuration. 85 | 86 | #### `#define CFG_sx1272_radio 1` 87 | Configures the library for use with an sx1272 transceiver. 88 | 89 | #### `#define CFG_sx1276_radio 1` 90 | Configures the library for use with an sx1276 transceiver. 91 | 92 | ### Controlling use of interrupts 93 | #### `#define LMIC_USE_INTERRUPTS` 94 | 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. 95 | 96 | ### Disabling PING 97 | #### `#define DISABLE_PING` 98 | If defined, removes all code needed for PING. Removes the APIs `LMIC_setPingable()` and `LMIC_stopPingable()`. 99 | Class A devices don't support PING, so defining `DISABLE_PING` is often a good idea. 100 | 101 | By default, PING support is included in the library. 102 | 103 | ### Disabling Beacons 104 | #### `#define DISABLE_BEACONS` 105 | If defined, removes all code needed for handling beacons. Removes the APIs `LMIC_enableTracking()` and `LMIC_disableTracking()`. 106 | Class A devices don't support beacons, so defining `DISABLE_BEACONS` might be a good idea. 107 | 108 | 109 | ### Rarely changed variables ### 110 | The remaining variables are rarely used, but we list them here for completeness. 111 | 112 | #### Changing debug output 113 | ##### `#define LMIC_PRINTF_TO SerialLikeObject` 114 | This variable should be set to the name of a `Serial`-like object, used for printing messages. If not defined, `Serial` 115 | is assumed. 116 | 117 | #### Getting debug from the RF library 118 | ##### `#define LMIC_DEBUG_LEVEL number /* 0, 1, or 2 */` 119 | This variable determines the amount of debug output to be produced by the library. The default is `0`. 120 | 121 | If `LMIC_DEBUG_LEVEL` is zero, no output is produced. If `1`, limited output is produced. If `2`, more extensive 122 | output is produced. If non-zero, printf() is used, and the Arduino environment must be configured to support it, 123 | otherwise the sketch will crash at runtime. 124 | 125 | #### Selecting the AES library 126 | The library comes with two AES implementations. The original implementation is better on 127 | ARM processors becasue it's faster, but it's larger. For smaller AVR8 processors, a 128 | second library ("IDEETRON") is provided that has a smaller code footprint. 129 | You may define one of the following variables to choose the AES implementation. If you don't, 130 | the library uses the IDEETRON version. 131 | ##### `#define USE_ORIGINAL_AES` 132 | If defined, the original AES implementation is used. 133 | ##### `#define USE_IDEETRON_AES` 134 | If defined, the IDEETRON AES implementation is used. 135 | 136 | #### Defining the OS Tick Frequency 137 | ##### `#define US_PER_OSTICK_EXPONENT number` 138 | This variable should be set to the base-2 logarithm of the number of microseconds per OS tick. The default is 4, 139 | which indicates that each tick corresponds to 16 microseconds (because 16 == 2^4). 140 | 141 | #### Setting the SPI-bus frequency 142 | ##### `#define LMIC_SPI_FREQ floatNumber` 143 | This variable sets to the frequency for the SPI bus connection to the transceiver. The default is `1E6`, meaning 1 MHz. 144 | 145 | #### Changing handling of runtime assertion failures 146 | The variables `LMIC_FAILURE_TO` and `DISABLE_LMIC_FAILURE_TO` 147 | control the handling of runtime assertion failures. By default, assertion messages are displayed using 148 | the `Serial` object. You can define LMIC_FAILURE_TO to be the name of some other `Print`-like obect. You can 149 | also define `DISABLE_LMIC_FAILURE_TO` to any value, in which case assert failures will silently halt execution. 150 | 151 | #### Disabling JOIN 152 | ##### `#define DISABLE_JOIN` 153 | If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_startJoining()` and `LMIC_tryRejoin()`. 154 | 155 | #### Disabling Class A MAC commands 156 | `DISABLE_MCMD_DCAP_REQ`, `DISABLE_MCMD_DN2P_SET`, and `DISABLE_MCMD_SNCH_REQ` respectively disable code for various Class A MAC 157 | commands. 158 | 159 | #### Disabling Class B MAC commands 160 | `DISABLE_MCMD_PING_SET` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. 161 | 162 | `DISABLE_MCMD_BCNI_ANS` disables the next-beacon start command. I'ts implied by `DISABLE_BEACON' 163 | 164 | #### Special purpose 165 | `#define DISABLE_INVERT_IQ_ON_RX` disables the inverted Q-I polarity on RX. If this is defined, end-devices will be able 166 | to receive messages from each other, but will not be able to hear the gateway. 167 | 168 | Supported hardware 169 | ------------------ 170 | This library is intended to be used with plain LoRa transceivers, 171 | connecting to them using SPI. In particular, the SX1272 and SX1276 172 | families are supported (which should include SX1273, SX1277, SX1278 and 173 | SX1279 which only differ in the available frequencies, bandwidths and 174 | spreading factors). It has been tested with both SX1272 and SX1276 175 | chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 176 | and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip 177 | respectively). 178 | 179 | This library contains a full LoRaWAN stack and is intended to drive 180 | these Transceivers directly. It is *not* intended to be used with 181 | full-stack devices like the Microchip RN2483 and the Embit LR1272E. 182 | These contain a transceiver and microcontroller that implements the 183 | LoRaWAN stack and exposes a high-level serial interface instead of the 184 | low-level SPI transceiver interface. 185 | 186 | This library is intended to be used inside the Arduino environment. It 187 | should be architecture-independent, so it should run on "normal" AVR 188 | arduinos, but also on the ARM-based ones, and some success has been seen 189 | running on the ESP8266 board as well. It was tested on the Arduino Uno, 190 | Pinoccio Scout, Teensy LC and 3.x, ESP8266, Arduino 101, Adafruit Feather M0 LoRa 900. 191 | 192 | This library an be quite heavy, especially if the fairly small ATmega 193 | 328p (such as in the Arduino Uno) is used. In the default configuration, 194 | the available 32K flash space is nearly filled up (this includes some 195 | debug output overhead, though). By disabling some features in `config.h` 196 | (like beacon tracking and ping slots, which are not typically needed), 197 | some space can be freed up. Some work is underway to replace the AES 198 | encryption implementation, which should free up another 8K or so of 199 | flash in the future, making this library feasible to run on a 328p 200 | microcontroller. 201 | 202 | Connections 203 | ----------- 204 | To make this library work, your Arduino (or whatever Arduino-compatible 205 | board you are using) should be connected to the transceiver. The exact 206 | connections are a bit dependent on the transceiver board and Arduino 207 | used, so this section tries to explain what each connection is for and 208 | in what cases it is (not) required. 209 | 210 | Note that the SX1272 module runs at 3.3V and likely does not like 5V on 211 | its pins (though the datasheet is not say anything about this, and my 212 | transceiver did not obviously break after accidentally using 5V I/O for 213 | a few hours). To be safe, make sure to use a level shifter, or an 214 | Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in 215 | series with all data lines that might prevent damage, but I would not 216 | count on that. 217 | 218 | ### Power 219 | The SX127x transceivers need a supply voltage between 1.8V and 3.9V. 220 | Using a 3.3V supply is typical. Some modules have a single power pin 221 | (like the HopeRF modules, labeled 3.3V) but others expose multiple power 222 | pins for different parts (like the Semtech evaluation board that has 223 | `VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together. 224 | Any *GND* pins need to be connected to the Arduino *GND* pin(s). 225 | 226 | ### SPI 227 | The primary way of communicating with the transceiver is through SPI 228 | (Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and 229 | SS. The former three need to be directly connected: so MOSI to MOSI, 230 | MISO to MISO, SCK to SCK. Where these pins are located on your Arduino 231 | varies, see for example the "Connections" section of the [Arduino SPI 232 | documentation](SPI). 233 | 234 | The SS (slave select) connection is a bit more flexible. On the SPI 235 | slave side (the transceiver), this must be connect to the pin 236 | (typically) labeled *NSS*. On the SPI master (Arduino) side, this pin 237 | can connect to any I/O pin. Most Arduinos also have a pin labeled "SS", 238 | but this is only relevant when the Arduino works as an SPI slave, which 239 | is not the case here. Whatever pin you pick, you need to tell the 240 | library what pin you used through the pin mapping (see below). 241 | 242 | [SPI]: https://www.arduino.cc/en/Reference/SPI 243 | 244 | ### DIO pins 245 | The DIO (digitial I/O) pins on the transceiver board can be configured 246 | for various functions. The LMIC library uses them to get instant status 247 | information from the transceiver. For example, when a LoRa transmission 248 | starts, the DIO0 pin is configured as a TxDone output. When the 249 | transmission is complete, the DIO0 pin is made high by the transceiver, 250 | which can be detected by the LMIC library. 251 | 252 | The LMIC library needs only access to DIO0, DIO1 and DIO2, the other 253 | DIOx pins can be left disconnected. On the Arduino side, they can 254 | connect to any I/O pin, since the current implementation does not use 255 | interrupts or other special hardware features (though this might be 256 | added in the feature, see also the "Timing" section). 257 | 258 | In LoRa mode the DIO pins are used as follows: 259 | * DIO0: TxDone and RxDone 260 | * DIO1: RxTimeout 261 | 262 | In FSK mode they are used as follows:: 263 | * DIO0: PayloadReady and PacketSent 264 | * DIO2: TimeOut 265 | 266 | Both modes need only 2 pins, but the tranceiver does not allow mapping 267 | them in such a way that all needed interrupts map to the same 2 pins. 268 | So, if both LoRa and FSK modes are used, all three pins must be 269 | connected. 270 | 271 | The pins used on the Arduino side should be configured in the pin 272 | mapping in your sketch (see below). 273 | 274 | ### Reset 275 | The transceiver has a reset pin that can be used to explicitely reset 276 | it. The LMIC library uses this to ensure the chip is in a consistent 277 | state at startup. In practice, this pin can be left disconnected, since 278 | the transceiver will already be in a sane state on power-on, but 279 | connecting it might prevent problems in some cases. 280 | 281 | On the Arduino side, any I/O pin can be used. The pin number used must 282 | be configured in the pin mapping (see below). 283 | 284 | ### RXTX 285 | The transceiver contains two separate antenna connections: One for RX 286 | and one for TX. A typical transceiver board contains an antenna switch 287 | chip, that allows switching a single antenna between these RX and TX 288 | connections. Such a antenna switcher can typically be told what 289 | position it should be through an input pin, often labeled *RXTX*. 290 | 291 | The easiest way to control the antenna switch is to use the *RXTX* pin 292 | on the SX127x transceiver. This pin is automatically set high during TX 293 | and low during RX. For example, the HopeRF boards seem to have this 294 | connection in place, so they do not expose any *RXTX* pins and the pin 295 | can be marked as unused in the pin mapping. 296 | 297 | Some boards do expose the antenna switcher pin, and sometimes also the 298 | SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the 299 | former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these 300 | together with a jumper wire is the easiest solution. 301 | 302 | Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be 303 | configured to control the antenna switch. Connect the antenna switch 304 | control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O 305 | pin on the Arduino side, and configure the pin used in the pin map (see 306 | below). It is not entirely clear why would *not* want the transceiver to 307 | control the antenna directly, though. 308 | 309 | ### Pin mapping 310 | As described above, most connections can use arbitrary I/O pins on the 311 | Arduino side. To tell the LMIC library about these, a pin mapping struct 312 | is used in the sketch file. 313 | 314 | For example, this could look like this: 315 | 316 | lmic_pinmap lmic_pins = { 317 | .nss = 6, 318 | .rxtx = LMIC_UNUSED_PIN, 319 | .rst = 5, 320 | .dio = {2, 3, 4}, 321 | }; 322 | 323 | The names refer to the pins on the transceiver side, the numbers refer 324 | to the Arduino pin numbers (to use the analog pins, use constants like 325 | `A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2 326 | respectively. Any pins that are not needed should be specified as 327 | `LMIC_UNUSED_PIN`. The nss and dio0 pin is required, the others can 328 | potentially left out (depending on the environments and requirements, 329 | see the notes above for when a pin can or cannot be left out). 330 | 331 | The name of this struct must always be `lmic_pins`, which is a special name 332 | recognized by the library. 333 | 334 | #### LoRa Nexus by Ideetron 335 | This board uses the following pin mapping: 336 | 337 | const lmic_pinmap lmic_pins = { 338 | .nss = 10, 339 | .rxtx = LMIC_UNUSED_PIN, 340 | .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET 341 | .dio = {4, 5, 7}, 342 | }; 343 | 344 | Examples 345 | -------- 346 | This library currently provides three examples: 347 | 348 | - `ttn-abp.ino` shows a basic transmission of a "Hello, world!" message 349 | using the LoRaWAN protocol. It contains some frequency settings and 350 | encryption keys intended for use with The Things Network, but these 351 | also correspond to the default settings of most gateways, so it 352 | should work with other networks and gateways as well. This example 353 | uses activation-by-personalization (ABP, preconfiguring a device 354 | address and encryption keys), and does not employ over-the-air 355 | activation. 356 | 357 | Reception of packets (in response to transmission, using the RX1 and 358 | RX2 receive windows is also supported). 359 | 360 | - `ttn-otaa.ino` also sends a "Hello, world!" message, but uses over 361 | the air activation (OTAA) to first join a network to establish a 362 | session and security keys. This was tested with The Things Network, 363 | but should also work (perhaps with some changes) for other networks. 364 | 365 | - `raw.ino` shows how to access the radio on a somewhat low level, 366 | and allows to send raw (non-LoRaWAN) packets between nodes directly. 367 | This is useful to verify basic connectivity, and when no gateway is 368 | available, but this example also bypasses duty cycle checks, so be 369 | careful when changing the settings. 370 | 371 | Timing 372 | ------ 373 | Unfortunately, the SX127x tranceivers do not support accurate 374 | timekeeping themselves (there is a sequencer that is *almost* sufficient 375 | for timing the RX1 and RX2 downlink windows, but that is only available 376 | in FSK mode, not in LoRa mode). This means that the microcontroller is 377 | responsible for keeping track of time. In particular, it should note 378 | when a packet finished transmitting, so it can open up the RX1 and RX2 379 | receive windows at a fixed time after the end of transmission. 380 | 381 | This timing uses the Arduino `micros()` timer, which has a granularity 382 | of 4μs and is based on the primary microcontroller clock. For timing 383 | events, the tranceiver uses its DIOx pins as interrupt outputs. In the 384 | current implementation, these pins are handled by an interrupt handler, 385 | but only to set a flag - actual processing is done once every LMIC loop, 386 | resulting in a bit inaccuracy in the timestamping. Also, running 387 | scheduled jobs (such as opening up the receive windows) is done using a 388 | polling approach, which might also result in further delays. 389 | 390 | Fortunately, LoRa is a fairly slow protocol and the timing of the 391 | receive windows is not super critical. To synchronize transmitter and 392 | receiver, a preamble is first transmitted. Using LoRaWAN, this preamble 393 | consists of 8 symbols, of which the receiver needs to see 4 symbols to 394 | lock on. The current implementation tries to enable the receiver for 5 395 | symbol times at 1.5 symbol after the start of the receive window, 396 | meaning that a inacurracy of plus or minus 2.5 symbol times should be 397 | acceptable. 398 | 399 | At the fastest LoRa setting supported by the tranceiver (SF5BW500) a 400 | single preamble symbol takes 64μs, so the receive window timing should 401 | be accurate within 160μs (for LoRaWAN this is SF7BW250, needing accuracy 402 | within 1280μs). This is certainly within a crystal's accuracy, but using 403 | the internal oscillator is probably not feasible (which is 1% - 10% 404 | accurate, depending on calibration). This accuracy should also be 405 | feasible with the polling approach used, provided that the LMIC loop is 406 | run often enough. 407 | 408 | It would be good to properly review this code at some point, since it 409 | seems that in some places some offsets and corrections are applied that 410 | might not be appropriate for the Arduino environment. So if reception is 411 | not working, the timing is something to have a closer look at. 412 | 413 | The LMIC library was intended to connect the DIO pins to interrupt 414 | lines and run code inside the interrupt handler. However, doing this 415 | opens up an entire can of worms with regard to doing SPI transfers 416 | inside interrupt routines (some of which is solved by the Arduino 417 | `beginTransaction()` API, but possibly not everything). One simpler 418 | alternative could be to use an interrupt handler to just store a 419 | timestamp, and then do the actual handling in the main loop (this 420 | requires modifications of the library to pass a timestamp to the LMIC 421 | `radio_irq_handler()` function). 422 | 423 | An even more accurate solution could be to use a dedicated timer with an 424 | input capture unit, that can store the timestamp of a change on the DIO0 425 | pin (the only one that is timing-critical) entirely in hardware. 426 | Unfortunately, timer0, as used by Arduino's `millis()` and `micros()` 427 | functions does not seem to have an input capture unit, meaning a 428 | separate timer is needed for this. 429 | 430 | If the main microcontroller does not have a crystal, but uses the 431 | internal oscillator, the clock output of the transceiver (on DIO5) could 432 | be usable to drive this timer instead of the main microcontroller clock, 433 | to ensure the receive window timing is sufficiently accurate. Ideally, 434 | this would use timer2, which supports asynchronous mode (e.g. running 435 | while the microcontroller is sleeping), but that timer does not have an 436 | input capture unit. Timer1 has one, but it seems it will stop running 437 | once the microcontroller sleeps. Running the microcontroller in idle 438 | mode with a slower clock might be feasible, though. Instead of using the 439 | main crystal oscillator of the transceiver, it could be possible to use 440 | the transceiver's internal RC oscillator (which is calibrated against 441 | the transceiver crystal), or to calibrate the microcontroller internal 442 | RC oscillator using the transceiver's clkout. However, that datasheet is 443 | a bit vague on the RC oscillator's accuracy and how to use it exactly 444 | (some registers seem to be FSK-mode only), so this needs some 445 | experiments. 446 | 447 | Downlink datarate 448 | ----------------- 449 | Note that the datarate used for downlink packets in the RX2 window 450 | defaults to SF12BW125 according to the specification, but some networks 451 | use different values (iot.semtech.com and The Things Network both use 452 | SF9BW). When using personalized activate (ABP), it is your 453 | responsibility to set the right settings, e.g. by adding this to your 454 | sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does 455 | this. 456 | 457 | LMIC.dn2Dr = DR_SF9; 458 | 459 | When using OTAA, the network communicates the RX2 settings in the 460 | join accept message, but the LMIC library does not currently process 461 | these settings. Until that is solved (see issue #20), you should 462 | manually set the RX2 rate, *after* joining (see the handling of 463 | `EV_JOINED` in the `ttn-otaa.ino` for an example. 464 | 465 | License 466 | ------- 467 | The upstream files from IBM v1.6 are based on the Berkeley license, 468 | and the merge which synchronized this repository therefore migrated 469 | the core files to the Berkeley license. However, modifications made 470 | in the Arduino branch were done under the Eclipse license, so the 471 | overall license of this repository is still Eclipse Public License 472 | v1.0. The examples which use a more liberal 473 | license. Some of the AES code is available under the LGPL. Refer to each 474 | individual source file for more details, but bear in mind that until 475 | the upstream developers look into this issue, it is safest to assume 476 | the Eclipse license applies. 477 | -------------------------------------------------------------------------------- /doc/LMiC-v1.6.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/things-nyc/arduino-lmic/a1e509fd8463a003e1aef92e052a7c685312130b/doc/LMiC-v1.6.docx -------------------------------------------------------------------------------- /doc/LMiC-v1.6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/things-nyc/arduino-lmic/a1e509fd8463a003e1aef92e052a7c685312130b/doc/LMiC-v1.6.pdf -------------------------------------------------------------------------------- /doc/README.txt: -------------------------------------------------------------------------------- 1 | DISCLAIMER: 2 | Please note that the software is provided AS IS and we cannot 3 | provide support for optimizations, adaptations, integration, 4 | ports to other platforms or device drivers! 5 | -------------------------------------------------------------------------------- /doc/release-notes.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LMIC VERSION 1.6 (13-July-2015) 3 | --------------------------------- 4 | 5 | - License changed to BSD 6 | - Modem included, see LMiC-Modem.pdf and examples/modem 7 | - Additional stm32 hardware and Blipper board specific peripheral code 8 | 9 | 10 | ============================================================================== 11 | LMIC VERSION 1.5 (8-May-2015) 12 | ------------------------------ 13 | 14 | - fixed condition in convFreq() 15 | 16 | - fixed freq*100 bug and freq==0 bug for CFList 17 | 18 | - fixed TX scheduling bug 19 | 20 | - better support for GNU compiler toolchain 21 | 22 | 23 | ============================================================================== 24 | LMIC VERSION 1.4 (17-Mar-2015) 25 | ------------------------------- 26 | 27 | - changed API: inverted port indicator flag in LMIC.txrxFlags 28 | (now TXRX_PORT, previously TXRX_NOPORT) 29 | 30 | - fixed offset OFF_CFLIST constant 31 | 32 | - changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial 33 | 34 | - fixed radio driver (low data rate optimization for SF11+SF12 only for BW125) 35 | 36 | - fixed timer rollover handling in job queue 37 | 38 | ============================================================================== 39 | -------------------------------------------------------------------------------- /examples/raw/raw.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example transmits data on hardcoded channel and receives data 11 | * when not transmitting. Running this sketch on two nodes should allow 12 | * them to communicate. 13 | *******************************************************************************/ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #if !defined(DISABLE_INVERT_IQ_ON_RX) 20 | #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ 21 | config.h in the lmic library to set it. 22 | #endif 23 | 24 | // How often to send a packet. Note that this sketch bypasses the normal 25 | // LMIC duty cycle limiting, so when you change anything in this sketch 26 | // (payload length, frequency, spreading factor), be sure to check if 27 | // this interval should not also be increased. 28 | // See this spreadsheet for an easy airtime and duty cycle calculator: 29 | // https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc 30 | #define TX_INTERVAL 2000 31 | 32 | // Pin mapping 33 | const lmic_pinmap lmic_pins = { 34 | .nss = 6, 35 | .rxtx = LMIC_UNUSED_PIN, 36 | .rst = 5, 37 | .dio = {2, 3, 4}, 38 | }; 39 | 40 | 41 | // These callbacks are only used in over-the-air activation, so they are 42 | // left empty here (we cannot leave them out completely unless 43 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). 44 | void os_getArtEui (u1_t* buf) { } 45 | void os_getDevEui (u1_t* buf) { } 46 | void os_getDevKey (u1_t* buf) { } 47 | 48 | void onEvent (ev_t ev) { 49 | } 50 | 51 | osjob_t txjob; 52 | osjob_t timeoutjob; 53 | static void tx_func (osjob_t* job); 54 | 55 | // Transmit the given string and call the given function afterwards 56 | void tx(const char *str, osjobcb_t func) { 57 | os_radio(RADIO_RST); // Stop RX first 58 | delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet 59 | LMIC.dataLen = 0; 60 | while (*str) 61 | LMIC.frame[LMIC.dataLen++] = *str++; 62 | LMIC.osjob.func = func; 63 | os_radio(RADIO_TX); 64 | Serial.println("TX"); 65 | } 66 | 67 | // Enable rx mode and call func when a packet is received 68 | void rx(osjobcb_t func) { 69 | LMIC.osjob.func = func; 70 | LMIC.rxtime = os_getTime(); // RX _now_ 71 | // Enable "continuous" RX (e.g. without a timeout, still stops after 72 | // receiving a packet) 73 | os_radio(RADIO_RXON); 74 | Serial.println("RX"); 75 | } 76 | 77 | static void rxtimeout_func(osjob_t *job) { 78 | digitalWrite(LED_BUILTIN, LOW); // off 79 | } 80 | 81 | static void rx_func (osjob_t* job) { 82 | // Blink once to confirm reception and then keep the led on 83 | digitalWrite(LED_BUILTIN, LOW); // off 84 | delay(10); 85 | digitalWrite(LED_BUILTIN, HIGH); // on 86 | 87 | // Timeout RX (i.e. update led status) after 3 periods without RX 88 | os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); 89 | 90 | // Reschedule TX so that it should not collide with the other side's 91 | // next TX 92 | os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); 93 | 94 | Serial.print("Got "); 95 | Serial.print(LMIC.dataLen); 96 | Serial.println(" bytes"); 97 | Serial.write(LMIC.frame, LMIC.dataLen); 98 | Serial.println(); 99 | 100 | // Restart RX 101 | rx(rx_func); 102 | } 103 | 104 | static void txdone_func (osjob_t* job) { 105 | rx(rx_func); 106 | } 107 | 108 | // log text to USART and toggle LED 109 | static void tx_func (osjob_t* job) { 110 | // say hello 111 | tx("Hello, world!", txdone_func); 112 | // reschedule job every TX_INTERVAL (plus a bit of random to prevent 113 | // systematic collisions), unless packets are received, then rx_func 114 | // will reschedule at half this time. 115 | os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); 116 | } 117 | 118 | // application entry point 119 | void setup() { 120 | Serial.begin(115200); 121 | Serial.println("Starting"); 122 | #ifdef VCC_ENABLE 123 | // For Pinoccio Scout boards 124 | pinMode(VCC_ENABLE, OUTPUT); 125 | digitalWrite(VCC_ENABLE, HIGH); 126 | delay(1000); 127 | #endif 128 | 129 | pinMode(LED_BUILTIN, OUTPUT); 130 | 131 | // initialize runtime env 132 | os_init(); 133 | 134 | // Set up these settings once, and use them for both TX and RX 135 | 136 | #if defined(CFG_eu868) 137 | // Use a frequency in the g3 which allows 10% duty cycling. 138 | LMIC.freq = 869525000; 139 | #elif defined(CFG_us915) 140 | LMIC.freq = 902300000; 141 | #endif 142 | 143 | // Maximum TX power 144 | LMIC.txpow = 27; 145 | // Use a medium spread factor. This can be increased up to SF12 for 146 | // better range, but then the interval should be (significantly) 147 | // lowered to comply with duty cycle limits as well. 148 | LMIC.datarate = DR_SF9; 149 | // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) 150 | LMIC.rps = updr2rps(LMIC.datarate); 151 | 152 | Serial.println("Started"); 153 | Serial.flush(); 154 | 155 | // setup initial job 156 | os_setCallback(&txjob, tx_func); 157 | } 158 | 159 | void loop() { 160 | // execute scheduled jobs and events 161 | os_runloop_once(); 162 | } 163 | -------------------------------------------------------------------------------- /examples/ttn-abp/ttn-abp.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example sends a valid LoRaWAN packet with payload "Hello, 11 | * world!", using frequency and encryption settings matching those of 12 | * the The Things Network. 13 | * 14 | * This uses ABP (Activation-by-personalisation), where a DevAddr and 15 | * Session keys are preconfigured (unlike OTAA, where a DevEUI and 16 | * application key is configured, while the DevAddr and session keys are 17 | * assigned/generated in the over-the-air-activation procedure). 18 | * 19 | * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in 20 | * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably 21 | * violated by this sketch when left running for longer)! 22 | * 23 | * To use this sketch, first register your application and device with 24 | * the things network, to set or generate a DevAddr, NwkSKey and 25 | * AppSKey. Each device should have their own unique values for these 26 | * fields. 27 | * 28 | * Do not forget to define the radio type correctly in config.h. 29 | * 30 | *******************************************************************************/ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | // LoRaWAN NwkSKey, network session key 37 | // This is the default Semtech key, which is used by the early prototype TTN 38 | // network. 39 | static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 40 | 41 | // LoRaWAN AppSKey, application session key 42 | // This is the default Semtech key, which is used by the early prototype TTN 43 | // network. 44 | static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 45 | 46 | // LoRaWAN end-device address (DevAddr) 47 | static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node! 48 | 49 | // These callbacks are only used in over-the-air activation, so they are 50 | // left empty here (we cannot leave them out completely unless 51 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). 52 | void os_getArtEui (u1_t* buf) { } 53 | void os_getDevEui (u1_t* buf) { } 54 | void os_getDevKey (u1_t* buf) { } 55 | 56 | static uint8_t mydata[] = "Hello, world!"; 57 | static osjob_t sendjob; 58 | 59 | // Schedule TX every this many seconds (might become longer due to duty 60 | // cycle limitations). 61 | const unsigned TX_INTERVAL = 60; 62 | 63 | // Pin mapping 64 | const lmic_pinmap lmic_pins = { 65 | .nss = 6, 66 | .rxtx = LMIC_UNUSED_PIN, 67 | .rst = 5, 68 | .dio = {2, 3, 4}, 69 | }; 70 | 71 | void onEvent (ev_t ev) { 72 | Serial.print(os_getTime()); 73 | Serial.print(": "); 74 | switch(ev) { 75 | case EV_SCAN_TIMEOUT: 76 | Serial.println(F("EV_SCAN_TIMEOUT")); 77 | break; 78 | case EV_BEACON_FOUND: 79 | Serial.println(F("EV_BEACON_FOUND")); 80 | break; 81 | case EV_BEACON_MISSED: 82 | Serial.println(F("EV_BEACON_MISSED")); 83 | break; 84 | case EV_BEACON_TRACKED: 85 | Serial.println(F("EV_BEACON_TRACKED")); 86 | break; 87 | case EV_JOINING: 88 | Serial.println(F("EV_JOINING")); 89 | break; 90 | case EV_JOINED: 91 | Serial.println(F("EV_JOINED")); 92 | break; 93 | case EV_RFU1: 94 | Serial.println(F("EV_RFU1")); 95 | break; 96 | case EV_JOIN_FAILED: 97 | Serial.println(F("EV_JOIN_FAILED")); 98 | break; 99 | case EV_REJOIN_FAILED: 100 | Serial.println(F("EV_REJOIN_FAILED")); 101 | break; 102 | case EV_TXCOMPLETE: 103 | Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); 104 | if (LMIC.txrxFlags & TXRX_ACK) 105 | Serial.println(F("Received ack")); 106 | if (LMIC.dataLen) { 107 | Serial.println(F("Received ")); 108 | Serial.println(LMIC.dataLen); 109 | Serial.println(F(" bytes of payload")); 110 | } 111 | // Schedule next transmission 112 | os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); 113 | break; 114 | case EV_LOST_TSYNC: 115 | Serial.println(F("EV_LOST_TSYNC")); 116 | break; 117 | case EV_RESET: 118 | Serial.println(F("EV_RESET")); 119 | break; 120 | case EV_RXCOMPLETE: 121 | // data received in ping slot 122 | Serial.println(F("EV_RXCOMPLETE")); 123 | break; 124 | case EV_LINK_DEAD: 125 | Serial.println(F("EV_LINK_DEAD")); 126 | break; 127 | case EV_LINK_ALIVE: 128 | Serial.println(F("EV_LINK_ALIVE")); 129 | break; 130 | default: 131 | Serial.println(F("Unknown event")); 132 | break; 133 | } 134 | } 135 | 136 | void do_send(osjob_t* j){ 137 | // Check if there is not a current TX/RX job running 138 | if (LMIC.opmode & OP_TXRXPEND) { 139 | Serial.println(F("OP_TXRXPEND, not sending")); 140 | } else { 141 | // Prepare upstream data transmission at the next possible time. 142 | LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); 143 | Serial.println(F("Packet queued")); 144 | } 145 | // Next TX is scheduled after TX_COMPLETE event. 146 | } 147 | 148 | void setup() { 149 | Serial.begin(115200); 150 | Serial.println(F("Starting")); 151 | 152 | #ifdef VCC_ENABLE 153 | // For Pinoccio Scout boards 154 | pinMode(VCC_ENABLE, OUTPUT); 155 | digitalWrite(VCC_ENABLE, HIGH); 156 | delay(1000); 157 | #endif 158 | 159 | // LMIC init 160 | os_init(); 161 | // Reset the MAC state. Session and pending data transfers will be discarded. 162 | LMIC_reset(); 163 | 164 | // Set static session parameters. Instead of dynamically establishing a session 165 | // by joining the network, precomputed session parameters are be provided. 166 | #ifdef PROGMEM 167 | // On AVR, these values are stored in flash and only copied to RAM 168 | // once. Copy them to a temporary buffer here, LMIC_setSession will 169 | // copy them into a buffer of its own again. 170 | uint8_t appskey[sizeof(APPSKEY)]; 171 | uint8_t nwkskey[sizeof(NWKSKEY)]; 172 | memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); 173 | memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); 174 | LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); 175 | #else 176 | // If not running an AVR with PROGMEM, just use the arrays directly 177 | LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); 178 | #endif 179 | 180 | #if defined(CFG_eu868) 181 | // Set up the channels used by the Things Network, which corresponds 182 | // to the defaults of most gateways. Without this, only three base 183 | // channels from the LoRaWAN specification are used, which certainly 184 | // works, so it is good for debugging, but can overload those 185 | // frequencies, so be sure to configure the full frequency range of 186 | // your network here (unless your network autoconfigures them). 187 | // Setting up channels should happen after LMIC_setSession, as that 188 | // configures the minimal channel set. 189 | LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 190 | LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band 191 | LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 192 | LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 193 | LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 194 | LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 195 | LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 196 | LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 197 | LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band 198 | // TTN defines an additional channel at 869.525Mhz using SF9 for class B 199 | // devices' ping slots. LMIC does not have an easy way to define set this 200 | // frequency and support for class B is spotty and untested, so this 201 | // frequency is not configured here. 202 | #elif defined(CFG_us915) 203 | // NA-US channels 0-71 are configured automatically 204 | // but only one group of 8 should (a subband) should be active 205 | // TTN recommends the second sub band, 1 in a zero based count. 206 | // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json 207 | LMIC_selectSubBand(1); 208 | #endif 209 | 210 | // Disable link check validation 211 | LMIC_setLinkCheckMode(0); 212 | 213 | // TTN uses SF9 for its RX2 window. 214 | LMIC.dn2Dr = DR_SF9; 215 | 216 | // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) 217 | LMIC_setDrTxpow(DR_SF7,14); 218 | 219 | // Start job 220 | do_send(&sendjob); 221 | } 222 | 223 | void loop() { 224 | os_runloop_once(); 225 | } 226 | -------------------------------------------------------------------------------- /examples/ttn-otaa/ttn-otaa.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 3 | * 4 | * Permission is hereby granted, free of charge, to anyone 5 | * obtaining a copy of this document and accompanying files, 6 | * to do whatever they want with them without any restriction, 7 | * including, but not limited to, copying, modification and redistribution. 8 | * NO WARRANTY OF ANY KIND IS PROVIDED. 9 | * 10 | * This example sends a valid LoRaWAN packet with payload "Hello, 11 | * world!", using frequency and encryption settings matching those of 12 | * the The Things Network. 13 | * 14 | * This uses OTAA (Over-the-air activation), where where a DevEUI and 15 | * application key is configured, which are used in an over-the-air 16 | * activation procedure where a DevAddr and session keys are 17 | * assigned/generated for use with all further communication. 18 | * 19 | * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in 20 | * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably 21 | * violated by this sketch when left running for longer)! 22 | 23 | * To use this sketch, first register your application and device with 24 | * the things network, to set or generate an AppEUI, DevEUI and AppKey. 25 | * Multiple devices can use the same AppEUI, but each device has its own 26 | * DevEUI and AppKey. 27 | * 28 | * Do not forget to define the radio type correctly in config.h. 29 | * 30 | *******************************************************************************/ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | // This EUI must be in little-endian format, so least-significant-byte 37 | // first. When copying an EUI from ttnctl output, this means to reverse 38 | // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 39 | // 0x70. 40 | static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 41 | void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} 42 | 43 | // This should also be in little endian format, see above. 44 | static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 45 | void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} 46 | 47 | // This key should be in big endian format (or, since it is not really a 48 | // number but a block of memory, endianness does not really apply). In 49 | // practice, a key taken from ttnctl can be copied as-is. 50 | // The key shown here is the semtech default key. 51 | static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; 52 | void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} 53 | 54 | static uint8_t mydata[] = "Hello, world!"; 55 | static osjob_t sendjob; 56 | 57 | // Schedule TX every this many seconds (might become longer due to duty 58 | // cycle limitations). 59 | const unsigned TX_INTERVAL = 60; 60 | 61 | // Pin mapping 62 | const lmic_pinmap lmic_pins = { 63 | .nss = 6, 64 | .rxtx = LMIC_UNUSED_PIN, 65 | .rst = 5, 66 | .dio = {2, 3, 4}, 67 | }; 68 | 69 | void onEvent (ev_t ev) { 70 | Serial.print(os_getTime()); 71 | Serial.print(": "); 72 | switch(ev) { 73 | case EV_SCAN_TIMEOUT: 74 | Serial.println(F("EV_SCAN_TIMEOUT")); 75 | break; 76 | case EV_BEACON_FOUND: 77 | Serial.println(F("EV_BEACON_FOUND")); 78 | break; 79 | case EV_BEACON_MISSED: 80 | Serial.println(F("EV_BEACON_MISSED")); 81 | break; 82 | case EV_BEACON_TRACKED: 83 | Serial.println(F("EV_BEACON_TRACKED")); 84 | break; 85 | case EV_JOINING: 86 | Serial.println(F("EV_JOINING")); 87 | break; 88 | case EV_JOINED: 89 | Serial.println(F("EV_JOINED")); 90 | { 91 | u4_t netid = 0; 92 | devaddr_t devaddr = 0; 93 | u1_t nwkKey[16]; 94 | u1_t artKey[16]; 95 | LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); 96 | Serial.print("netid: "); 97 | Serial.println(netid, DEC); 98 | Serial.print("devaddr: "); 99 | Serial.println(devaddr, HEX); 100 | Serial.print("artKey: "); 101 | for (int i=0; i 5 | sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. 6 | paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers 7 | category=Communication 8 | url=http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html 9 | architectures=* 10 | -------------------------------------------------------------------------------- /project_config/lmic_project_config.h: -------------------------------------------------------------------------------- 1 | // project-specific definitions for otaa sensor 2 | #define CFG_us915 1 3 | #define CFG_sx1276_radio 1 4 | //#define LMIC_USE_INTERRUPTS 5 | -------------------------------------------------------------------------------- /src/aes/ideetron/AES-128_V10.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | #if defined(USE_IDEETRON_AES) 3 | * Copyright 2015, 2016 Ideetron B.V. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | ******************************************************************************************/ 18 | /****************************************************************************************** 19 | * 20 | * File: AES-128_V10.cpp 21 | * Author: Gerben den Hartog 22 | * Compagny: Ideetron B.V. 23 | * Website: http://www.ideetron.nl/LoRa 24 | * E-mail: info@ideetron.nl 25 | ******************************************************************************************/ 26 | /**************************************************************************************** 27 | * 28 | * Created on: 20-10-2015 29 | * Supported Hardware: ID150119-02 Nexus board with RFM95 30 | * 31 | * Firmware Version 1.0 32 | * First version 33 | ****************************************************************************************/ 34 | 35 | // This file was taken from 36 | // https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for 37 | // use with LMIC. It was only cosmetically modified: 38 | // - AES_Encrypt was renamed to lmic_aes_encrypt. 39 | // - All other functions and variables were made static 40 | // - Tabs were converted to 2 spaces 41 | // - An #include and #if guard was added 42 | // - S_Table is now stored in PROGMEM 43 | 44 | #include "../../lmic/oslmic.h" 45 | 46 | #if defined(USE_IDEETRON_AES) 47 | 48 | /* 49 | ******************************************************************************************** 50 | * Global Variables 51 | ******************************************************************************************** 52 | */ 53 | 54 | static unsigned char State[4][4]; 55 | 56 | static CONST_TABLE(unsigned char, S_Table)[16][16] = { 57 | {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, 58 | {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, 59 | {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, 60 | {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, 61 | {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, 62 | {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, 63 | {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, 64 | {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, 65 | {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, 66 | {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, 67 | {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, 68 | {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, 69 | {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, 70 | {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, 71 | {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, 72 | {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} 73 | }; 74 | 75 | extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); 76 | static void AES_Add_Round_Key(unsigned char *Round_Key); 77 | static unsigned char AES_Sub_Byte(unsigned char Byte); 78 | static void AES_Shift_Rows(); 79 | static void AES_Mix_Collums(); 80 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); 81 | static void Send_State(); 82 | 83 | /* 84 | ***************************************************************************************** 85 | * Description : Function for encrypting data using AES-128 86 | * 87 | * Arguments : *Data Data to encrypt is a 16 byte long arry 88 | * *Key Key to encrypt data with is a 16 byte long arry 89 | ***************************************************************************************** 90 | */ 91 | void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) 92 | { 93 | unsigned char i; 94 | unsigned char Row,Collum; 95 | unsigned char Round = 0x00; 96 | unsigned char Round_Key[16]; 97 | 98 | //Copy input to State arry 99 | for(Collum = 0; Collum < 4; Collum++) 100 | { 101 | for(Row = 0; Row < 4; Row++) 102 | { 103 | State[Row][Collum] = Data[Row + (4*Collum)]; 104 | } 105 | } 106 | 107 | //Copy key to round key 108 | for(i = 0; i < 16; i++) 109 | { 110 | Round_Key[i] = Key[i]; 111 | } 112 | 113 | //Add round key 114 | AES_Add_Round_Key(Round_Key); 115 | 116 | //Preform 9 full rounds 117 | for(Round = 1; Round < 10; Round++) 118 | { 119 | //Preform Byte substitution with S table 120 | for(Collum = 0; Collum < 4; Collum++) 121 | { 122 | for(Row = 0; Row < 4; Row++) 123 | { 124 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); 125 | } 126 | } 127 | 128 | //Preform Row Shift 129 | AES_Shift_Rows(); 130 | 131 | //Mix Collums 132 | AES_Mix_Collums(); 133 | 134 | //Calculate new round key 135 | AES_Calculate_Round_Key(Round,Round_Key); 136 | 137 | //Add round key 138 | AES_Add_Round_Key(Round_Key); 139 | } 140 | 141 | //Last round whitout mix collums 142 | //Preform Byte substitution with S table 143 | for(Collum = 0; Collum < 4; Collum++) 144 | { 145 | for(Row = 0; Row < 4; Row++) 146 | { 147 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); 148 | } 149 | } 150 | 151 | //Shift rows 152 | AES_Shift_Rows(); 153 | 154 | //Calculate new round key 155 | AES_Calculate_Round_Key(Round,Round_Key); 156 | 157 | //Add round Key 158 | AES_Add_Round_Key(Round_Key); 159 | 160 | //Copy the State into the data array 161 | for(Collum = 0; Collum < 4; Collum++) 162 | { 163 | for(Row = 0; Row < 4; Row++) 164 | { 165 | Data[Row + (4*Collum)] = State[Row][Collum]; 166 | } 167 | } 168 | 169 | } 170 | 171 | /* 172 | ***************************************************************************************** 173 | * Description : Function that add's the round key for the current round 174 | * 175 | * Arguments : *Round_Key 16 byte long array holding the Round Key 176 | ***************************************************************************************** 177 | */ 178 | static void AES_Add_Round_Key(unsigned char *Round_Key) 179 | { 180 | unsigned char Row,Collum; 181 | 182 | for(Collum = 0; Collum < 4; Collum++) 183 | { 184 | for(Row = 0; Row < 4; Row++) 185 | { 186 | State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; 187 | } 188 | } 189 | } 190 | 191 | /* 192 | ***************************************************************************************** 193 | * Description : Function that substitutes a byte with a byte from the S_Table 194 | * 195 | * Arguments : Byte The byte that will be substituted 196 | * 197 | * Return : The return is the found byte in the S_Table 198 | ***************************************************************************************** 199 | */ 200 | static unsigned char AES_Sub_Byte(unsigned char Byte) 201 | { 202 | unsigned char S_Row,S_Collum; 203 | unsigned char S_Byte; 204 | 205 | //Split byte up in Row and Collum 206 | S_Row = ((Byte >> 4) & 0x0F); 207 | S_Collum = (Byte & 0x0F); 208 | 209 | //Find the correct byte in the S_Table 210 | S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum); 211 | 212 | return S_Byte; 213 | } 214 | 215 | /* 216 | ***************************************************************************************** 217 | * Description : Function that preforms the shift row operation described in the AES standard 218 | ***************************************************************************************** 219 | */ 220 | static void AES_Shift_Rows() 221 | { 222 | unsigned char Buffer; 223 | 224 | //Row 0 doesn't change 225 | 226 | //Shift Row 1 one left 227 | //Store firt byte in buffer 228 | Buffer = State[1][0]; 229 | //Shift all bytes 230 | State[1][0] = State[1][1]; 231 | State[1][1] = State[1][2]; 232 | State[1][2] = State[1][3]; 233 | State[1][3] = Buffer; 234 | 235 | //Shift row 2 two left 236 | Buffer = State[2][0]; 237 | State[2][0] = State[2][2]; 238 | State[2][2] = Buffer; 239 | Buffer = State[2][1]; 240 | State[2][1] = State[2][3]; 241 | State[2][3] = Buffer; 242 | 243 | //Shift row 3 three left 244 | Buffer = State[3][3]; 245 | State[3][3] = State[3][2]; 246 | State[3][2] = State[3][1]; 247 | State[3][1] = State[3][0]; 248 | State[3][0] = Buffer; 249 | } 250 | 251 | /* 252 | ***************************************************************************************** 253 | * Description : Function that preforms the Mix Collums operation described in the AES standard 254 | ***************************************************************************************** 255 | */ 256 | static void AES_Mix_Collums() 257 | { 258 | unsigned char Row,Collum; 259 | unsigned char a[4], b[4]; 260 | for(Collum = 0; Collum < 4; Collum++) 261 | { 262 | for(Row = 0; Row < 4; Row++) 263 | { 264 | a[Row] = State[Row][Collum]; 265 | b[Row] = (State[Row][Collum] << 1); 266 | 267 | if((State[Row][Collum] & 0x80) == 0x80) 268 | { 269 | b[Row] = b[Row] ^ 0x1B; 270 | } 271 | } 272 | State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; 273 | State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; 274 | State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; 275 | State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; 276 | } 277 | } 278 | 279 | /* 280 | ***************************************************************************************** 281 | * Description : Function that calculaties the round key for the current round 282 | * 283 | * Arguments : Round Number of current Round 284 | * *Round_Key 16 byte long array holding the Round Key 285 | ***************************************************************************************** 286 | */ 287 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) 288 | { 289 | unsigned char i,j; 290 | unsigned char b; 291 | unsigned char Temp[4]; 292 | unsigned char Buffer; 293 | unsigned char Rcon; 294 | 295 | //Calculate first Temp 296 | //Copy laste byte from previous key 297 | for(i = 0; i < 4; i++) 298 | { 299 | Temp[i] = Round_Key[i+12]; 300 | } 301 | 302 | //Rotate Temp 303 | Buffer = Temp[0]; 304 | Temp[0] = Temp[1]; 305 | Temp[1] = Temp[2]; 306 | Temp[2] = Temp[3]; 307 | Temp[3] = Buffer; 308 | 309 | //Substitute Temp 310 | for(i = 0; i < 4; i++) 311 | { 312 | Temp[i] = AES_Sub_Byte(Temp[i]); 313 | } 314 | 315 | //Calculate Rcon 316 | Rcon = 0x01; 317 | while(Round != 1) 318 | { 319 | b = Rcon & 0x80; 320 | Rcon = Rcon << 1; 321 | if(b == 0x80) 322 | { 323 | Rcon = Rcon ^ 0x1b; 324 | } 325 | Round--; 326 | } 327 | 328 | //XOR Rcon 329 | Temp[0] = Temp[0] ^ Rcon; 330 | 331 | //Calculate new key 332 | for(i = 0; i < 4; i++) 333 | { 334 | for(j = 0; j < 4; j++) 335 | { 336 | Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; 337 | Temp[j] = Round_Key[j + (4*i)]; 338 | } 339 | } 340 | } 341 | 342 | #endif // defined(USE_IDEETRON_AES) 343 | -------------------------------------------------------------------------------- /src/aes/lmic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "../lmic/oslmic.h" 29 | 30 | #if defined(USE_ORIGINAL_AES) 31 | 32 | #define AES_MICSUB 0x30 // internal use only 33 | 34 | static CONST_TABLE(u4_t, AES_RCON)[10] = { 35 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 36 | 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 37 | }; 38 | 39 | static CONST_TABLE(u1_t, AES_S)[256] = { 40 | 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 41 | 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 42 | 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 43 | 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 44 | 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 45 | 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 46 | 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 47 | 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 48 | 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 49 | 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 50 | 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 51 | 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 52 | 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 53 | 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 54 | 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 55 | 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, 56 | }; 57 | 58 | static CONST_TABLE(u4_t, AES_E1)[256] = { 59 | 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, 60 | 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, 61 | 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, 62 | 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, 63 | 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, 64 | 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, 65 | 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, 66 | 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, 67 | 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, 68 | 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, 69 | 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, 70 | 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, 71 | 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, 72 | 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, 73 | 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, 74 | 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, 75 | 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, 76 | 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, 77 | 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, 78 | 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, 79 | 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, 80 | 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, 81 | 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, 82 | 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, 83 | 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, 84 | 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, 85 | 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, 86 | 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, 87 | 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, 88 | 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, 89 | 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, 90 | 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A, 91 | }; 92 | 93 | static CONST_TABLE(u4_t, AES_E2)[256] = { 94 | 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, 95 | 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, 96 | 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, 97 | 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, 98 | 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, 99 | 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, 100 | 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, 101 | 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, 102 | 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, 103 | 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, 104 | 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, 105 | 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, 106 | 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, 107 | 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, 108 | 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, 109 | 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, 110 | 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, 111 | 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, 112 | 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, 113 | 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, 114 | 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, 115 | 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, 116 | 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, 117 | 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, 118 | 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, 119 | 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, 120 | 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, 121 | 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, 122 | 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, 123 | 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, 124 | 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, 125 | 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616, 126 | }; 127 | 128 | static CONST_TABLE(u4_t, AES_E3)[256] = { 129 | 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, 130 | 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, 131 | 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, 132 | 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, 133 | 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, 134 | 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, 135 | 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, 136 | 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, 137 | 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, 138 | 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, 139 | 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, 140 | 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, 141 | 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, 142 | 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, 143 | 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, 144 | 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, 145 | 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, 146 | 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, 147 | 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, 148 | 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, 149 | 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, 150 | 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, 151 | 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, 152 | 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, 153 | 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, 154 | 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, 155 | 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, 156 | 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, 157 | 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, 158 | 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, 159 | 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, 160 | 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16, 161 | }; 162 | 163 | static CONST_TABLE(u4_t, AES_E4)[256] = { 164 | 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, 165 | 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, 166 | 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, 167 | 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, 168 | 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, 169 | 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, 170 | 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, 171 | 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, 172 | 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, 173 | 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, 174 | 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, 175 | 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, 176 | 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, 177 | 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, 178 | 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, 179 | 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, 180 | 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, 181 | 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, 182 | 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, 183 | 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, 184 | 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, 185 | 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, 186 | 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, 187 | 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, 188 | 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, 189 | 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, 190 | 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, 191 | 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, 192 | 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, 193 | 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, 194 | 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, 195 | 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C, 196 | }; 197 | 198 | #define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3]) 199 | #define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v) 200 | #define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) ) 201 | 202 | #define u1(v) ((u1_t)(v)) 203 | 204 | #define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \ 205 | r2 = ki[i+2]; \ 206 | r3 = ki[i+3]; \ 207 | r0 = ki[i] 208 | 209 | #define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \ 210 | r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \ 211 | r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \ 212 | r0 ^= TABLE_GET_U4(AES_E1, (i>>24)) 213 | 214 | #define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ 215 | a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \ 216 | a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \ 217 | a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \ 218 | a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) ) 219 | 220 | // global area for passing parameters (aux, key) and for storing round keys 221 | u4_t AESAUX[16/sizeof(u4_t)]; 222 | u4_t AESKEY[11*16/sizeof(u4_t)]; 223 | 224 | // generate 1+10 roundkeys for encryption with 128-bit key 225 | // read 128-bit key from AESKEY in MSBF, generate roundkey words in place 226 | static void aesroundkeys () { 227 | int i; 228 | u4_t b; 229 | 230 | for( i=0; i<4; i++) { 231 | AESKEY[i] = swapmsbf(AESKEY[i]); 232 | } 233 | 234 | b = AESKEY[3]; 235 | for( ; i<44; i++ ) { 236 | if( i%4==0 ) { 237 | // b = SubWord(RotWord(b)) xor Rcon[i/4] 238 | b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^ 239 | ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^ 240 | ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^ 241 | ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^ 242 | TABLE_GET_U4(AES_RCON, (i-4)/4); 243 | } 244 | AESKEY[i] = b ^= AESKEY[i-4]; 245 | } 246 | } 247 | 248 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { 249 | 250 | aesroundkeys(); 251 | 252 | if( mode & AES_MICNOAUX ) { 253 | AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0; 254 | } else { 255 | AESAUX[0] = swapmsbf(AESAUX[0]); 256 | AESAUX[1] = swapmsbf(AESAUX[1]); 257 | AESAUX[2] = swapmsbf(AESAUX[2]); 258 | AESAUX[3] = swapmsbf(AESAUX[3]); 259 | } 260 | 261 | while( (signed char)len > 0 ) { 262 | u4_t a0, a1, a2, a3; 263 | u4_t t0, t1, t2, t3; 264 | u4_t *ki, *ke; 265 | 266 | // load input block 267 | if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block 268 | a0 = AESAUX[0]; 269 | a1 = AESAUX[1]; 270 | a2 = AESAUX[2]; 271 | a3 = AESAUX[3]; 272 | } 273 | else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block 274 | a0 = a1 = a2 = a3 = 0; // load null block 275 | mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2 276 | } else 277 | LOADDATA: { // load data block (partially) 278 | for(t0=0; t0<16; t0++) { 279 | t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block 330 | do { 331 | // compute CMAC subkey K1 and K2 332 | t0 = a0 >> 31; // save MSB 333 | a0 = (a0 << 1) | (a1 >> 31); 334 | a1 = (a1 << 1) | (a2 >> 31); 335 | a2 = (a2 << 1) | (a3 >> 31); 336 | a3 = (a3 << 1); 337 | if( t0 ) a3 ^= 0x87; 338 | } while( --t1 ); 339 | 340 | AESAUX[0] ^= a0; 341 | AESAUX[1] ^= a1; 342 | AESAUX[2] ^= a2; 343 | AESAUX[3] ^= a3; 344 | mode &= ~AES_MICSUB; 345 | goto LOADDATA; 346 | } else { 347 | // save cipher block as new iv 348 | AESAUX[0] = a0; 349 | AESAUX[1] = a1; 350 | AESAUX[2] = a2; 351 | AESAUX[3] = a3; 352 | } 353 | } else { // CIPHER 354 | if( mode & AES_CTR ) { // xor block (partially) 355 | t0 = (len > 16) ? 16: len; 356 | for(t1=0; t1>24); 358 | a0 <<= 8; 359 | if((t1&3)==3) { 360 | a0 = a1; 361 | a1 = a2; 362 | a2 = a3; 363 | } 364 | } 365 | // update counter 366 | AESAUX[3]++; 367 | } else { // ECB 368 | // store block 369 | msbf4_write(buf+0, a0); 370 | msbf4_write(buf+4, a1); 371 | msbf4_write(buf+8, a2); 372 | msbf4_write(buf+12, a3); 373 | } 374 | } 375 | 376 | // update block state 377 | if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) { 378 | buf += 16; 379 | len -= 16; 380 | } 381 | mode |= AES_MICNOAUX; 382 | } 383 | return AESAUX[0]; 384 | } 385 | 386 | #endif 387 | -------------------------------------------------------------------------------- /src/aes/other.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016 Matthijs Kooijman 3 | * 4 | * LICENSE 5 | * 6 | * Permission is hereby granted, free of charge, to anyone 7 | * obtaining a copy of this document and accompanying files, 8 | * to do whatever they want with them without any restriction, 9 | * including, but not limited to, copying, modification and 10 | * redistribution. 11 | * 12 | * NO WARRANTY OF ANY KIND IS PROVIDED. 13 | *******************************************************************************/ 14 | 15 | /* 16 | * The original LMIC AES implementation integrates raw AES encryption 17 | * with CMAC and AES-CTR in a single piece of code. Most other AES 18 | * implementations (only) offer raw single block AES encryption, so this 19 | * file contains an implementation of CMAC and AES-CTR, and offers the 20 | * same API through the os_aes() function as the original AES 21 | * implementation. This file assumes that there is an encryption 22 | * function available with this signature: 23 | * 24 | * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); 25 | * 26 | * That takes a single 16-byte buffer and encrypts it wit the given 27 | * 16-byte key. 28 | */ 29 | 30 | #include "../lmic/oslmic.h" 31 | 32 | #if !defined(USE_ORIGINAL_AES) 33 | 34 | // This should be defined elsewhere 35 | void lmic_aes_encrypt(u1_t *data, u1_t *key); 36 | 37 | // global area for passing parameters (aux, key) and for storing round keys 38 | u4_t AESAUX[16/sizeof(u4_t)]; 39 | u4_t AESKEY[11*16/sizeof(u4_t)]; 40 | 41 | // Shift the given buffer left one bit 42 | static void shift_left(xref2u1_t buf, u1_t len) { 43 | while (len--) { 44 | u1_t next = len ? buf[1] : 0; 45 | 46 | u1_t val = (*buf << 1); 47 | if (next & 0x80) 48 | val |= 1; 49 | *buf++ = val; 50 | } 51 | } 52 | 53 | // Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, 54 | // AESAUX is prepended to the message. AESAUX is used as working memory 55 | // in any case. The CMAC result is returned in AESAUX as well. 56 | static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) { 57 | if (prepend_aux) 58 | lmic_aes_encrypt(AESaux, AESkey); 59 | else 60 | memset (AESaux, 0, 16); 61 | 62 | while (len > 0) { 63 | u1_t need_padding = 0; 64 | for (u1_t i = 0; i < 16; ++i, ++buf, --len) { 65 | if (len == 0) { 66 | // The message is padded with 0x80 and then zeroes. 67 | // Since zeroes are no-op for xor, we can just skip them 68 | // and leave AESAUX unchanged for them. 69 | AESaux[i] ^= 0x80; 70 | need_padding = 1; 71 | break; 72 | } 73 | AESaux[i] ^= *buf; 74 | } 75 | 76 | if (len == 0) { 77 | // Final block, xor with K1 or K2. K1 and K2 are calculated 78 | // by encrypting the all-zeroes block and then applying some 79 | // shifts and xor on that. 80 | u1_t final_key[16]; 81 | memset(final_key, 0, sizeof(final_key)); 82 | lmic_aes_encrypt(final_key, AESkey); 83 | 84 | // Calculate K1 85 | u1_t msb = final_key[0] & 0x80; 86 | shift_left(final_key, sizeof(final_key)); 87 | if (msb) 88 | final_key[sizeof(final_key)-1] ^= 0x87; 89 | 90 | // If the final block was not complete, calculate K2 from K1 91 | if (need_padding) { 92 | msb = final_key[0] & 0x80; 93 | shift_left(final_key, sizeof(final_key)); 94 | if (msb) 95 | final_key[sizeof(final_key)-1] ^= 0x87; 96 | } 97 | 98 | // Xor with K1 or K2 99 | for (u1_t i = 0; i < sizeof(final_key); ++i) 100 | AESaux[i] ^= final_key[i]; 101 | } 102 | 103 | lmic_aes_encrypt(AESaux, AESkey); 104 | } 105 | } 106 | 107 | // Run AES-CTR using the key in AESKEY and using AESAUX as the 108 | // counter block. The last byte of the counter block will be incremented 109 | // for every block. The given buffer will be encrypted in place. 110 | static void os_aes_ctr (xref2u1_t buf, u2_t len) { 111 | u1_t ctr[16]; 112 | while (len) { 113 | // Encrypt the counter block with the selected key 114 | memcpy(ctr, AESaux, sizeof(ctr)); 115 | lmic_aes_encrypt(ctr, AESkey); 116 | 117 | // Xor the payload with the resulting ciphertext 118 | for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) 119 | *buf ^= ctr[i]; 120 | 121 | // Increment the block index byte 122 | AESaux[15]++; 123 | } 124 | } 125 | 126 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) { 127 | switch (mode & ~AES_MICNOAUX) { 128 | case AES_MIC: 129 | os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); 130 | return os_rmsbf4(AESaux); 131 | 132 | case AES_ENC: 133 | // TODO: Check / handle when len is not a multiple of 16 134 | for (u1_t i = 0; i < len; i += 16) 135 | lmic_aes_encrypt(buf+i, AESkey); 136 | break; 137 | 138 | case AES_CTR: 139 | os_aes_ctr(buf, len); 140 | break; 141 | } 142 | return 0; 143 | } 144 | 145 | #endif // !defined(USE_ORIGINAL_AES) 146 | -------------------------------------------------------------------------------- /src/hal/hal.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | 11 | #include 12 | #include 13 | #include "../lmic.h" 14 | #include "hal.h" 15 | #include 16 | 17 | // ----------------------------------------------------------------------------- 18 | // I/O 19 | 20 | static void hal_interrupt_init(); // Fwd declaration 21 | 22 | static void hal_io_init () { 23 | // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK 24 | ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); 25 | ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); 26 | ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); 27 | 28 | pinMode(lmic_pins.nss, OUTPUT); 29 | if (lmic_pins.rxtx != LMIC_UNUSED_PIN) 30 | pinMode(lmic_pins.rxtx, OUTPUT); 31 | if (lmic_pins.rst != LMIC_UNUSED_PIN) 32 | pinMode(lmic_pins.rst, OUTPUT); 33 | 34 | hal_interrupt_init(); 35 | } 36 | 37 | // val == 1 => tx 1 38 | void hal_pin_rxtx (u1_t val) { 39 | if (lmic_pins.rxtx != LMIC_UNUSED_PIN) 40 | digitalWrite(lmic_pins.rxtx, val); 41 | } 42 | 43 | // set radio RST pin to given value (or keep floating!) 44 | void hal_pin_rst (u1_t val) { 45 | if (lmic_pins.rst == LMIC_UNUSED_PIN) 46 | return; 47 | 48 | if(val == 0 || val == 1) { // drive pin 49 | pinMode(lmic_pins.rst, OUTPUT); 50 | digitalWrite(lmic_pins.rst, val); 51 | } else { // keep pin floating 52 | pinMode(lmic_pins.rst, INPUT); 53 | } 54 | } 55 | 56 | #if !defined(LMIC_USE_INTERRUPTS) 57 | static void hal_interrupt_init() { 58 | pinMode(lmic_pins.dio[0], INPUT); 59 | if (lmic_pins.dio[1] != LMIC_UNUSED_PIN) 60 | pinMode(lmic_pins.dio[1], INPUT); 61 | if (lmic_pins.dio[2] != LMIC_UNUSED_PIN) 62 | pinMode(lmic_pins.dio[2], INPUT); 63 | } 64 | 65 | static bool dio_states[NUM_DIO] = {0}; 66 | static void hal_io_check() { 67 | uint8_t i; 68 | for (i = 0; i < NUM_DIO; ++i) { 69 | if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) 70 | continue; 71 | 72 | if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { 73 | dio_states[i] = !dio_states[i]; 74 | if (dio_states[i]) 75 | radio_irq_handler(i); 76 | } 77 | } 78 | } 79 | 80 | #else 81 | // Interrupt handlers 82 | static bool interrupt_flags[NUM_DIO] = {0}; 83 | 84 | static void hal_isrPin0() { 85 | interrupt_flags[0] = true; 86 | } 87 | static void hal_isrPin1() { 88 | interrupt_flags[1] = true; 89 | } 90 | static void hal_isrPin2() { 91 | interrupt_flags[2] = true; 92 | } 93 | 94 | typedef void (*isr_t)(); 95 | static isr_t interrupt_fns[NUM_DIO] = {hal_isrPin0, hal_isrPin1, hal_isrPin2}; 96 | 97 | static void hal_interrupt_init() { 98 | for (uint8_t i = 0; i < NUM_DIO; ++i) { 99 | if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) 100 | continue; 101 | 102 | attachInterrupt(digitalPinToInterrupt(lmic_pins.dio[i]), interrupt_fns[i], RISING); 103 | } 104 | } 105 | 106 | static void hal_io_check() { 107 | uint8_t i; 108 | for (i = 0; i < NUM_DIO; ++i) { 109 | if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) 110 | continue; 111 | 112 | if (interrupt_flags[i]) { 113 | interrupt_flags[i] = false; 114 | radio_irq_handler(i); 115 | } 116 | } 117 | } 118 | #endif // LMIC_USE_INTERRUPTS 119 | 120 | // ----------------------------------------------------------------------------- 121 | // SPI 122 | 123 | static const SPISettings settings(LMIC_SPI_FREQ, MSBFIRST, SPI_MODE0); 124 | 125 | static void hal_spi_init () { 126 | SPI.begin(); 127 | } 128 | 129 | void hal_pin_nss (u1_t val) { 130 | if (!val) 131 | SPI.beginTransaction(settings); 132 | else 133 | SPI.endTransaction(); 134 | 135 | //Serial.println(val?">>":"<<"); 136 | digitalWrite(lmic_pins.nss, val); 137 | } 138 | 139 | // perform SPI transaction with radio 140 | u1_t hal_spi (u1_t out) { 141 | u1_t res = SPI.transfer(out); 142 | /* 143 | Serial.print(">"); 144 | Serial.print(out, HEX); 145 | Serial.print("<"); 146 | Serial.println(res, HEX); 147 | */ 148 | return res; 149 | } 150 | 151 | // ----------------------------------------------------------------------------- 152 | // TIME 153 | 154 | static void hal_time_init () { 155 | // Nothing to do 156 | } 157 | 158 | u4_t hal_ticks () { 159 | // Because micros() is scaled down in this function, micros() will 160 | // overflow before the tick timer should, causing the tick timer to 161 | // miss a significant part of its values if not corrected. To fix 162 | // this, the "overflow" serves as an overflow area for the micros() 163 | // counter. It consists of three parts: 164 | // - The US_PER_OSTICK upper bits are effectively an extension for 165 | // the micros() counter and are added to the result of this 166 | // function. 167 | // - The next bit overlaps with the most significant bit of 168 | // micros(). This is used to detect micros() overflows. 169 | // - The remaining bits are always zero. 170 | // 171 | // By comparing the overlapping bit with the corresponding bit in 172 | // the micros() return value, overflows can be detected and the 173 | // upper bits are incremented. This is done using some clever 174 | // bitwise operations, to remove the need for comparisons and a 175 | // jumps, which should result in efficient code. By avoiding shifts 176 | // other than by multiples of 8 as much as possible, this is also 177 | // efficient on AVR (which only has 1-bit shifts). 178 | static uint8_t overflow = 0; 179 | 180 | // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, 181 | // the others will be the lower bits of our return value. 182 | uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; 183 | // Most significant byte of scaled 184 | uint8_t msb = scaled >> 24; 185 | // Mask pointing to the overlapping bit in msb and overflow. 186 | const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); 187 | // Update overflow. If the overlapping bit is different 188 | // between overflow and msb, it is added to the stored value, 189 | // so the overlapping bit becomes equal again and, if it changed 190 | // from 1 to 0, the upper bits are incremented. 191 | overflow += (msb ^ overflow) & mask; 192 | 193 | // Return the scaled value with the upper bits of stored added. The 194 | // overlapping bit will be equal and the lower bits will be 0, so 195 | // bitwise or is a no-op for them. 196 | return scaled | ((uint32_t)overflow << 24); 197 | 198 | // 0 leads to correct, but overly complex code (it could just return 199 | // micros() unmodified), 8 leaves no room for the overlapping bit. 200 | static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); 201 | } 202 | 203 | // Returns the number of ticks until time. Negative values indicate that 204 | // time has already passed. 205 | static s4_t delta_time(u4_t time) { 206 | return (s4_t)(time - hal_ticks()); 207 | } 208 | 209 | void hal_waitUntil (u4_t time) { 210 | s4_t delta = delta_time(time); 211 | // From delayMicroseconds docs: Currently, the largest value that 212 | // will produce an accurate delay is 16383. 213 | while (delta > (16000 / US_PER_OSTICK)) { 214 | delay(16); 215 | delta -= (16000 / US_PER_OSTICK); 216 | } 217 | if (delta > 0) 218 | delayMicroseconds(delta * US_PER_OSTICK); 219 | } 220 | 221 | // check and rewind for target time 222 | u1_t hal_checkTimer (u4_t time) { 223 | // No need to schedule wakeup, since we're not sleeping 224 | return delta_time(time) <= 0; 225 | } 226 | 227 | static uint8_t irqlevel = 0; 228 | 229 | void hal_disableIRQs () { 230 | noInterrupts(); 231 | irqlevel++; 232 | } 233 | 234 | void hal_enableIRQs () { 235 | if(--irqlevel == 0) { 236 | interrupts(); 237 | 238 | // Instead of using proper interrupts (which are a bit tricky 239 | // and/or not available on all pins on AVR), just poll the pin 240 | // values. Since os_runloop disables and re-enables interrupts, 241 | // putting this here makes sure we check at least once every 242 | // loop. 243 | // 244 | // As an additional bonus, this prevents the can of worms that 245 | // we would otherwise get for running SPI transfers inside ISRs 246 | hal_io_check(); 247 | } 248 | } 249 | 250 | void hal_sleep () { 251 | // Not implemented 252 | } 253 | 254 | // ----------------------------------------------------------------------------- 255 | 256 | #if defined(LMIC_PRINTF_TO) 257 | static int uart_putchar (char c, FILE *) 258 | { 259 | LMIC_PRINTF_TO.write(c) ; 260 | return 0 ; 261 | } 262 | 263 | void hal_printf_init() { 264 | // create a FILE structure to reference our UART output function 265 | static FILE uartout; 266 | memset(&uartout, 0, sizeof(uartout)); 267 | 268 | // fill in the UART file descriptor with pointer to writer. 269 | fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); 270 | 271 | // The uart is the standard output device STDOUT. 272 | stdout = &uartout ; 273 | } 274 | #endif // defined(LMIC_PRINTF_TO) 275 | 276 | void hal_init () { 277 | // configure radio I/O and interrupt handler 278 | hal_io_init(); 279 | // configure radio SPI 280 | hal_spi_init(); 281 | // configure timer and interrupt handler 282 | hal_time_init(); 283 | #if defined(LMIC_PRINTF_TO) 284 | // printf support 285 | hal_printf_init(); 286 | #endif 287 | } 288 | 289 | void hal_failed (const char *file, u2_t line) { 290 | #if defined(LMIC_FAILURE_TO) 291 | LMIC_FAILURE_TO.println("FAILURE "); 292 | LMIC_FAILURE_TO.print(file); 293 | LMIC_FAILURE_TO.print(':'); 294 | LMIC_FAILURE_TO.println(line); 295 | LMIC_FAILURE_TO.flush(); 296 | #endif 297 | hal_disableIRQs(); 298 | while(1); 299 | } 300 | -------------------------------------------------------------------------------- /src/hal/hal.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | #ifndef _hal_hal_h_ 11 | #define _hal_hal_h_ 12 | 13 | static const int NUM_DIO = 3; 14 | 15 | struct lmic_pinmap { 16 | u1_t nss; 17 | u1_t rxtx; 18 | u1_t rst; 19 | u1_t dio[NUM_DIO]; 20 | }; 21 | 22 | // Use this for any unused pins. 23 | const u1_t LMIC_UNUSED_PIN = 0xff; 24 | 25 | // Declared here, to be defined an initialized by the application 26 | extern const lmic_pinmap lmic_pins; 27 | 28 | #endif // _hal_hal_h_ 29 | -------------------------------------------------------------------------------- /src/lmic.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C"{ 3 | #endif 4 | 5 | #include "lmic/lmic.h" 6 | 7 | #ifdef __cplusplus 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /src/lmic/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _lmic_config_h_ 2 | #define _lmic_config_h_ 3 | 4 | // In the original LMIC code, these config values were defined on the 5 | // gcc commandline. Since Arduino does not allow easily modifying the 6 | // compiler commandline, use this file to load project defaults and then fall back. 7 | // Note that you should not edit this file, because then your changes will 8 | // be applied to a globally-distributed file. Instead, create an 9 | // lmic_project_config.h file. 10 | 11 | // if you want to override this, put a lora_project_config.h in your sketch directory. 12 | // otherwise the lmic_project_config.h from this directory will be used. 13 | #include "../../project_config/lmic_project_config.h" 14 | 15 | #if ! (defined(CFG_eu868) || defined(CFG_us915)) 16 | # warning Target RF configuration not defined, assuming CFG_eu868 17 | #define CFG_eu868 1 18 | #elif defined(CFG_eu868) && defined(CFG_us915) 19 | # error You can define at most one of CFG_eu868 and CFG_us915 20 | #endif 21 | 22 | // This is the SX1272/SX1273 radio, which is also used on the HopeRF 23 | // RFM92 boards. 24 | //#define CFG_sx1272_radio 1 25 | // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on 26 | // the HopeRF RFM95 boards. 27 | #if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio)) 28 | # warning Target radio not defined, assuming CFG_sx1276_radio 29 | #define CFG_sx1276_radio 1 30 | #elif defined(CFG_sx1272_radio) && defined(CFG_sx1276_radio) 31 | # error You can define at most one of CFG_sx1272_radio and CF_sx1276_radio 32 | #endif 33 | 34 | // LMIC requires ticks to be 15.5μs - 100 μs long 35 | #ifndef OSTICKS_PER_SEC 36 | // 16 μs per tick 37 | # ifndef US_PER_OSTICK_EXPONENT 38 | # define US_PER_OSTICK_EXPONENT 4 39 | # endif 40 | # define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) 41 | # define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) 42 | #endif /* OSTICKS_PER_SEC */ 43 | 44 | #if ! (10000 <= OSTICKS_PER_SEC && OSTICKS_PER_SEC < 64516) 45 | # error LMIC requires ticks to be 15.5 us to 100 us long 46 | #endif 47 | 48 | // Change the SPI clock speed if you encounter errors 49 | // communicating with the radio. 50 | // The standard range is 125kHz-8MHz, but some boards can go faster. 51 | #ifndef LMIC_SPI_FREQ 52 | #define LMIC_SPI_FREQ 1E6 53 | #endif 54 | 55 | // Set this to 1 to enable some basic debug output (using printf) about 56 | // RF settings used during transmission and reception. Set to 2 to 57 | // enable more verbose output. Make sure that printf is actually 58 | // configured (e.g. on AVR it is not by default), otherwise using it can 59 | // cause crashing. 60 | #ifndef LMIC_DEBUG_LEVEL 61 | #define LMIC_DEBUG_LEVEL 0 62 | #endif 63 | 64 | // Enable this to allow using printf() to print to the given serial port 65 | // (or any other Print object). This can be easy for debugging. The 66 | // current implementation only works on AVR, though. 67 | //#define LMIC_PRINTF_TO Serial 68 | 69 | // Enable this to use interrupt handler routines listening for RISING signals. 70 | // Otherwise, the library polls digital input lines for changes. 71 | //#define LMIC_USE_INTERRUPTS 72 | 73 | // If DISABLE_LMIC_FAILURE_TO is defined, runtime assertion failures 74 | // silently halt execution. Otherwise, LMIC_FAILURE_TO should be defined 75 | // as the name of an object derived from Print, which will be used for 76 | // displaying runtime assertion failures. If you say nothing in your 77 | // lmic_project_config.h, runtime assertion failures are displayed 78 | // using the Serial object. 79 | #if ! defined(DISABLE_LMIC_FAILURE_TO) && ! defined(LMIC_FAILURE_TO) 80 | #define LMIC_FAILURE_TO Serial 81 | #endif 82 | 83 | // define this in lmic_project_config.h to disable all code related to joining 84 | //#define DISABLE_JOIN 85 | // define this in lmic_project_config.h to disable all code related to ping 86 | //#define DISABLE_PING 87 | // define this in lmic_project_config.h to disable all code related to beacon tracking. 88 | // Requires ping to be disabled too 89 | //#define DISABLE_BEACONS 90 | 91 | // define these in lmic_project_config.h to disable the corresponding MAC commands. 92 | // Class A 93 | //#define DISABLE_MCMD_DCAP_REQ // duty cycle cap 94 | //#define DISABLE_MCMD_DN2P_SET // 2nd DN window param 95 | //#define DISABLE_MCMD_SNCH_REQ // set new channel 96 | // Class B 97 | //#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING 98 | //#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatically disabled by DISABLE_BEACON 99 | 100 | // In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the 101 | // same on RX. This ensures that gateways can talk to nodes and vice 102 | // versa, but gateways will not hear other gateways and nodes will not 103 | // hear other nodes. By defining this macro in lmic_project_config.h, 104 | // this inversion is disabled and this node can hear other nodes. If 105 | // two nodes both have this macro set, they can talk to each other 106 | // (but they can no longer hear gateways). This should probably only 107 | // be used when debugging and/or when talking to the radio directly 108 | // (e.g. like in the "raw" example). 109 | //#define DISABLE_INVERT_IQ_ON_RX 110 | 111 | // This allows choosing between multiple included AES implementations. 112 | // Make sure exactly one of these is uncommented. 113 | // 114 | // This selects the original AES implementation included LMIC. This 115 | // implementation is optimized for speed on 32-bit processors using 116 | // fairly big lookup tables, but it takes up big amounts of flash on the 117 | // AVR architecture. 118 | // #define USE_ORIGINAL_AES 119 | // 120 | // This selects the AES implementation written by Ideetroon for their 121 | // own LoRaWAN library. It also uses lookup tables, but smaller 122 | // byte-oriented ones, making it use a lot less flash space (but it is 123 | // also about twice as slow as the original). 124 | // #define USE_IDEETRON_AES 125 | 126 | #if ! (defined(USE_ORIGINAL_AES) || defined(USE_IDEETRON_AES)) 127 | # define USE_IDEETRON_AES 128 | #endif 129 | 130 | #if defined(USE_ORIGINAL_AES) && defined(USE_IDEETRON_AES) 131 | # error "You may define at most one of USE_ORIGINAL_AES and USE_IDEETRON_AES" 132 | #endif 133 | #endif // _lmic_config_h_ 134 | -------------------------------------------------------------------------------- /src/lmic/hal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _hal_hpp_ 29 | #define _hal_hpp_ 30 | 31 | #ifdef __cplusplus 32 | extern "C"{ 33 | #endif 34 | 35 | /* 36 | * initialize hardware (IO, SPI, TIMER, IRQ). 37 | */ 38 | void hal_init (void); 39 | 40 | /* 41 | * drive radio NSS pin (0=low, 1=high). 42 | */ 43 | void hal_pin_nss (u1_t val); 44 | 45 | /* 46 | * drive radio RX/TX pins (0=rx, 1=tx). 47 | */ 48 | void hal_pin_rxtx (u1_t val); 49 | 50 | /* 51 | * control radio RST pin (0=low, 1=high, 2=floating) 52 | */ 53 | void hal_pin_rst (u1_t val); 54 | 55 | /* 56 | * perform 8-bit SPI transaction with radio. 57 | * - write given byte 'outval' 58 | * - read byte and return value 59 | */ 60 | u1_t hal_spi (u1_t outval); 61 | 62 | /* 63 | * disable all CPU interrupts. 64 | * - might be invoked nested 65 | * - will be followed by matching call to hal_enableIRQs() 66 | */ 67 | void hal_disableIRQs (void); 68 | 69 | /* 70 | * enable CPU interrupts. 71 | */ 72 | void hal_enableIRQs (void); 73 | 74 | /* 75 | * put system and CPU in low-power mode, sleep until interrupt. 76 | */ 77 | void hal_sleep (void); 78 | 79 | /* 80 | * return 32-bit system time in ticks. 81 | */ 82 | u4_t hal_ticks (void); 83 | 84 | /* 85 | * busy-wait until specified timestamp (in ticks) is reached. 86 | */ 87 | void hal_waitUntil (u4_t time); 88 | 89 | /* 90 | * check and rewind timer for target time. 91 | * - return 1 if target time is close 92 | * - otherwise rewind timer for target time or full period and return 0 93 | */ 94 | u1_t hal_checkTimer (u4_t targettime); 95 | 96 | /* 97 | * perform fatal failure action. 98 | * - called by assertions 99 | * - action could be HALT or reboot 100 | */ 101 | void hal_failed (const char *file, u2_t line); 102 | 103 | #ifdef __cplusplus 104 | } // extern "C" 105 | #endif 106 | 107 | #endif // _hal_hpp_ 108 | -------------------------------------------------------------------------------- /src/lmic/lmic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | //! @file 29 | //! @brief LMIC API 30 | 31 | #ifndef _lmic_h_ 32 | #define _lmic_h_ 33 | 34 | #include "oslmic.h" 35 | #include "lorabase.h" 36 | 37 | #if LMIC_DEBUG_LEVEL > 0 38 | # if defined(LMIC_DEBUG_INCLUDE) 39 | # define LMIC_STRINGIFY_(x) #x 40 | # define LMIC_STRINGIFY(x) LMIC_STRINGIFY_(x) 41 | # include LMIC_STRINGIFY(LMIC_DEBUG_INCLUDE) 42 | # endif 43 | // if LMIC_DEBUG_PRINTF is now defined, just use it. This lets you do anything 44 | // you like with a sufficiently crazy header file. 45 | # ifndef LMIC_DEBUG_PRINTF 46 | // otherwise, check whether someone configured a print-function to be used, 47 | // and use it if so. 48 | # ifdef LMIC_DEBUG_PRINTF_FN 49 | # define LMIC_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__) 50 | # else // ndef LMIC_DEBUG_PRINTF_FN 51 | // if there's no other info, just use printf. In a pure Arduino environment, 52 | // that's what will happen. 53 | # define LMIC_DEBUG_PRINTF(f, ...) printf(f, ## __VA_ARGS__) 54 | # endif // ndef LMIC_DEBUG_PRINTF_FN 55 | # endif // ndef LMIC_DEBUG_PRINTF 56 | #endif // LMIC_DEBUG_LEVEL > 0 57 | 58 | #ifdef __cplusplus 59 | extern "C"{ 60 | #endif 61 | 62 | // LMIC version 63 | #define LMIC_VERSION_MAJOR 1 64 | #define LMIC_VERSION_MINOR 6 65 | #define LMIC_VERSION_BUILD 1468577746 66 | 67 | //! Only For Antenna Tuning Tests ! 68 | //#define CFG_TxContinuousMode 1 69 | 70 | enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length 71 | enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames 72 | enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests 73 | enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this 74 | 75 | enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link 76 | LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead 77 | LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate 78 | LINK_CHECK_OFF =-128 }; // link check disabled 79 | 80 | enum { TIME_RESYNC = 6*128 }; // secs 81 | enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon 82 | enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon 83 | enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time 84 | enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send 85 | 86 | #if defined(CFG_eu868) // EU868 spectrum ==================================================== 87 | 88 | enum { MAX_CHANNELS = 16 }; //!< Max supported channels 89 | enum { MAX_BANDS = 4 }; 90 | 91 | enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels 92 | //! \internal 93 | struct band_t { 94 | u2_t txcap; // duty cycle limitation: 1/txcap 95 | s1_t txpow; // maximum TX power 96 | u1_t lastchnl; // last used channel 97 | ostime_t avail; // channel is blocked until this time 98 | }; 99 | TYPEDEF_xref2band_t; //!< \internal 100 | 101 | #elif defined(CFG_us915) // US915 spectrum ================================================= 102 | 103 | enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable 104 | enum { MAX_TXPOW_125kHz = 30 }; 105 | 106 | #endif // ========================================================================== 107 | 108 | // Keep in sync with evdefs.hpp::drChange 109 | enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD }; 110 | enum { KEEP_TXPOW = -128 }; 111 | 112 | 113 | #if !defined(DISABLE_PING) 114 | //! \internal 115 | struct rxsched_t { 116 | u1_t dr; 117 | u1_t intvExp; // 0..7 118 | u1_t slot; // runs from 0 to 128 119 | u1_t rxsyms; 120 | ostime_t rxbase; 121 | ostime_t rxtime; // start of next spot 122 | u4_t freq; 123 | }; 124 | TYPEDEF_xref2rxsched_t; //!< \internal 125 | #endif // !DISABLE_PING 126 | 127 | 128 | #if !defined(DISABLE_BEACONS) 129 | //! Parsing and tracking states of beacons. 130 | enum { BCN_NONE = 0x00, //!< No beacon received 131 | BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) 132 | BCN_FULL = 0x02, //!< Full beacon decoded 133 | BCN_NODRIFT = 0x04, //!< No drift value measured yet 134 | BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet 135 | //! Information about the last and previous beacons. 136 | struct bcninfo_t { 137 | ostime_t txtime; //!< Time when the beacon was sent 138 | s1_t rssi; //!< Adjusted RSSI value of last received beacon 139 | s1_t snr; //!< Scaled SNR value of last received beacon 140 | u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values. 141 | u4_t time; //!< GPS time in seconds of last beacon (received or surrogate) 142 | // 143 | u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set) 144 | s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) 145 | s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) 146 | }; 147 | #endif // !DISABLE_BEACONS 148 | 149 | // purpose of receive window - lmic_t.rxState 150 | enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 }; 151 | // Netid values / lmic_t.netid 152 | enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; 153 | // MAC operation modes (lmic_t.opmode). 154 | enum { OP_NONE = 0x0000, 155 | OP_SCAN = 0x0001, // radio scan to find a beacon 156 | OP_TRACK = 0x0002, // track my networks beacon (netid) 157 | OP_JOINING = 0x0004, // device joining in progress (blocks other activities) 158 | OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData) 159 | OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data 160 | OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST 161 | OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything 162 | OP_TXRXPEND = 0x0080, // TX/RX transaction pending 163 | OP_RNDTX = 0x0100, // prevent TX lining up after a beacon 164 | OP_PINGINI = 0x0200, // pingable is initialized and scheduling active 165 | OP_PINGABLE = 0x0400, // we're pingable 166 | OP_NEXTCHNL = 0x0800, // find a new channel 167 | OP_LINKDEAD = 0x1000, // link was reported as dead 168 | OP_TESTMODE = 0x2000, // developer test mode 169 | }; 170 | // TX-RX transaction flags - report back to user 171 | enum { TXRX_ACK = 0x80, // confirmed UP frame was acked 172 | TXRX_NACK = 0x40, // confirmed UP frame was not acked 173 | TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port 174 | TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port 175 | TXRX_DNW1 = 0x01, // received in 1st DN slot 176 | TXRX_DNW2 = 0x02, // received in 2dn DN slot 177 | TXRX_PING = 0x04 }; // received in a scheduled RX slot 178 | // Event types for event callback 179 | enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, 180 | EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING, 181 | EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED, 182 | EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, 183 | EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND, 184 | EV_TXSTART }; 185 | typedef enum _ev_t ev_t; 186 | 187 | enum { 188 | // This value represents 100% error in LMIC.clockError 189 | MAX_CLOCK_ERROR = 65536, 190 | }; 191 | 192 | struct lmic_t { 193 | // Radio settings TX/RX (also accessed by HAL) 194 | ostime_t txend; 195 | ostime_t rxtime; 196 | u4_t freq; 197 | s1_t rssi; 198 | s1_t snr; 199 | rps_t rps; 200 | u1_t rxsyms; 201 | u1_t dndr; 202 | s1_t txpow; // dBm 203 | 204 | osjob_t osjob; 205 | 206 | // Channel scheduling 207 | #if defined(CFG_eu868) 208 | band_t bands[MAX_BANDS]; 209 | u4_t channelFreq[MAX_CHANNELS]; 210 | u2_t channelDrMap[MAX_CHANNELS]; 211 | u2_t channelMap; 212 | #elif defined(CFG_us915) 213 | u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) 214 | u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto 215 | u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits 216 | u2_t activeChannels125khz; 217 | u2_t activeChannels500khz; 218 | #endif 219 | u1_t txChnl; // channel for next TX 220 | u1_t globalDutyRate; // max rate: 1/2^k 221 | ostime_t globalDutyAvail; // time device can send again 222 | 223 | u4_t netid; // current network id (~0 - none) 224 | u2_t opmode; 225 | u1_t upRepeat; // configured up repeat 226 | s1_t adrTxPow; // ADR adjusted TX power 227 | u1_t datarate; // current data rate 228 | u1_t errcr; // error coding rate (used for TX only) 229 | u1_t rejoinCnt; // adjustment for rejoin datarate 230 | #if !defined(DISABLE_BEACONS) 231 | s2_t drift; // last measured drift 232 | s2_t lastDriftDiff; 233 | s2_t maxDriftDiff; 234 | #endif 235 | 236 | u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX 237 | // represents +/-100% error 238 | 239 | u1_t pendTxPort; 240 | u1_t pendTxConf; // confirmed data 241 | u1_t pendTxLen; // +0x80 = confirmed 242 | u1_t pendTxData[MAX_LEN_PAYLOAD]; 243 | 244 | u2_t devNonce; // last generated nonce 245 | u1_t nwkKey[16]; // network session key 246 | u1_t artKey[16]; // application router session key 247 | devaddr_t devaddr; 248 | u4_t seqnoDn; // device level down stream seqno 249 | u4_t seqnoUp; 250 | 251 | u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0 252 | s1_t adrAckReq; // counter until we reset data rate (0=off) 253 | u1_t adrChanged; 254 | 255 | u1_t rxDelay; // Rx delay after TX 256 | 257 | u1_t margin; 258 | bit_t ladrAns; // link adr adapt answer pending 259 | bit_t devsAns; // device status answer pending 260 | u1_t adrEnabled; 261 | u1_t moreData; // NWK has more data pending 262 | #if !defined(DISABLE_MCMD_DCAP_REQ) 263 | bit_t dutyCapAns; // have to ACK duty cycle settings 264 | #endif 265 | #if !defined(DISABLE_MCMD_SNCH_REQ) 266 | u1_t snchAns; // answer set new channel 267 | #endif 268 | // 2nd RX window (after up stream) 269 | u1_t dn2Dr; 270 | u4_t dn2Freq; 271 | #if !defined(DISABLE_MCMD_DN2P_SET) 272 | u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs 273 | #endif 274 | 275 | // Class B state 276 | #if !defined(DISABLE_BEACONS) 277 | u1_t missedBcns; // unable to track last N beacons 278 | u1_t bcninfoTries; // how often to try (scan mode only) 279 | #endif 280 | #if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) 281 | u1_t pingSetAns; // answer set cmd and ACK bits 282 | #endif 283 | #if !defined(DISABLE_PING) 284 | rxsched_t ping; // pingable setup 285 | #endif 286 | 287 | // Public part of MAC state 288 | u1_t txCnt; 289 | u1_t txrxFlags; // transaction flags (TX-RX combo) 290 | u1_t dataBeg; // 0 or start of data (dataBeg-1 is port) 291 | u1_t dataLen; // 0 no data or zero length data, >0 byte count of data 292 | u1_t frame[MAX_LEN_FRAME]; 293 | 294 | #if !defined(DISABLE_BEACONS) 295 | u1_t bcnChnl; 296 | u1_t bcnRxsyms; // 297 | ostime_t bcnRxtime; 298 | bcninfo_t bcninfo; // Last received beacon info 299 | #endif 300 | 301 | u1_t noRXIQinversion; 302 | }; 303 | //! \var struct lmic_t LMIC 304 | //! The state of LMIC MAC layer is encapsulated in this variable. 305 | DECLARE_LMIC; //!< \internal 306 | 307 | //! Construct a bit map of allowed datarates from drlo to drhi (both included). 308 | #define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi)))) 309 | #if defined(CFG_eu868) 310 | enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 }; 311 | bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap); 312 | #endif 313 | bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band); 314 | void LMIC_disableChannel (u1_t channel); 315 | #if defined(CFG_us915) 316 | void LMIC_enableChannel (u1_t channel); 317 | void LMIC_enableSubBand (u1_t band); 318 | void LMIC_disableSubBand (u1_t band); 319 | void LMIC_selectSubBand (u1_t band); 320 | #endif 321 | 322 | void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow 323 | void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) 324 | #if !defined(DISABLE_JOIN) 325 | bit_t LMIC_startJoining (void); 326 | #endif 327 | 328 | void LMIC_shutdown (void); 329 | void LMIC_init (void); 330 | void LMIC_reset (void); 331 | void LMIC_clrTxData (void); 332 | void LMIC_setTxData (void); 333 | int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed); 334 | void LMIC_sendAlive (void); 335 | 336 | #if !defined(DISABLE_BEACONS) 337 | bit_t LMIC_enableTracking (u1_t tryBcnInfo); 338 | void LMIC_disableTracking (void); 339 | #endif 340 | 341 | #if !defined(DISABLE_PING) 342 | void LMIC_stopPingable (void); 343 | void LMIC_setPingable (u1_t intvExp); 344 | #endif 345 | #if !defined(DISABLE_JOIN) 346 | void LMIC_tryRejoin (void); 347 | #endif 348 | 349 | void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey); 350 | void LMIC_setLinkCheckMode (bit_t enabled); 351 | void LMIC_setClockError(u2_t error); 352 | 353 | u4_t LMIC_getSeqnoUp (void); 354 | u4_t LMIC_setSeqnoUp (u4_t); 355 | void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey); 356 | 357 | // Declare onEvent() function, to make sure any definition will have the 358 | // C conventions, even when in a C++ file. 359 | DECL_ON_LMIC_EVENT; 360 | 361 | 362 | 363 | // Special APIs - for development or testing 364 | // !!!See implementation for caveats!!! 365 | 366 | #ifdef __cplusplus 367 | } // extern "C" 368 | #endif 369 | 370 | #endif // _lmic_h_ 371 | -------------------------------------------------------------------------------- /src/lmic/lorabase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _lorabase_h_ 29 | #define _lorabase_h_ 30 | 31 | #ifdef __cplusplus 32 | extern "C"{ 33 | #endif 34 | 35 | // ================================================================================ 36 | // BEG: Keep in sync with lorabase.hpp 37 | // 38 | 39 | enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; 40 | enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; 41 | enum _bw_t { BW125=0, BW250, BW500, BWrfu }; 42 | typedef u1_t cr_t; 43 | typedef u1_t sf_t; 44 | typedef u1_t bw_t; 45 | typedef u1_t dr_t; 46 | // Radio parameter set (encodes SF/BW/CR/IH/NOCRC) 47 | typedef u2_t rps_t; 48 | TYPEDEF_xref2rps_t; 49 | 50 | enum { ILLEGAL_RPS = 0xFF }; 51 | enum { DR_PAGE_EU868 = 0x00 }; 52 | enum { DR_PAGE_US915 = 0x10 }; 53 | 54 | // Global maximum frame length 55 | enum { STD_PREAMBLE_LEN = 8 }; 56 | enum { MAX_LEN_FRAME = 64 }; 57 | enum { LEN_DEVNONCE = 2 }; 58 | enum { LEN_ARTNONCE = 3 }; 59 | enum { LEN_NETID = 3 }; 60 | enum { DELAY_JACC1 = 5 }; // in secs 61 | enum { DELAY_DNW1 = 1 }; // in secs down window #1 62 | enum { DELAY_EXTDNW2 = 1 }; // in secs 63 | enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs 64 | enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1 65 | enum { BCN_INTV_exp = 7 }; 66 | enum { BCN_INTV_sec = 1<> 3) & 0x3); } 366 | inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); } 367 | inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); } 368 | inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); } 369 | inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); } 370 | inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } 371 | inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } 372 | inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } 373 | inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { 374 | return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); 375 | } 376 | #define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) 377 | // Two frames with params r1/r2 would interfere on air: same SFx + BWx 378 | inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } 379 | 380 | extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; 381 | inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } 382 | inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } 383 | inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } 384 | inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } 385 | 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 386 | inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate 387 | 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 388 | inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range 389 | inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps 390 | 391 | // 392 | // BEG: Keep in sync with lorabase.hpp 393 | // ================================================================================ 394 | 395 | 396 | // Convert between dBm values and power codes (MCMD_LADR_XdBm) 397 | s1_t pow2dBm (u1_t mcmd_ladr_p1); 398 | // Calculate airtime 399 | ostime_t calcAirTime (rps_t rps, u1_t plen); 400 | // Sensitivity at given SF/BW 401 | int getSensitivity (rps_t rps); 402 | 403 | #ifdef __cplusplus 404 | } // extern "C" 405 | #endif 406 | 407 | #endif // _lorabase_h_ 408 | -------------------------------------------------------------------------------- /src/lmic/oslmic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "lmic.h" 29 | 30 | // RUNTIME STATE 31 | static struct { 32 | osjob_t* scheduledjobs; 33 | osjob_t* runnablejobs; 34 | } OS; 35 | 36 | void os_init () { 37 | memset(&OS, 0x00, sizeof(OS)); 38 | hal_init(); 39 | radio_init(); 40 | LMIC_init(); 41 | } 42 | 43 | ostime_t os_getTime () { 44 | return hal_ticks(); 45 | } 46 | 47 | // unlink job from queue, return if removed 48 | static int unlinkjob (osjob_t** pnext, osjob_t* job) { 49 | for( ; *pnext; pnext = &((*pnext)->next)) { 50 | if(*pnext == job) { // unlink 51 | *pnext = job->next; 52 | return 1; 53 | } 54 | } 55 | return 0; 56 | } 57 | 58 | // clear scheduled job 59 | void os_clearCallback (osjob_t* job) { 60 | hal_disableIRQs(); 61 | unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job); 62 | hal_enableIRQs(); 63 | } 64 | 65 | // schedule immediately runnable job 66 | void os_setCallback (osjob_t* job, osjobcb_t cb) { 67 | osjob_t** pnext; 68 | hal_disableIRQs(); 69 | // remove if job was already queued 70 | unlinkjob(&OS.runnablejobs, job); 71 | // fill-in job 72 | job->func = cb; 73 | job->next = NULL; 74 | // add to end of run queue 75 | for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next)); 76 | *pnext = job; 77 | hal_enableIRQs(); 78 | } 79 | 80 | // schedule timed job 81 | void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) { 82 | osjob_t** pnext; 83 | hal_disableIRQs(); 84 | // remove if job was already queued 85 | unlinkjob(&OS.scheduledjobs, job); 86 | // fill-in job 87 | job->deadline = time; 88 | job->func = cb; 89 | job->next = NULL; 90 | // insert into schedule 91 | for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { 92 | if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) 93 | // enqueue before next element and stop 94 | job->next = *pnext; 95 | break; 96 | } 97 | } 98 | *pnext = job; 99 | hal_enableIRQs(); 100 | } 101 | 102 | // execute jobs from timer and from run queue 103 | void os_runloop () { 104 | while(1) { 105 | os_runloop_once(); 106 | } 107 | } 108 | 109 | void os_runloop_once() { 110 | osjob_t* j = NULL; 111 | hal_disableIRQs(); 112 | // check for runnable jobs 113 | if(OS.runnablejobs) { 114 | j = OS.runnablejobs; 115 | OS.runnablejobs = j->next; 116 | } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs 117 | j = OS.scheduledjobs; 118 | OS.scheduledjobs = j->next; 119 | } else { // nothing pending 120 | hal_sleep(); // wake by irq (timer already restarted) 121 | } 122 | hal_enableIRQs(); 123 | if(j) { // run job callback 124 | j->func(j); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/lmic/oslmic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | //! \file 29 | #ifndef _oslmic_h_ 30 | #define _oslmic_h_ 31 | 32 | // Dependencies required for the LoRa MAC in C to run. 33 | // These settings can be adapted to the underlying system. 34 | // You should not, however, change the lmic.[hc] 35 | 36 | #include "config.h" 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C"{ 41 | #endif 42 | 43 | //================================================================================ 44 | //================================================================================ 45 | // Target platform as C library 46 | typedef uint8_t bit_t; 47 | typedef uint8_t u1_t; 48 | typedef int8_t s1_t; 49 | typedef uint16_t u2_t; 50 | typedef int16_t s2_t; 51 | typedef uint32_t u4_t; 52 | typedef int32_t s4_t; 53 | typedef unsigned int uint; 54 | typedef const char* str_t; 55 | 56 | #include 57 | #include "hal.h" 58 | #define EV(a,b,c) /**/ 59 | #define DO_DEVDB(field1,field2) /**/ 60 | #if !defined(CFG_noassert) 61 | #define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__) 62 | #else 63 | #define ASSERT(cond) /**/ 64 | #endif 65 | 66 | #define os_clearMem(a,b) memset(a,0,b) 67 | #define os_copyMem(a,b,c) memcpy(a,b,c) 68 | 69 | typedef struct osjob_t osjob_t; 70 | typedef struct band_t band_t; 71 | typedef struct chnldef_t chnldef_t; 72 | typedef struct rxsched_t rxsched_t; 73 | typedef struct bcninfo_t bcninfo_t; 74 | typedef const u1_t* xref2cu1_t; 75 | typedef u1_t* xref2u1_t; 76 | #define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t 77 | #define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t 78 | #define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t 79 | #define TYPEDEF_xref2band_t typedef band_t* xref2band_t 80 | #define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t 81 | 82 | #define SIZEOFEXPR(x) sizeof(x) 83 | 84 | #define ON_LMIC_EVENT(ev) onEvent(ev) 85 | #define DECL_ON_LMIC_EVENT void onEvent(ev_t e) 86 | 87 | extern u4_t AESAUX[]; 88 | extern u4_t AESKEY[]; 89 | #define AESkey ((u1_t*)AESKEY) 90 | #define AESaux ((u1_t*)AESAUX) 91 | #define FUNC_ADDR(func) (&(func)) 92 | 93 | u1_t radio_rand1 (void); 94 | #define os_getRndU1() radio_rand1() 95 | 96 | #define DEFINE_LMIC struct lmic_t LMIC 97 | #define DECLARE_LMIC extern struct lmic_t LMIC 98 | 99 | void radio_init (void); 100 | void radio_irq_handler (u1_t dio); 101 | void os_init (void); 102 | void os_runloop (void); 103 | void os_runloop_once (void); 104 | 105 | //================================================================================ 106 | 107 | 108 | #ifndef RX_RAMPUP 109 | #define RX_RAMPUP (us2osticks(2000)) 110 | #endif 111 | #ifndef TX_RAMPUP 112 | #define TX_RAMPUP (us2osticks(2000)) 113 | #endif 114 | 115 | #ifndef OSTICKS_PER_SEC 116 | #define OSTICKS_PER_SEC 32768 117 | #elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 118 | #error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. 119 | #endif 120 | 121 | typedef s4_t ostime_t; 122 | 123 | #if !HAS_ostick_conv 124 | #define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000)) 125 | #define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000)) 126 | #define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC)) 127 | #define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC)) 128 | #define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC)) 129 | // Special versions 130 | #define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000)) 131 | #define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000)) 132 | #define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000)) 133 | #define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000)) 134 | #endif 135 | 136 | 137 | struct osjob_t; // fwd decl. 138 | typedef void (*osjobcb_t) (struct osjob_t*); 139 | struct osjob_t { 140 | struct osjob_t* next; 141 | ostime_t deadline; 142 | osjobcb_t func; 143 | }; 144 | TYPEDEF_xref2osjob_t; 145 | 146 | 147 | #ifndef HAS_os_calls 148 | 149 | #ifndef os_getDevKey 150 | void os_getDevKey (xref2u1_t buf); 151 | #endif 152 | #ifndef os_getArtEui 153 | void os_getArtEui (xref2u1_t buf); 154 | #endif 155 | #ifndef os_getDevEui 156 | void os_getDevEui (xref2u1_t buf); 157 | #endif 158 | #ifndef os_setCallback 159 | void os_setCallback (xref2osjob_t job, osjobcb_t cb); 160 | #endif 161 | #ifndef os_setTimedCallback 162 | void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb); 163 | #endif 164 | #ifndef os_clearCallback 165 | void os_clearCallback (xref2osjob_t job); 166 | #endif 167 | #ifndef os_getTime 168 | ostime_t os_getTime (void); 169 | #endif 170 | #ifndef os_getTimeSecs 171 | uint os_getTimeSecs (void); 172 | #endif 173 | #ifndef os_radio 174 | void os_radio (u1_t mode); 175 | #endif 176 | #ifndef os_getBattLevel 177 | u1_t os_getBattLevel (void); 178 | #endif 179 | 180 | #ifndef os_rlsbf4 181 | //! Read 32-bit quantity from given pointer in little endian byte order. 182 | u4_t os_rlsbf4 (xref2cu1_t buf); 183 | #endif 184 | #ifndef os_wlsbf4 185 | //! Write 32-bit quntity into buffer in little endian byte order. 186 | void os_wlsbf4 (xref2u1_t buf, u4_t value); 187 | #endif 188 | #ifndef os_rmsbf4 189 | //! Read 32-bit quantity from given pointer in big endian byte order. 190 | u4_t os_rmsbf4 (xref2cu1_t buf); 191 | #endif 192 | #ifndef os_wmsbf4 193 | //! Write 32-bit quntity into buffer in big endian byte order. 194 | void os_wmsbf4 (xref2u1_t buf, u4_t value); 195 | #endif 196 | #ifndef os_rlsbf2 197 | //! Read 16-bit quantity from given pointer in little endian byte order. 198 | u2_t os_rlsbf2 (xref2cu1_t buf); 199 | #endif 200 | #ifndef os_wlsbf2 201 | //! Write 16-bit quntity into buffer in little endian byte order. 202 | void os_wlsbf2 (xref2u1_t buf, u2_t value); 203 | #endif 204 | 205 | //! Get random number (default impl for u2_t). 206 | #ifndef os_getRndU2 207 | #define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1())) 208 | #endif 209 | #ifndef os_crc16 210 | u2_t os_crc16 (xref2u1_t d, uint len); 211 | #endif 212 | 213 | #endif // !HAS_os_calls 214 | 215 | // ====================================================================== 216 | // Table support 217 | // These macros for defining a table of constants and retrieving values 218 | // from it makes it easier for other platforms (like AVR) to optimize 219 | // table accesses. 220 | // Use CONST_TABLE() whenever declaring or defining a table, and 221 | // TABLE_GET_xx whenever accessing its values. The actual name of the 222 | // declared variable will be modified to prevent accidental direct 223 | // access. The accessor macros forward to an inline function to allow 224 | // proper type checking of the array element type. 225 | 226 | // Helper to add a prefix to the table name 227 | #define RESOLVE_TABLE(table) constant_table_ ## table 228 | 229 | // Accessors for table elements 230 | #define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) 231 | #define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) 232 | #define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) 233 | #define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index) 234 | #define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) 235 | #define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) 236 | #define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index) 237 | #define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2) 238 | 239 | #if defined(__AVR__) 240 | #include 241 | // Macro to define the getter functions. This loads data from 242 | // progmem using pgm_read_xx, or accesses memory directly when the 243 | // index is a constant so gcc can optimize it away; 244 | #define TABLE_GETTER(postfix, type, pgm_type) \ 245 | inline type table_get ## postfix(const type *table, size_t index) { \ 246 | if (__builtin_constant_p(table[index])) \ 247 | return table[index]; \ 248 | return pgm_read_ ## pgm_type(&table[index]); \ 249 | } 250 | 251 | TABLE_GETTER(_u1, u1_t, byte); 252 | TABLE_GETTER(_s1, s1_t, byte); 253 | TABLE_GETTER(_u2, u2_t, word); 254 | TABLE_GETTER(_s2, s2_t, word); 255 | TABLE_GETTER(_u4, u4_t, dword); 256 | TABLE_GETTER(_s4, s4_t, dword); 257 | 258 | // This assumes ostime_t is 4 bytes, so error out if it is not 259 | typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1]; 260 | TABLE_GETTER(_ostime, ostime_t, dword); 261 | 262 | // For AVR, store constants in PROGMEM, saving on RAM usage 263 | #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name) 264 | #else 265 | inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; } 266 | inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; } 267 | inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; } 268 | inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; } 269 | inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; } 270 | inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; } 271 | inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; } 272 | 273 | // Declare a table 274 | #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name) 275 | #endif 276 | 277 | // ====================================================================== 278 | // AES support 279 | // !!Keep in sync with lorabase.hpp!! 280 | 281 | #ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too 282 | #define AES_ENC 0x00 283 | #define AES_DEC 0x01 284 | #define AES_MIC 0x02 285 | #define AES_CTR 0x04 286 | #define AES_MICNOAUX 0x08 287 | #endif 288 | #ifndef AESkey // if AESkey is defined as macro all other values must be too 289 | extern xref2u1_t AESkey; 290 | extern xref2u1_t AESaux; 291 | #endif 292 | #ifndef os_aes 293 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len); 294 | #endif 295 | 296 | #ifdef __cplusplus 297 | } // extern "C" 298 | #endif 299 | 300 | #endif // _oslmic_h_ 301 | -------------------------------------------------------------------------------- /src/lmic/radio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 IBM Corporation. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "lmic.h" 29 | 30 | // ---------------------------------------- 31 | // Registers Mapping 32 | #define RegFifo 0x00 // common 33 | #define RegOpMode 0x01 // common 34 | #define FSKRegBitrateMsb 0x02 35 | #define FSKRegBitrateLsb 0x03 36 | #define FSKRegFdevMsb 0x04 37 | #define FSKRegFdevLsb 0x05 38 | #define RegFrfMsb 0x06 // common 39 | #define RegFrfMid 0x07 // common 40 | #define RegFrfLsb 0x08 // common 41 | #define RegPaConfig 0x09 // common 42 | #define RegPaRamp 0x0A // common 43 | #define RegOcp 0x0B // common 44 | #define RegLna 0x0C // common 45 | #define FSKRegRxConfig 0x0D 46 | #define LORARegFifoAddrPtr 0x0D 47 | #define FSKRegRssiConfig 0x0E 48 | #define LORARegFifoTxBaseAddr 0x0E 49 | #define FSKRegRssiCollision 0x0F 50 | #define LORARegFifoRxBaseAddr 0x0F 51 | #define FSKRegRssiThresh 0x10 52 | #define LORARegFifoRxCurrentAddr 0x10 53 | #define FSKRegRssiValue 0x11 54 | #define LORARegIrqFlagsMask 0x11 55 | #define FSKRegRxBw 0x12 56 | #define LORARegIrqFlags 0x12 57 | #define FSKRegAfcBw 0x13 58 | #define LORARegRxNbBytes 0x13 59 | #define FSKRegOokPeak 0x14 60 | #define LORARegRxHeaderCntValueMsb 0x14 61 | #define FSKRegOokFix 0x15 62 | #define LORARegRxHeaderCntValueLsb 0x15 63 | #define FSKRegOokAvg 0x16 64 | #define LORARegRxPacketCntValueMsb 0x16 65 | #define LORARegRxpacketCntValueLsb 0x17 66 | #define LORARegModemStat 0x18 67 | #define LORARegPktSnrValue 0x19 68 | #define FSKRegAfcFei 0x1A 69 | #define LORARegPktRssiValue 0x1A 70 | #define FSKRegAfcMsb 0x1B 71 | #define LORARegRssiValue 0x1B 72 | #define FSKRegAfcLsb 0x1C 73 | #define LORARegHopChannel 0x1C 74 | #define FSKRegFeiMsb 0x1D 75 | #define LORARegModemConfig1 0x1D 76 | #define FSKRegFeiLsb 0x1E 77 | #define LORARegModemConfig2 0x1E 78 | #define FSKRegPreambleDetect 0x1F 79 | #define LORARegSymbTimeoutLsb 0x1F 80 | #define FSKRegRxTimeout1 0x20 81 | #define LORARegPreambleMsb 0x20 82 | #define FSKRegRxTimeout2 0x21 83 | #define LORARegPreambleLsb 0x21 84 | #define FSKRegRxTimeout3 0x22 85 | #define LORARegPayloadLength 0x22 86 | #define FSKRegRxDelay 0x23 87 | #define LORARegPayloadMaxLength 0x23 88 | #define FSKRegOsc 0x24 89 | #define LORARegHopPeriod 0x24 90 | #define FSKRegPreambleMsb 0x25 91 | #define LORARegFifoRxByteAddr 0x25 92 | #define LORARegModemConfig3 0x26 93 | #define FSKRegPreambleLsb 0x26 94 | #define FSKRegSyncConfig 0x27 95 | #define LORARegFeiMsb 0x28 96 | #define FSKRegSyncValue1 0x28 97 | #define LORAFeiMib 0x29 98 | #define FSKRegSyncValue2 0x29 99 | #define LORARegFeiLsb 0x2A 100 | #define FSKRegSyncValue3 0x2A 101 | #define FSKRegSyncValue4 0x2B 102 | #define LORARegRssiWideband 0x2C 103 | #define FSKRegSyncValue5 0x2C 104 | #define FSKRegSyncValue6 0x2D 105 | #define FSKRegSyncValue7 0x2E 106 | #define FSKRegSyncValue8 0x2F 107 | #define FSKRegPacketConfig1 0x30 108 | #define FSKRegPacketConfig2 0x31 109 | #define LORARegDetectOptimize 0x31 110 | #define FSKRegPayloadLength 0x32 111 | #define FSKRegNodeAdrs 0x33 112 | #define LORARegInvertIQ 0x33 113 | #define FSKRegBroadcastAdrs 0x34 114 | #define FSKRegFifoThresh 0x35 115 | #define FSKRegSeqConfig1 0x36 116 | #define FSKRegSeqConfig2 0x37 117 | #define LORARegDetectionThreshold 0x37 118 | #define FSKRegTimerResol 0x38 119 | #define FSKRegTimer1Coef 0x39 120 | #define LORARegSyncWord 0x39 121 | #define FSKRegTimer2Coef 0x3A 122 | #define FSKRegImageCal 0x3B 123 | #define FSKRegTemp 0x3C 124 | #define FSKRegLowBat 0x3D 125 | #define FSKRegIrqFlags1 0x3E 126 | #define FSKRegIrqFlags2 0x3F 127 | #define RegDioMapping1 0x40 // common 128 | #define RegDioMapping2 0x41 // common 129 | #define RegVersion 0x42 // common 130 | // #define RegAgcRef 0x43 // common 131 | // #define RegAgcThresh1 0x44 // common 132 | // #define RegAgcThresh2 0x45 // common 133 | // #define RegAgcThresh3 0x46 // common 134 | // #define RegPllHop 0x4B // common 135 | // #define RegTcxo 0x58 // common 136 | #define RegPaDac 0x5A // common 137 | // #define RegPll 0x5C // common 138 | // #define RegPllLowPn 0x5E // common 139 | // #define RegFormerTemp 0x6C // common 140 | // #define RegBitRateFrac 0x70 // common 141 | 142 | // ---------------------------------------- 143 | // spread factors and mode for RegModemConfig2 144 | #define SX1272_MC2_FSK 0x00 145 | #define SX1272_MC2_SF7 0x70 146 | #define SX1272_MC2_SF8 0x80 147 | #define SX1272_MC2_SF9 0x90 148 | #define SX1272_MC2_SF10 0xA0 149 | #define SX1272_MC2_SF11 0xB0 150 | #define SX1272_MC2_SF12 0xC0 151 | // bandwidth for RegModemConfig1 152 | #define SX1272_MC1_BW_125 0x00 153 | #define SX1272_MC1_BW_250 0x40 154 | #define SX1272_MC1_BW_500 0x80 155 | // coding rate for RegModemConfig1 156 | #define SX1272_MC1_CR_4_5 0x08 157 | #define SX1272_MC1_CR_4_6 0x10 158 | #define SX1272_MC1_CR_4_7 0x18 159 | #define SX1272_MC1_CR_4_8 0x20 160 | #define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive 161 | #define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 162 | #define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 163 | // transmit power configuration for RegPaConfig 164 | #define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 165 | #define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 166 | 167 | 168 | // sx1276 RegModemConfig1 169 | #define SX1276_MC1_BW_125 0x70 170 | #define SX1276_MC1_BW_250 0x80 171 | #define SX1276_MC1_BW_500 0x90 172 | #define SX1276_MC1_CR_4_5 0x02 173 | #define SX1276_MC1_CR_4_6 0x04 174 | #define SX1276_MC1_CR_4_7 0x06 175 | #define SX1276_MC1_CR_4_8 0x08 176 | 177 | #define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 178 | 179 | // sx1276 RegModemConfig2 180 | #define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 181 | 182 | // sx1276 RegModemConfig3 183 | #define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 184 | #define SX1276_MC3_AGCAUTO 0x04 185 | 186 | // preamble for lora networks (nibbles swapped) 187 | #define LORA_MAC_PREAMBLE 0x34 188 | 189 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A 190 | #ifdef CFG_sx1276_radio 191 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 192 | #elif CFG_sx1272_radio 193 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 194 | #endif 195 | 196 | 197 | 198 | // ---------------------------------------- 199 | // Constants for radio registers 200 | #define OPMODE_LORA 0x80 201 | #define OPMODE_MASK 0x07 202 | #define OPMODE_SLEEP 0x00 203 | #define OPMODE_STANDBY 0x01 204 | #define OPMODE_FSTX 0x02 205 | #define OPMODE_TX 0x03 206 | #define OPMODE_FSRX 0x04 207 | #define OPMODE_RX 0x05 208 | #define OPMODE_RX_SINGLE 0x06 209 | #define OPMODE_CAD 0x07 210 | 211 | // ---------------------------------------- 212 | // Bits masking the corresponding IRQs from the radio 213 | #define IRQ_LORA_RXTOUT_MASK 0x80 214 | #define IRQ_LORA_RXDONE_MASK 0x40 215 | #define IRQ_LORA_CRCERR_MASK 0x20 216 | #define IRQ_LORA_HEADER_MASK 0x10 217 | #define IRQ_LORA_TXDONE_MASK 0x08 218 | #define IRQ_LORA_CDDONE_MASK 0x04 219 | #define IRQ_LORA_FHSSCH_MASK 0x02 220 | #define IRQ_LORA_CDDETD_MASK 0x01 221 | 222 | #define IRQ_FSK1_MODEREADY_MASK 0x80 223 | #define IRQ_FSK1_RXREADY_MASK 0x40 224 | #define IRQ_FSK1_TXREADY_MASK 0x20 225 | #define IRQ_FSK1_PLLLOCK_MASK 0x10 226 | #define IRQ_FSK1_RSSI_MASK 0x08 227 | #define IRQ_FSK1_TIMEOUT_MASK 0x04 228 | #define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 229 | #define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 230 | #define IRQ_FSK2_FIFOFULL_MASK 0x80 231 | #define IRQ_FSK2_FIFOEMPTY_MASK 0x40 232 | #define IRQ_FSK2_FIFOLEVEL_MASK 0x20 233 | #define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 234 | #define IRQ_FSK2_PACKETSENT_MASK 0x08 235 | #define IRQ_FSK2_PAYLOADREADY_MASK 0x04 236 | #define IRQ_FSK2_CRCOK_MASK 0x02 237 | #define IRQ_FSK2_LOWBAT_MASK 0x01 238 | 239 | // ---------------------------------------- 240 | // DIO function mappings D0D1D2D3 241 | #define MAP_DIO0_LORA_RXDONE 0x00 // 00------ 242 | #define MAP_DIO0_LORA_TXDONE 0x40 // 01------ 243 | #define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- 244 | #define MAP_DIO1_LORA_NOP 0x30 // --11---- 245 | #define MAP_DIO2_LORA_NOP 0xC0 // ----11-- 246 | 247 | #define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) 248 | #define MAP_DIO1_FSK_NOP 0x30 // --11---- 249 | #define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- 250 | #define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- 251 | 252 | 253 | // FSK IMAGECAL defines 254 | #define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F 255 | #define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 256 | #define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default 257 | 258 | #define RF_IMAGECAL_IMAGECAL_MASK 0xBF 259 | #define RF_IMAGECAL_IMAGECAL_START 0x40 260 | 261 | #define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 262 | #define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default 263 | 264 | 265 | // RADIO STATE 266 | // (initialized by radio_init(), used by radio_rand1()) 267 | static u1_t randbuf[16]; 268 | 269 | 270 | #ifdef CFG_sx1276_radio 271 | #define LNA_RX_GAIN (0x20|0x1) 272 | #elif CFG_sx1272_radio 273 | #define LNA_RX_GAIN (0x20|0x03) 274 | #else 275 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio 276 | #endif 277 | 278 | 279 | static void writeReg (u1_t addr, u1_t data ) { 280 | hal_pin_nss(0); 281 | hal_spi(addr | 0x80); 282 | hal_spi(data); 283 | hal_pin_nss(1); 284 | } 285 | 286 | static u1_t readReg (u1_t addr) { 287 | hal_pin_nss(0); 288 | hal_spi(addr & 0x7F); 289 | u1_t val = hal_spi(0x00); 290 | hal_pin_nss(1); 291 | return val; 292 | } 293 | 294 | static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { 295 | hal_pin_nss(0); 296 | hal_spi(addr | 0x80); 297 | for (u1_t i=0; i>16)); 416 | writeReg(RegFrfMid, (u1_t)(frf>> 8)); 417 | writeReg(RegFrfLsb, (u1_t)(frf>> 0)); 418 | } 419 | 420 | 421 | 422 | static void configPower () { 423 | #ifdef CFG_sx1276_radio 424 | // no boost used for now 425 | s1_t pw = (s1_t)LMIC.txpow; 426 | if(pw >= 17) { 427 | pw = 15; 428 | } else if(pw < 2) { 429 | pw = 2; 430 | } 431 | // check board type for BOOST pin 432 | writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf))); 433 | writeReg(RegPaDac, readReg(RegPaDac)|0x4); 434 | 435 | #elif CFG_sx1272_radio 436 | // set PA config (2-17 dBm using PA_BOOST) 437 | s1_t pw = (s1_t)LMIC.txpow; 438 | if(pw > 17) { 439 | pw = 17; 440 | } else if(pw < 2) { 441 | pw = 2; 442 | } 443 | writeReg(RegPaConfig, (u1_t)(0x80|(pw-2))); 444 | #else 445 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio 446 | #endif /* CFG_sx1272_radio */ 447 | } 448 | 449 | static void txfsk () { 450 | // select FSK modem (from sleep mode) 451 | writeReg(RegOpMode, 0x10); // FSK, BT=0.5 452 | ASSERT(readReg(RegOpMode) == 0x10); 453 | // enter standby mode (required for FIFO loading)) 454 | opmode(OPMODE_STANDBY); 455 | // set bitrate 456 | writeReg(FSKRegBitrateMsb, 0x02); // 50kbps 457 | writeReg(FSKRegBitrateLsb, 0x80); 458 | // set frequency deviation 459 | writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz 460 | writeReg(FSKRegFdevLsb, 0x99); 461 | // frame and packet handler settings 462 | writeReg(FSKRegPreambleMsb, 0x00); 463 | writeReg(FSKRegPreambleLsb, 0x05); 464 | writeReg(FSKRegSyncConfig, 0x12); 465 | writeReg(FSKRegPacketConfig1, 0xD0); 466 | writeReg(FSKRegPacketConfig2, 0x40); 467 | writeReg(FSKRegSyncValue1, 0xC1); 468 | writeReg(FSKRegSyncValue2, 0x94); 469 | writeReg(FSKRegSyncValue3, 0xC1); 470 | // configure frequency 471 | configChannel(); 472 | // configure output power 473 | configPower(); 474 | 475 | // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP 476 | writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP); 477 | 478 | // initialize the payload size and address pointers 479 | writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) 480 | 481 | // download length byte and buffer to the radio FIFO 482 | writeReg(RegFifo, LMIC.dataLen); 483 | writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); 484 | 485 | // enable antenna switch for TX 486 | hal_pin_rxtx(1); 487 | 488 | // now we actually start the transmission 489 | opmode(OPMODE_TX); 490 | } 491 | 492 | static void txlora () { 493 | // select LoRa modem (from sleep mode) 494 | //writeReg(RegOpMode, OPMODE_LORA); 495 | opmodeLora(); 496 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); 497 | 498 | // enter standby mode (required for FIFO loading)) 499 | opmode(OPMODE_STANDBY); 500 | // configure LoRa modem (cfg1, cfg2) 501 | configLoraModem(); 502 | // configure frequency 503 | configChannel(); 504 | // configure output power 505 | writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec 506 | configPower(); 507 | // set sync word 508 | writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); 509 | 510 | // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP 511 | writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); 512 | // clear all radio IRQ flags 513 | writeReg(LORARegIrqFlags, 0xFF); 514 | // mask all IRQs but TxDone 515 | writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); 516 | 517 | // initialize the payload size and address pointers 518 | writeReg(LORARegFifoTxBaseAddr, 0x00); 519 | writeReg(LORARegFifoAddrPtr, 0x00); 520 | writeReg(LORARegPayloadLength, LMIC.dataLen); 521 | 522 | // download buffer to the radio FIFO 523 | writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); 524 | 525 | // enable antenna switch for TX 526 | hal_pin_rxtx(1); 527 | 528 | // now we actually start the transmission 529 | opmode(OPMODE_TX); 530 | 531 | #if LMIC_DEBUG_LEVEL > 0 532 | u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 533 | u1_t bw = getBw(LMIC.rps); 534 | u1_t cr = getCr(LMIC.rps); 535 | LMIC_DEBUG_PRINTF("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n", 536 | os_getTime(), LMIC.freq, LMIC.dataLen, sf, 537 | bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), 538 | cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), 539 | getIh(LMIC.rps) 540 | ); 541 | #endif 542 | } 543 | 544 | // start transmitter (buf=LMIC.frame, len=LMIC.dataLen) 545 | static void starttx () { 546 | ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); 547 | if(getSf(LMIC.rps) == FSK) { // FSK modem 548 | txfsk(); 549 | } else { // LoRa modem 550 | txlora(); 551 | } 552 | // the radio will go back to STANDBY mode as soon as the TX is finished 553 | // the corresponding IRQ will inform us about completion. 554 | } 555 | 556 | enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; 557 | 558 | static CONST_TABLE(u1_t, rxlorairqmask)[] = { 559 | [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, 560 | [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, 561 | [RXMODE_RSSI] = 0x00, 562 | }; 563 | 564 | // start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) 565 | static void rxlora (u1_t rxmode) { 566 | // select LoRa modem (from sleep mode) 567 | opmodeLora(); 568 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); 569 | // enter standby mode (warm up)) 570 | opmode(OPMODE_STANDBY); 571 | // don't use MAC settings at startup 572 | if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan 573 | writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); 574 | writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); 575 | } else { // single or continuous rx mode 576 | // configure LoRa modem (cfg1, cfg2) 577 | configLoraModem(); 578 | // configure frequency 579 | configChannel(); 580 | } 581 | // set LNA gain 582 | writeReg(RegLna, LNA_RX_GAIN); 583 | // set max payload size 584 | writeReg(LORARegPayloadMaxLength, 64); 585 | #if !defined(DISABLE_INVERT_IQ_ON_RX) 586 | // use inverted I/Q signal (prevent mote-to-mote communication) 587 | 588 | // XXX: use flag to switch on/off inversion 589 | if (LMIC.noRXIQinversion) { 590 | writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6)); 591 | } else { 592 | writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); 593 | } 594 | #endif 595 | // set symbol timeout (for single rx) 596 | writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms); 597 | // set sync word 598 | writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); 599 | 600 | // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP 601 | writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); 602 | // clear all radio IRQ flags 603 | writeReg(LORARegIrqFlags, 0xFF); 604 | // enable required radio IRQs 605 | writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode)); 606 | 607 | // enable antenna switch for RX 608 | hal_pin_rxtx(0); 609 | 610 | // now instruct the radio to receive 611 | if (rxmode == RXMODE_SINGLE) { // single rx 612 | hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time 613 | opmode(OPMODE_RX_SINGLE); 614 | #if LMIC_DEBUG_LEVEL > 0 615 | ostime_t now = os_getTime(); 616 | LMIC_DEBUG_PRINTF("start single rx: now-rxtime: %lu\n", now - LMIC.rxtime); 617 | #endif 618 | } else { // continous rx (scan or rssi) 619 | opmode(OPMODE_RX); 620 | } 621 | 622 | #if LMIC_DEBUG_LEVEL > 0 623 | if (rxmode == RXMODE_RSSI) { 624 | LMIC_DEBUG_PRINTF("RXMODE_RSSI\n"); 625 | } else { 626 | u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7 627 | u1_t bw = getBw(LMIC.rps); 628 | u1_t cr = getCr(LMIC.rps); 629 | LMIC_DEBUG_PRINTF("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n", 630 | os_getTime(), 631 | rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"), 632 | LMIC.freq, sf, 633 | bw == BW125 ? 125 : (bw == BW250 ? 250 : 500), 634 | cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)), 635 | getIh(LMIC.rps) 636 | ); 637 | } 638 | #endif 639 | } 640 | 641 | static void rxfsk (u1_t rxmode) { 642 | // only single rx (no continuous scanning, no noise sampling) 643 | ASSERT( rxmode == RXMODE_SINGLE ); 644 | // select FSK modem (from sleep mode) 645 | //writeReg(RegOpMode, 0x00); // (not LoRa) 646 | opmodeFSK(); 647 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); 648 | // enter standby mode (warm up)) 649 | opmode(OPMODE_STANDBY); 650 | // configure frequency 651 | configChannel(); 652 | // set LNA gain 653 | //writeReg(RegLna, 0x20|0x03); // max gain, boost enable 654 | writeReg(RegLna, LNA_RX_GAIN); 655 | // configure receiver 656 | writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? 657 | // set receiver bandwidth 658 | writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb 659 | // set AFC bandwidth 660 | writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB 661 | // set preamble detection 662 | writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors 663 | // set sync config 664 | writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync 665 | // set packet config 666 | writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter 667 | writeReg(FSKRegPacketConfig2, 0x40); // packet mode 668 | // set sync value 669 | writeReg(FSKRegSyncValue1, 0xC1); 670 | writeReg(FSKRegSyncValue2, 0x94); 671 | writeReg(FSKRegSyncValue3, 0xC1); 672 | // set preamble timeout 673 | writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2); 674 | // set bitrate 675 | writeReg(FSKRegBitrateMsb, 0x02); // 50kbps 676 | writeReg(FSKRegBitrateLsb, 0x80); 677 | // set frequency deviation 678 | writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz 679 | writeReg(FSKRegFdevLsb, 0x99); 680 | 681 | // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut 682 | writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); 683 | 684 | // enable antenna switch for RX 685 | hal_pin_rxtx(0); 686 | 687 | // now instruct the radio to receive 688 | hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time 689 | opmode(OPMODE_RX); // no single rx mode available in FSK 690 | } 691 | 692 | static void startrx (u1_t rxmode) { 693 | ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); 694 | if(getSf(LMIC.rps) == FSK) { // FSK modem 695 | rxfsk(rxmode); 696 | } else { // LoRa modem 697 | rxlora(rxmode); 698 | } 699 | // the radio will go back to STANDBY mode as soon as the RX is finished 700 | // or timed out, and the corresponding IRQ will inform us about completion. 701 | } 702 | 703 | // get random seed from wideband noise rssi 704 | void radio_init () { 705 | hal_disableIRQs(); 706 | 707 | // manually reset radio 708 | #ifdef CFG_sx1276_radio 709 | hal_pin_rst(0); // drive RST pin low 710 | #else 711 | hal_pin_rst(1); // drive RST pin high 712 | #endif 713 | hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us 714 | hal_pin_rst(2); // configure RST pin floating! 715 | hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms 716 | 717 | opmode(OPMODE_SLEEP); 718 | 719 | // some sanity checks, e.g., read version number 720 | u1_t v = readReg(RegVersion); 721 | #ifdef CFG_sx1276_radio 722 | ASSERT(v == 0x12 ); 723 | #elif CFG_sx1272_radio 724 | ASSERT(v == 0x22); 725 | #else 726 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio 727 | #endif 728 | // seed 15-byte randomness via noise rssi 729 | rxlora(RXMODE_RSSI); 730 | while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx 731 | for(int i=1; i<16; i++) { 732 | for(int j=0; j<8; j++) { 733 | u1_t b; // wait for two non-identical subsequent least-significant bits 734 | while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); 735 | randbuf[i] = (randbuf[i] << 1) | b; 736 | } 737 | } 738 | randbuf[0] = 16; // set initial index 739 | 740 | #ifdef CFG_sx1276mb1_board 741 | // chain calibration 742 | writeReg(RegPaConfig, 0); 743 | 744 | // Launch Rx chain calibration for LF band 745 | writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); 746 | while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } 747 | 748 | // Sets a Frequency in HF band 749 | u4_t frf = 868000000; 750 | writeReg(RegFrfMsb, (u1_t)(frf>>16)); 751 | writeReg(RegFrfMid, (u1_t)(frf>> 8)); 752 | writeReg(RegFrfLsb, (u1_t)(frf>> 0)); 753 | 754 | // Launch Rx chain calibration for HF band 755 | writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); 756 | while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } 757 | #endif /* CFG_sx1276mb1_board */ 758 | 759 | opmode(OPMODE_SLEEP); 760 | 761 | hal_enableIRQs(); 762 | } 763 | 764 | // return next random byte derived from seed buffer 765 | // (buf[0] holds index of next byte to be returned) 766 | u1_t radio_rand1 () { 767 | u1_t i = randbuf[0]; 768 | ASSERT( i != 0 ); 769 | if( i==16 ) { 770 | os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key 771 | i = 0; 772 | } 773 | u1_t v = randbuf[i++]; 774 | randbuf[0] = i; 775 | return v; 776 | } 777 | 778 | u1_t radio_rssi () { 779 | hal_disableIRQs(); 780 | u1_t r = readReg(LORARegRssiValue); 781 | hal_enableIRQs(); 782 | return r; 783 | } 784 | 785 | static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = { 786 | [FSK] = us2osticks(0), // ( 0 ticks) 787 | [SF7] = us2osticks(0), // ( 0 ticks) 788 | [SF8] = us2osticks(1648), // ( 54 ticks) 789 | [SF9] = us2osticks(3265), // ( 107 ticks) 790 | [SF10] = us2osticks(7049), // ( 231 ticks) 791 | [SF11] = us2osticks(13641), // ( 447 ticks) 792 | [SF12] = us2osticks(31189), // (1022 ticks) 793 | }; 794 | 795 | // called by hal ext IRQ handler 796 | // (radio goes to stanby mode after tx/rx operations) 797 | void radio_irq_handler (u1_t dio) { 798 | #if CFG_TxContinuousMode 799 | // clear radio IRQ flags 800 | writeReg(LORARegIrqFlags, 0xFF); 801 | u1_t p = readReg(LORARegFifoAddrPtr); 802 | writeReg(LORARegFifoAddrPtr, 0x00); 803 | u1_t s = readReg(RegOpMode); 804 | u1_t c = readReg(LORARegModemConfig2); 805 | opmode(OPMODE_TX); 806 | return; 807 | #else /* ! CFG_TxContinuousMode */ 808 | ostime_t now = os_getTime(); 809 | #if LMIC_DEBUG_LEVEL > 0 810 | ostime_t const entry = now; 811 | #endif 812 | if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem 813 | u1_t flags = readReg(LORARegIrqFlags); 814 | if( flags & IRQ_LORA_TXDONE_MASK ) { 815 | // save exact tx time 816 | LMIC.txend = now - us2osticks(43); // TXDONE FIXUP 817 | } else if( flags & IRQ_LORA_RXDONE_MASK ) { 818 | // save exact rx time 819 | if(getBw(LMIC.rps) == BW125) { 820 | now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps)); 821 | } 822 | LMIC.rxtime = now; 823 | // read the PDU and inform the MAC that we received something 824 | LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ? 825 | readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); 826 | // set FIFO read address pointer 827 | writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); 828 | // now read the FIFO 829 | readBuf(RegFifo, LMIC.frame, LMIC.dataLen); 830 | // read rx quality parameters 831 | LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 832 | LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63) 833 | } else if( flags & IRQ_LORA_RXTOUT_MASK ) { 834 | // indicate timeout 835 | LMIC.dataLen = 0; 836 | #if LMIC_DEBUG_LEVEL > 0 837 | ostime_t now2 = os_getTime(); 838 | 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); 839 | #endif 840 | } 841 | // mask all radio IRQs 842 | writeReg(LORARegIrqFlagsMask, 0xFF); 843 | // clear radio IRQ flags 844 | writeReg(LORARegIrqFlags, 0xFF); 845 | } else { // FSK modem 846 | u1_t flags1 = readReg(FSKRegIrqFlags1); 847 | u1_t flags2 = readReg(FSKRegIrqFlags2); 848 | if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { 849 | // save exact tx time 850 | LMIC.txend = now; 851 | } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { 852 | // save exact rx time 853 | LMIC.rxtime = now; 854 | // read the PDU and inform the MAC that we received something 855 | LMIC.dataLen = readReg(FSKRegPayloadLength); 856 | // now read the FIFO 857 | readBuf(RegFifo, LMIC.frame, LMIC.dataLen); 858 | // read rx quality parameters 859 | LMIC.snr = 0; // determine snr 860 | LMIC.rssi = 0; // determine rssi 861 | } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { 862 | // indicate timeout 863 | LMIC.dataLen = 0; 864 | } else { 865 | ASSERT(0); 866 | } 867 | } 868 | // go from stanby to sleep 869 | opmode(OPMODE_SLEEP); 870 | // run os job (use preset func ptr) 871 | os_setCallback(&LMIC.osjob, LMIC.osjob.func); 872 | #endif /* ! CFG_TxContinuousMode */ 873 | } 874 | 875 | void os_radio (u1_t mode) { 876 | hal_disableIRQs(); 877 | switch (mode) { 878 | case RADIO_RST: 879 | // put radio to sleep 880 | opmode(OPMODE_SLEEP); 881 | break; 882 | 883 | case RADIO_TX: 884 | // transmit frame now 885 | starttx(); // buf=LMIC.frame, len=LMIC.dataLen 886 | break; 887 | 888 | case RADIO_RX: 889 | // receive frame now (exactly at rxtime) 890 | startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms 891 | break; 892 | 893 | case RADIO_RXON: 894 | // start scanning for beacon now 895 | startrx(RXMODE_SCAN); // buf=LMIC.frame 896 | break; 897 | } 898 | hal_enableIRQs(); 899 | } 900 | --------------------------------------------------------------------------------