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