diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml
new file mode 100644
index 0000000..83ce17a
--- /dev/null
+++ b/.github/workflows/compile-arduino-examples.yml
@@ -0,0 +1,74 @@
+name: Compile Arduino examples
+on: [push, pull_request]
+jobs:
+ build:
+ name: ${{ matrix.board }} / ${{ matrix.region }} / ${{ matrix.transceiver }}
+ runs-on: ubuntu-latest
+ env:
+ PLATFORM_DEFAULT_URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json,https://dl.espressif.com/dl/package_esp32_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json,https://lacunaspace.github.io/arduino-STM32L4-Lacuna/package_STM32L4_Lacuna_boards_index.json
+ strategy:
+ matrix:
+ board:
+ - arduino:avr:mega
+ - arduino:samd:arduino_zero_edbg
+ - esp8266:esp8266:generic
+ - esp32:esp32:ttgo-lora32-v1
+ - lacunaspace:stm32l4:Lacuna-LS200
+ # Just a random STM32 board
+ - STM32:stm32:Nucleo_64:pnum=NUCLEO_F401RE
+ region:
+ - CFG_eu868
+ - CFG_us915
+ transceiver:
+ - BRD_sx1272_radio
+ - BRD_sx1276_radio
+ - BRD_sx1261_radio
+ - BRD_sx1262_radio
+ include:
+ # Older compilers (used by STM32 and ESP) seem to get confused
+ # by the REGION initializers and produce (seemingly unfixable)
+ # missing-field-initializers warnings. So just ignore these
+ # for these boards.
+ - board: esp8266:esp8266:generic
+ extra_flags: -Wno-error=missing-field-initializers
+ # The STM32 cores have unused-parameter warnings in the core,
+ # so these (this also ignores the same warnings in the sketch
+ # and library, which is not ideal, but this seems to be the
+ # only way to fix this).
+ - board: lacunaspace:stm32l4:Lacuna-LS200
+ extra_flags: -Wno-error=unused-parameter -Wno-error=missing-field-initializers
+ - board: STM32:stm32:Nucleo_64:pnum=NUCLEO_F401RE
+ extra_flags: -Wno-error=unused-parameter -Wno-error=missing-field-initializers
+
+ # Run the entire matrix, even if one failed
+ fail-fast: false
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Generate Arduino library
+ # Generate twice, once to be moved into the Arduino directory and
+ # once to compile the examples from
+ run: |
+ ./target/arduino/export.sh BasicMacCustom
+ ./target/arduino/export.sh BasicMacArduino
+
+ - name: Compile all examples
+ uses: ArminJo/arduino-test-compile@v3
+ with:
+ arduino-board-fqbn: ${{ matrix.board }}
+ platform-default-url: ${{ env.PLATFORM_DEFAULT_URL }}
+ sketch-names-find-start: ./BasicMacArduino/examples/
+ # Normally, all .ino files are treated as separate sketches, but
+ # this is just a secondary .ino file that does not need to be
+ # separately compiled.
+ sketches-exclude: standard-pinmaps
+ build-properties: >
+ {"All": "
+ -DBASICMAC_DUMMY_PINMAP
+ -D${{matrix.region}}
+ -D${{matrix.transceiver}}
+ -Werror
+ ${{matrix.extra_flags}}
+ "}
diff --git a/.github/workflows/compile-projects.yml b/.github/workflows/compile-projects.yml
new file mode 100644
index 0000000..df4f0d7
--- /dev/null
+++ b/.github/workflows/compile-projects.yml
@@ -0,0 +1,42 @@
+name: Compile projects
+on: [push, pull_request]
+jobs:
+ build:
+ name: ${{matrix.target}}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ target:
+ # List taken from ex-join project, there might be more valid
+ # combinations.
+ - b_l072z_lrwan1
+ - nucleo_l073rz-sx1276mb1las
+ - nucleo_l073rz-sx1272mbed
+ - nucleo_l073rz-sx1276mb1mas
+ - nucleo_l053r8-sx1276mb1las
+ - nucleo_l053r8-sx1261mbed
+ - nucleo_l053r8-sx1262mbed
+
+ # Run the entire matrix, even if one failed
+ fail-fast: false
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: true
+
+ - name: Install compiler
+ run: sudo apt install gcc-arm-none-eabi
+
+ - name: Setup python
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.x' # Version range or exact version of a Python version to use, using SemVer's version range syntax
+
+ - name: Install python packages
+ run: python -m pip install lz4 pycryptodome click intelhex pyyaml
+
+ - name: Compile project
+ run: |
+ make -C projects/ex-join TARGET="${{matrix.target}}" || exit 1
diff --git a/.github/workflows/create-arduino-lib.yml b/.github/workflows/create-arduino-lib.yml
new file mode 100644
index 0000000..42f178b
--- /dev/null
+++ b/.github/workflows/create-arduino-lib.yml
@@ -0,0 +1,21 @@
+name: Create Arduino Library
+on: [push, pull_request]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Generate Arduino library
+ run: |
+ # Create two directory levels, so the inner one ends up in the
+ # generated zip
+ mkdir ArduinoLibrary
+ ./target/arduino/export.sh ArduinoLibrary/Basicmac
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: ArduinoLibrary
+ path: ArduinoLibrary
diff --git a/README.md b/README.md
index fa2a3bd..ad3cfd7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Basic MAC
+# BasicMAC
[Basic MAC](https://basicmac.io) is a portable implementation of the LoRa
Alliance®'s LoRaWAN® specification in the C programming language. It is a fork
@@ -13,3 +13,143 @@ Branch | Travis CI
#### Getting Started
View the [Getting Started Guide](https://basicmac.io/guide/gettingstarted.html).
+
+
+## This repository
+This repository contains an Arduino port of the BasicMAC library.
+However, the port is done without changing the structure of the
+repository itself, so development can continue on other targets at the
+same time.
+
+### Development status
+
+This master branch should be working, testing and contributions are
+welcome. There are still some rough edges, so this should not be
+considered a finished version. The final 2.2 release by Semtech was only
+recently integrated and has seen limited testing.
+
+Initially, development of this repository has been quite unstable, with
+lots of history editing and rebasing, but the intention is to keep the
+master branch stable now (i.e. no force pushes). Rebasing and history
+editing might still happen in other branches and is encourage in
+pullrequests to keep history clean.
+
+The API is still likely to change in a breaking way, especially wrt
+configuration.
+
+### Notable and breaking changes
+
+This section lists notable changes, in particular those that can break
+existing sketches. For full details, see the git changelog.
+
+ - 2020-07-14: On Arduino, the default config is changed to SX1262. If
+ your board uses SX1276 (the old default), you need to modify
+ `target-config.h` in the library.
+
+ - 2020-07-14: On SX126x, DIO3 is no longer configured to control a TCXO
+ by default. If your board needs this, it must be explicitly enabled.
+ On Arduino, set the `tcxo` field of the pin map to
+ `LMIC_CONTROLLED_BY_DIO3`. With Makefile-based stm32, define
+ `LMIC_DIO3_CONTROLS_TCXO` on the compiler commandline.
+
+ - 2020-07-14: On SX126x, DIO2 is no longer configured to control a TXRX
+ antenna switch by default. If your board needs this (most likely it
+ does), it must be explicitly enabled. On Arduino, set the `tx`
+ field of the pin map to `LMIC_CONTROLLED_BY_DIO2`. With
+ Makefile-based stm32, define `LMIC_DIO2_CONTROLS_TXRX` on the
+ compiler commandline.
+
+### Relation to other projects
+
+BasicMAC is a privately developed fork of LMIC, which was released
+publically by Semtech in 2019. In 2020, Semtech has announced it no
+longer intends to develop or support BasicMAC, so this repository is
+intended to become the primary repository for BasicMAC.
+
+This repository borrows heavily from the Arduino LMIC port that was
+first published at https://github.com/matthijskooijman/arduino-lmic/.
+There are some other LMIC versions (notably
+https://github.com/mcci-catena/arduino-lmic) for Arduino based off
+matthijskooijman's version and have seen more development, but no effort
+has been made to incorporate any changes from there.
+
+Ideally, all of these LMIC and BasicMAC-based projects would unify their
+efforts in a repository that is not Arduino-specific, but given that
+there have been signficant changes in both BasicMAC and the MCCI
+versions compared to the original IBM LMIC version, this might be a
+challenge (both technically, and project-wise).
+
+### Using this repository on Arduino
+
+This repository is not directly usable as an Arduino library, in order
+to keep the directory structure more generic. To use it in Arduino,
+there is a bash script that generates an Arduino library out of it. To do so,
+make a git checkout of this repository and from the root of it, run:
+
+ ./target/arduino/export.sh --link ~/Arduino/libraries/BasicMAC
+
+The `--link` parameter makes the script generate symlinks into the
+original repository, allowing you to make changes to the library there
+and have them reflected in the Arduino version directly. If you omit
+`--link`, copies will be made instead.
+
+The directory name passed is created and becomes the library directory,
+it should be in your Arduino sketchbook's `libraries` directly (the path
+shown should work with the default Arduino IDE configuration).
+
+This script was tested on Linux, should work on OSX as well, might work
+on Windows (if you have bash).
+
+### Using this repository without Arduino
+
+To build and upload without Arduino, a makefile-based build system is
+present in the `projects` subdirectory. It currently only supports
+STM32, on some specific boards.
+
+To use it, go into the example project directory (`projects/ex-join`)
+and run `make`.
+
+Some effort has been made to keep these builds working, but actual
+testing of the code in this repository has only been done under Arduino
+(Makefile-based builds are only automatically compile-tested), so the
+Makefile-based builds might very well be broken.
+
+### Hardware support
+
+This port is intended to work on any Arduino board, regardless of
+architecture. It was tested on AVR, SAMD and STM32 boards, and with
+SX1272, SX1276 and SX1262 radios.
+
+Unfortunately, BasicMAC is quite a bit bigger than LMIC, so it seems it
+does not fit in an atmega328p anymore. PROGMEM optimizations have not
+been applied yet, but those would only free up RAM, not flash (and now
+the ttn-otaa example compiles down to 35k of flash, with only 32k
+available).
+
+So far, only EU868 has been tested. Other regions are supported, but are
+not currently tested.
+
+### Converting from LMIC
+
+BasicMAC is based on LMIC and largely works in the same way. Sketches
+can likely be largely reused, though some small changes need to be made.
+See for example [this commit][migrate-examples] for the changes needed to the examples.
+
+Additionally the pinmap was significantly changed, so look at one of the
+current examples to see how that looks now.
+
+[migrate-examples]: https://github.com/LacunaSpace/basicmac/commit/1505722c912c8cb0cfff2e18b115f9f2c1a62d0f
+=======
+[Basic MAC](https://basicmac.io) is a portable implementation of the LoRa
+Alliance®'s LoRaWAN® specification in the C programming language. It is a fork
+of IBM's LMiC library, and supports multiple regions, which are selectable at
+compile and/or run time. It can handle Class A, Class B, and Class C devices.
+
+#### Status
+Branch | Travis CI
+-------|----------
+[`master`](https://github.com/mkuyper/basicmac/tree/master) | [](https://travis-ci.com/mkuyper/basicmac)
+
+#### Getting Started
+
+View the [Getting Started Guide](https://basicmac.io/guide/gettingstarted.html).
diff --git a/aes/aes-common.c b/aes/aes-common.c
new file mode 100644
index 0000000..2238fd6
--- /dev/null
+++ b/aes/aes-common.c
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Matthijs Kooijman
+ *
+ * LICENSE
+ *
+ * Permission is hereby granted, free of charge, to anyone
+ * obtaining a copy of this document and accompanying files,
+ * to do whatever they want with them without any restriction,
+ * including, but not limited to, copying, modification and
+ * redistribution.
+ *
+ * NO WARRANTY OF ANY KIND IS PROVIDED.
+ *******************************************************************************/
+
+/*
+ * The original LMIC AES implementation integrates raw AES encryption
+ * with CMAC and AES-CTR in a single piece of code. Most other AES
+ * implementations (only) offer raw single block AES encryption, so this
+ * file contains an implementation of CMAC and AES-CTR, and offers the
+ * same API through the os_aes() function as the original AES
+ * implementation. This file assumes that there is an encryption
+ * function available with this signature:
+ *
+ * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key);
+ *
+ * That takes a single 16-byte buffer and encrypts it wit the given
+ * 16-byte key.
+ */
+
+#include "../lmic/aes.h"
+
+#if !defined(USE_ORIGINAL_AES)
+
+// This should be defined elsewhere
+void lmic_aes_encrypt(u1_t *data, u1_t *key);
+
+// global area for passing parameters (aux, key) and for storing round keys
+u4_t AESAUX[16/sizeof(u4_t)];
+u4_t AESKEY[11*16/sizeof(u4_t)];
+
+// Shift the given buffer left one bit
+static void shift_left(u1_t *buf, u1_t len) {
+ while (len--) {
+ u1_t next = len ? buf[1] : 0;
+
+ u1_t val = (*buf << 1);
+ if (next & 0x80)
+ val |= 1;
+ *buf++ = val;
+ }
+}
+
+// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true,
+// AESAUX is prepended to the message. AESAUX is used as working memory
+// in any case. The CMAC result is returned in AESAUX as well.
+static void os_aes_cmac(u1_t *buf, u2_t len, u1_t prepend_aux) {
+ if (prepend_aux)
+ lmic_aes_encrypt(AESaux, AESkey);
+ else
+ memset (AESaux, 0, 16);
+
+ while (len > 0) {
+ u1_t need_padding = 0;
+ for (u1_t i = 0; i < 16; ++i, ++buf, --len) {
+ if (len == 0) {
+ // The message is padded with 0x80 and then zeroes.
+ // Since zeroes are no-op for xor, we can just skip them
+ // and leave AESAUX unchanged for them.
+ AESaux[i] ^= 0x80;
+ need_padding = 1;
+ break;
+ }
+ AESaux[i] ^= *buf;
+ }
+
+ if (len == 0) {
+ // Final block, xor with K1 or K2. K1 and K2 are calculated
+ // by encrypting the all-zeroes block and then applying some
+ // shifts and xor on that.
+ u1_t final_key[16];
+ memset(final_key, 0, sizeof(final_key));
+ lmic_aes_encrypt(final_key, AESkey);
+
+ // Calculate K1
+ u1_t msb = final_key[0] & 0x80;
+ shift_left(final_key, sizeof(final_key));
+ if (msb)
+ final_key[sizeof(final_key)-1] ^= 0x87;
+
+ // If the final block was not complete, calculate K2 from K1
+ if (need_padding) {
+ msb = final_key[0] & 0x80;
+ shift_left(final_key, sizeof(final_key));
+ if (msb)
+ final_key[sizeof(final_key)-1] ^= 0x87;
+ }
+
+ // Xor with K1 or K2
+ for (u1_t i = 0; i < sizeof(final_key); ++i)
+ AESaux[i] ^= final_key[i];
+ }
+
+ lmic_aes_encrypt(AESaux, AESkey);
+ }
+}
+
+// Run AES-CTR using the key in AESKEY and using AESAUX as the
+// counter block. The last byte of the counter block will be incremented
+// for every block. The given buffer will be encrypted in place.
+static void os_aes_ctr (u1_t *buf, u2_t len) {
+ u1_t ctr[16];
+ while (len) {
+ // Encrypt the counter block with the selected key
+ memcpy(ctr, AESaux, sizeof(ctr));
+ lmic_aes_encrypt(ctr, AESkey);
+
+ // Xor the payload with the resulting ciphertext
+ for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++)
+ *buf ^= ctr[i];
+
+ // Increment the block index byte
+ AESaux[15]++;
+ }
+}
+
+u4_t os_aes (u1_t mode, u1_t *buf, u2_t len) {
+ switch (mode & ~AES_MICNOAUX) {
+ case AES_MIC:
+ os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX));
+ return os_rmsbf4(AESaux);
+
+ case AES_ENC:
+ // TODO: Check / handle when len is not a multiple of 16
+ for (u1_t i = 0; i < len; i += 16)
+ lmic_aes_encrypt(buf+i, AESkey);
+ break;
+
+ case AES_CTR:
+ os_aes_ctr(buf, len);
+ break;
+ }
+ return 0;
+}
+
+#endif // !defined(USE_ORIGINAL_AES)
diff --git a/aes/aes-ideetron.c b/aes/aes-ideetron.c
new file mode 100644
index 0000000..1f51a60
--- /dev/null
+++ b/aes/aes-ideetron.c
@@ -0,0 +1,340 @@
+/******************************************************************************************
+#if defined(USE_IDEETRON_AES)
+* Copyright 2015, 2016 Ideetron B.V.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program. If not, see .
+******************************************************************************************/
+/******************************************************************************************
+*
+* File: AES-128_V10.cpp
+* Author: Gerben den Hartog
+* Compagny: Ideetron B.V.
+* Website: http://www.ideetron.nl/LoRa
+* E-mail: info@ideetron.nl
+******************************************************************************************/
+/****************************************************************************************
+*
+* Created on: 20-10-2015
+* Supported Hardware: ID150119-02 Nexus board with RFM95
+*
+* Firmware Version 1.0
+* First version
+****************************************************************************************/
+
+// This file was taken from
+// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for
+// use with LMIC. It was only cosmetically modified:
+// - AES_Encrypt was renamed to lmic_aes_encrypt.
+// - All other functions and variables were made static
+// - Tabs were converted to 2 spaces
+// - An #include and #if guard was added
+
+#include "../lmic/oslmic.h"
+
+#if defined(USE_IDEETRON_AES)
+
+/*
+********************************************************************************************
+* Global Variables
+********************************************************************************************
+*/
+
+static unsigned char State[4][4];
+
+static unsigned char S_Table[16][16] = {
+ {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
+ {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
+ {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
+ {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
+ {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
+ {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
+ {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
+ {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
+ {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
+ {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
+ {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
+ {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
+ {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
+ {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
+ {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
+ {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
+};
+
+void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key);
+static void AES_Add_Round_Key(unsigned char *Round_Key);
+static unsigned char AES_Sub_Byte(unsigned char Byte);
+static void AES_Shift_Rows();
+static void AES_Mix_Collums();
+static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
+
+/*
+*****************************************************************************************
+* Description : Function for encrypting data using AES-128
+*
+* Arguments : *Data Data to encrypt is a 16 byte long arry
+* *Key Key to encrypt data with is a 16 byte long arry
+*****************************************************************************************
+*/
+void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key)
+{
+ unsigned char i;
+ unsigned char Row,Collum;
+ unsigned char Round = 0x00;
+ unsigned char Round_Key[16];
+
+ //Copy input to State arry
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ State[Row][Collum] = Data[Row + (4*Collum)];
+ }
+ }
+
+ //Copy key to round key
+ for(i = 0; i < 16; i++)
+ {
+ Round_Key[i] = Key[i];
+ }
+
+ //Add round key
+ AES_Add_Round_Key(Round_Key);
+
+ //Preform 9 full rounds
+ for(Round = 1; Round < 10; Round++)
+ {
+ //Preform Byte substitution with S table
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
+ }
+ }
+
+ //Preform Row Shift
+ AES_Shift_Rows();
+
+ //Mix Collums
+ AES_Mix_Collums();
+
+ //Calculate new round key
+ AES_Calculate_Round_Key(Round,Round_Key);
+
+ //Add round key
+ AES_Add_Round_Key(Round_Key);
+ }
+
+ //Last round whitout mix collums
+ //Preform Byte substitution with S table
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
+ }
+ }
+
+ //Shift rows
+ AES_Shift_Rows();
+
+ //Calculate new round key
+ AES_Calculate_Round_Key(Round,Round_Key);
+
+ //Add round Key
+ AES_Add_Round_Key(Round_Key);
+
+ //Copy the State into the data array
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ Data[Row + (4*Collum)] = State[Row][Collum];
+ }
+ }
+
+}
+
+/*
+*****************************************************************************************
+* Description : Function that add's the round key for the current round
+*
+* Arguments : *Round_Key 16 byte long array holding the Round Key
+*****************************************************************************************
+*/
+static void AES_Add_Round_Key(unsigned char *Round_Key)
+{
+ unsigned char Row,Collum;
+
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)];
+ }
+ }
+}
+
+/*
+*****************************************************************************************
+* Description : Function that substitutes a byte with a byte from the S_Table
+*
+* Arguments : Byte The byte that will be substituted
+*
+* Return : The return is the found byte in the S_Table
+*****************************************************************************************
+*/
+static unsigned char AES_Sub_Byte(unsigned char Byte)
+{
+ unsigned char S_Row,S_Collum;
+ unsigned char S_Byte;
+
+ //Split byte up in Row and Collum
+ S_Row = ((Byte >> 4) & 0x0F);
+ S_Collum = (Byte & 0x0F);
+
+ //Find the correct byte in the S_Table
+ S_Byte = S_Table[S_Row][S_Collum];
+
+ return S_Byte;
+}
+
+/*
+*****************************************************************************************
+* Description : Function that preforms the shift row operation described in the AES standard
+*****************************************************************************************
+*/
+static void AES_Shift_Rows()
+{
+ unsigned char Buffer;
+
+ //Row 0 doesn't change
+
+ //Shift Row 1 one left
+ //Store firt byte in buffer
+ Buffer = State[1][0];
+ //Shift all bytes
+ State[1][0] = State[1][1];
+ State[1][1] = State[1][2];
+ State[1][2] = State[1][3];
+ State[1][3] = Buffer;
+
+ //Shift row 2 two left
+ Buffer = State[2][0];
+ State[2][0] = State[2][2];
+ State[2][2] = Buffer;
+ Buffer = State[2][1];
+ State[2][1] = State[2][3];
+ State[2][3] = Buffer;
+
+ //Shift row 3 three left
+ Buffer = State[3][3];
+ State[3][3] = State[3][2];
+ State[3][2] = State[3][1];
+ State[3][1] = State[3][0];
+ State[3][0] = Buffer;
+}
+
+/*
+*****************************************************************************************
+* Description : Function that preforms the Mix Collums operation described in the AES standard
+*****************************************************************************************
+*/
+static void AES_Mix_Collums()
+{
+ unsigned char Row,Collum;
+ unsigned char a[4], b[4];
+ for(Collum = 0; Collum < 4; Collum++)
+ {
+ for(Row = 0; Row < 4; Row++)
+ {
+ a[Row] = State[Row][Collum];
+ b[Row] = (State[Row][Collum] << 1);
+
+ if((State[Row][Collum] & 0x80) == 0x80)
+ {
+ b[Row] = b[Row] ^ 0x1B;
+ }
+ }
+ State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3];
+ State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3];
+ State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3];
+ State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3];
+ }
+}
+
+/*
+*****************************************************************************************
+* Description : Function that calculaties the round key for the current round
+*
+* Arguments : Round Number of current Round
+* *Round_Key 16 byte long array holding the Round Key
+*****************************************************************************************
+*/
+static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key)
+{
+ unsigned char i,j;
+ unsigned char b;
+ unsigned char Temp[4];
+ unsigned char Buffer;
+ unsigned char Rcon;
+
+ //Calculate first Temp
+ //Copy laste byte from previous key
+ for(i = 0; i < 4; i++)
+ {
+ Temp[i] = Round_Key[i+12];
+ }
+
+ //Rotate Temp
+ Buffer = Temp[0];
+ Temp[0] = Temp[1];
+ Temp[1] = Temp[2];
+ Temp[2] = Temp[3];
+ Temp[3] = Buffer;
+
+ //Substitute Temp
+ for(i = 0; i < 4; i++)
+ {
+ Temp[i] = AES_Sub_Byte(Temp[i]);
+ }
+
+ //Calculate Rcon
+ Rcon = 0x01;
+ while(Round != 1)
+ {
+ b = Rcon & 0x80;
+ Rcon = Rcon << 1;
+ if(b == 0x80)
+ {
+ Rcon = Rcon ^ 0x1b;
+ }
+ Round--;
+ }
+
+ //XOR Rcon
+ Temp[0] = Temp[0] ^ Rcon;
+
+ //Calculate new key
+ for(i = 0; i < 4; i++)
+ {
+ for(j = 0; j < 4; j++)
+ {
+ Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j];
+ Temp[j] = Round_Key[j + (4*i)];
+ }
+ }
+}
+
+#endif // defined(USE_IDEETRON_AES)
diff --git a/lmic/aes.c b/aes/aes-original.c
similarity index 97%
rename from lmic/aes.c
rename to aes/aes-original.c
index a461ac6..7d7e9a3 100644
--- a/lmic/aes.c
+++ b/aes/aes-original.c
@@ -4,7 +4,9 @@
// This file is subject to the terms and conditions defined in file 'LICENSE',
// which is part of this source code package.
-#include "aes.h"
+#if defined(USE_ORIGINAL_AES)
+
+#include "../lmic/aes.h"
// global area for passing parameters (aux, key) and for storing round keys
@@ -205,10 +207,10 @@ static const u4_t AES_E4[256] = {
r0 ^= AES_E1[ (i>>24)]
#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \
- a ^= (AES_S[ r0>>24 ]<<24); \
- a ^= (AES_S[u1(r1>>16)]<<16); \
- a ^= (AES_S[u1(r2>> 8)]<< 8); \
- a ^= AES_S[u1(r3) ]
+ a ^= ((u4_t)AES_S[ r0>>24 ]<<24); \
+ a ^= ((u4_t)AES_S[u1(r1>>16)]<<16); \
+ a ^= ((u4_t)AES_S[u1(r2>> 8)]<< 8); \
+ a ^= (u4_t)AES_S[u1(r3) ]
// generate 1+10 roundkeys for encryption with 128-bit key
// read 128-bit key from AESKEY in MSBF, generate roundkey words in place
@@ -224,10 +226,10 @@ static void aesroundkeys () {
for( ; i<44; i++ ) {
if( i%4==0 ) {
// b = SubWord(RotWord(b)) xor Rcon[i/4]
- b = (AES_S[u1(b >> 16)] << 24) ^
- (AES_S[u1(b >> 8)] << 16) ^
- (AES_S[u1(b) ] << 8) ^
- (AES_S[ b >> 24 ] ) ^
+ b = ((u4_t)AES_S[u1(b >> 16)] << 24) ^
+ ((u4_t)AES_S[u1(b >> 8)] << 16) ^
+ ((u4_t)AES_S[u1(b) ] << 8) ^
+ ((u4_t)AES_S[ b >> 24 ] ) ^
AES_RCON[(i-4)/4];
}
AESKEY[i] = b ^= AESKEY[i-4];
@@ -376,3 +378,5 @@ u4_t os_aes (u1_t mode, u1_t* buf, u2_t len) {
#pragma GCC diagnostic pop
#endif
+
+#endif // defined(USE_ORIGINAL_AES)
diff --git a/lmic/aes.h b/lmic/aes.h
index 131981c..9c19dd0 100644
--- a/lmic/aes.h
+++ b/lmic/aes.h
@@ -8,6 +8,10 @@
#include "oslmic.h"
+#ifdef __cplusplus
+extern "C"{
+#endif
+
// ======================================================================
// AES support
// !!Keep in sync with lorabase.hpp!!
@@ -28,4 +32,8 @@ extern u1_t* AESaux;
u4_t os_aes (u1_t mode, u1_t* buf, u2_t len);
#endif
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
#endif // _aes_h_
diff --git a/lmic/debug.c b/lmic/debug.c
index 4a7f161..dd432a7 100644
--- a/lmic/debug.c
+++ b/lmic/debug.c
@@ -4,12 +4,12 @@
// This file is subject to the terms and conditions defined in file 'LICENSE',
// which is part of this source code package.
+#include "lmic.h"
+
#ifdef CFG_DEBUG
#include
-#include "lmic.h"
-
void debug_led (int val) {
hal_debug_led(val);
}
@@ -18,26 +18,26 @@ void debug_str (const char* str) {
hal_debug_str(str);
}
-static int itoa (char* buf, unsigned int val, int base, int mindigits, int exp, int prec, char sign) {
+static int debug_itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec, char sign) {
char num[33], *p = num, *b = buf;
if (sign) {
- if ((int) val < 0) {
- val = -val;
- *b++ = '-';
- } else if (sign != '-') {
- *b++ = sign; // space or plus
- }
+ if ((s4_t) val < 0) {
+ val = -val;
+ *b++ = '-';
+ } else if (sign != '-') {
+ *b++ = sign; // space or plus
+ }
}
if (mindigits > 32) {
- mindigits = 32;
+ mindigits = 32;
}
do {
- int m = val % base;
+ int m = val % base;
*p++ = (m <= 9) ? m + '0' : m - 10 + 'A';
- if (p - num == exp) *p++ = '.';
+ if (p - num == exp) *p++ = '.';
} while ( (val /= base) || p - num < mindigits );
do {
- *b++ = *--p;
+ *b++ = *--p;
} while (p > num + exp - prec);
*b = 0;
return b - buf;
@@ -45,13 +45,13 @@ static int itoa (char* buf, unsigned int val, int base, int mindigits, int exp,
static int strpad (char *buf, int size, const char *str, int len, int width, int leftalign, char pad) {
if (len > width) {
- width = len;
+ width = len;
}
if (width > size) {
- width = size;
+ width = size;
}
for (int i = 0, npad = width - len; i < width; i++) {
- buf[i] = (leftalign) ? ((i < len) ? str[i] : pad) : ((i < npad) ? pad : str[i - npad]);
+ buf[i] = (leftalign) ? ((i < len) ? str[i] : pad) : ((i < npad) ? pad : str[i - npad]);
}
return width;
}
@@ -70,7 +70,6 @@ static const char* const evnames[] = {
[EV_LOST_TSYNC] = "LOST_TSYNC",
[EV_RESET] = "RESET",
[EV_RXCOMPLETE] = "RXCOMPLETE",
- [EV_ADR_BACKOFF] = "ADR_BACKOFF",
[EV_LINK_DEAD] = "LINK_DEAD",
[EV_LINK_ALIVE] = "LINK_ALIVE",
[EV_SCAN_FOUND] = "SCAN_FOUND",
@@ -83,154 +82,167 @@ static const char* const evnames[] = {
static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) {
char c, *dst = str, *end = str + size - 1;
- int width, left, base, zero, space, plus, prec, sign;
+ int width, left, base, zero, space, plus, prec, sign, longint;
while ( (c = *format++) && dst < end ) {
- if (c != '%') {
- *dst++ = c;
- } else {
- // flags
- width = prec = left = zero = sign = space = plus = 0;
- while ( (c = *format++) ) {
- if (c == '-') left = 1;
- else if (c == ' ') space = 1;
- else if (c == '+') plus = 1;
- else if (c == '0') zero = 1;
- else break;
- }
- // width
- if (c == '*') {
- width = va_arg(arg, int);
- c = *format++;
- } else {
- while (c >= '0' && c <= '9') {
- width = width * 10 + c - '0';
- c = *format++;
- }
- }
- // precision
- if (c == '.') {
- c = *format++;
- if (c == '*') {
- prec = va_arg(arg, int);
- c = *format++;
- } else {
- while (c >= '0' && c <= '9') {
- prec = prec * 10 + c - '0';
- c = *format++;
- }
- }
- }
- // conversion specifiers
- switch (c) {
- case 'c': // character
- c = va_arg(arg, int);
- // fallthrough
- case '%': // percent literal
- *dst++ = c;
- break;
- case 's': { // nul-terminated string
- char *s = va_arg(arg, char *);
- int l = strlen(s);
- if(prec && l > prec) {
- l = prec;
- }
- dst += strpad(dst, end - dst, s, l, width, left, ' ');
- break;
- }
- case 'd': // signed integer as decimal
- sign = (plus) ? '+' : (space) ? ' ' : '-';
- // fallthrough
- case 'u': // unsigned integer as decimal
- base = 10;
- goto numeric;
- case 'x':
- case 'X': // unsigned integer as hex
- base = 16;
- goto numeric;
- case 'b': // unsigned integer as binary
- base = 2;
- numeric: {
- char num[33], pad = ' ';
- if (zero && left == 0 && prec == 0) {
- prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign
- pad = '0';
- }
- int len = itoa(num, va_arg(arg, int), base, prec, 0, 0, sign);
- dst += strpad(dst, end - dst, num, len, width, left, pad);
- break;
- }
- case 'F': { // signed integer and exponent as fixed-point decimal
- char num[33], pad = (zero && left == 0) ? '0' : ' ';
- int val = va_arg(arg, int);
- int exp = va_arg(arg, int);
- int len = itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-');
- dst += strpad(dst, end - dst, num, len, width, left, pad);
- break;
- }
- case 'e': { // LMIC event name
- int ev = va_arg(arg, int);
- const char *evn = (ev < sizeof(evnames) / sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN";
- dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' ');
- break;
- }
- case 'E': { // EUI64, lsbf (xx-xx-xx-xx-xx-xx-xx-xx)
- char buf[23], *p = buf;
- unsigned char *eui = va_arg(arg, unsigned char *);
- for (int i = 7; i >= 0; i--) {
- p += itoa(p, eui[i], 16, 2, 0, 0, 0);
- if (i) *p++ = '-';
- }
- dst += strpad(dst, end - dst, buf, 23, width, left, ' ');
- break;
- }
- case 't': // ostime_t (hh:mm:ss.mmm)
- case 'T': { // osxtime_t (ddd.hh:mm:ss)
- char buf[12], *p = buf;
- uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC;
- int ms = t % 1000;
- t /= 1000;
- int sec = t % 60;
- t /= 60;
- int min = t % 60;
- t /= 60;
- int hr = t % 24;
- t /= 24;
- int day = t;
- if (c == 'T') {
- p += itoa(p, day, 10, 3, 0, 0, 0);
- *p++ = '.';
- }
- p += itoa(p, hr, 10, 2, 0, 0, 0);
- *p++ = ':';
- p += itoa(p, min, 10, 2, 0, 0, 0);
- *p++ = ':';
- p += itoa(p, sec, 10, 2, 0, 0, 0);
- if (c == 't') {
- *p++ = '.';
- p += itoa(p, ms, 10, 3, 0, 0, 0);
- }
- dst += strpad(dst, end - dst, buf, 12, width, left, ' ');
- break;
- }
- case 'h': { // buffer+length as hex dump (no field padding, but precision/maxsize truncation)
- unsigned char *buf = va_arg(arg, unsigned char *);
- int len = va_arg(arg, int);
- char *top = (prec == 0 || dst + prec > end) ? end : dst + prec;
- while (len--) {
- if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) {
- dst += itoa(dst, *buf++, 16, 2, 0, 0, 0);
- if(space && len && dst < top) *dst++ = ' ';
- } else {
- while (dst < top) *dst++ = '.';
- }
- }
- break;
- }
- default: // (also catch '\0')
- goto stop;
- }
- }
+ if (c != '%') {
+ *dst++ = c;
+ } else {
+ // flags
+ width = prec = left = zero = sign = space = plus = longint = 0;
+ while ( (c = *format++) ) {
+ if (c == '-') left = 1;
+ else if (c == ' ') space = 1;
+ else if (c == '+') plus = 1;
+ else if (c == '0') zero = 1;
+ else break;
+ }
+ // width
+ if (c == '*') {
+ width = va_arg(arg, int);
+ c = *format++;
+ } else {
+ while (c >= '0' && c <= '9') {
+ width = width * 10 + c - '0';
+ c = *format++;
+ }
+ }
+ // precision
+ if (c == '.') {
+ c = *format++;
+ if (c == '*') {
+ prec = va_arg(arg, int);
+ c = *format++;
+ } else {
+ while (c >= '0' && c <= '9') {
+ prec = prec * 10 + c - '0';
+ c = *format++;
+ }
+ }
+ }
+ // length
+ if(c == 'l') {
+ c = *format++;
+ longint = 1;
+ }
+ // conversion specifiers
+ switch (c) {
+ case 'c': // character
+ c = va_arg(arg, int);
+ // fallthrough
+ case '%': // percent literal
+ *dst++ = c;
+ break;
+ case 's': { // nul-terminated string
+ char *s = va_arg(arg, char *);
+ int l = strlen(s);
+ if(prec && l > prec) {
+ l = prec;
+ }
+ dst += strpad(dst, end - dst, s, l, width, left, ' ');
+ break;
+ }
+ case 'd': // signed integer as decimal
+ sign = (plus) ? '+' : (space) ? ' ' : '-';
+ // fallthrough
+ case 'u': // unsigned integer as decimal
+ base = 10;
+ goto numeric;
+ case 'x':
+ case 'X': // unsigned integer as hex
+ base = 16;
+ goto numeric;
+ case 'b': // unsigned integer as binary
+ base = 2;
+ numeric: {
+ char num[33], pad = ' ';
+ if (zero && left == 0 && prec == 0) {
+ prec = width - 1; // have debug_itoa() do the leading zero-padding for correct placement of sign
+ pad = '0';
+ }
+ u4_t val = longint ? va_arg(arg, long) : va_arg(arg, int);
+ int len = debug_itoa(num, val, base, prec, 0, 0, sign);
+ dst += strpad(dst, end - dst, num, len, width, left, pad);
+ break;
+ }
+ case 'F': { // signed integer and exponent as fixed-point decimal
+ char num[33], pad = (zero && left == 0) ? '0' : ' ';
+ u4_t val = va_arg(arg, u4_t);
+ int exp = va_arg(arg, int);
+ int len = debug_itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-');
+ dst += strpad(dst, end - dst, num, len, width, left, pad);
+ break;
+ }
+ case 'e': { // LMIC event name
+ unsigned ev = va_arg(arg, unsigned);
+ const char *evn = (ev < sizeof(evnames) / sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN";
+ dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' ');
+ break;
+ }
+ case 'E': { // EUI64, lsbf (xx-xx-xx-xx-xx-xx-xx-xx)
+ char buf[23], *p = buf;
+ unsigned char *eui = va_arg(arg, unsigned char *);
+ for (int i = 7; i >= 0; i--) {
+ p += debug_itoa(p, eui[i], 16, 2, 0, 0, 0);
+ if (i) *p++ = '-';
+ }
+ dst += strpad(dst, end - dst, buf, 23, width, left, ' ');
+ break;
+ }
+ case 't': // ostime_t (hh:mm:ss.mmm)
+ case 'T': { // osxtime_t (ddd.hh:mm:ss)
+ #if defined(CFG_DEBUG_RAW_TIMESTAMPS)
+ if (c == 't') {
+ longint = 1;
+ base = 10;
+ goto numeric;
+ }
+ #endif
+ char buf[12], *p = buf;
+ uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC;
+ int ms = t % 1000;
+ t /= 1000;
+ int sec = t % 60;
+ t /= 60;
+ int min = t % 60;
+ t /= 60;
+ int hr = t % 24;
+ t /= 24;
+ int day = t;
+ if (c == 'T') {
+ p += debug_itoa(p, day, 10, 3, 0, 0, 0);
+ *p++ = '.';
+ }
+ p += debug_itoa(p, hr, 10, 2, 0, 0, 0);
+ *p++ = ':';
+ p += debug_itoa(p, min, 10, 2, 0, 0, 0);
+ *p++ = ':';
+ p += debug_itoa(p, sec, 10, 2, 0, 0, 0);
+ if (c == 't') {
+ *p++ = '.';
+ p += debug_itoa(p, ms, 10, 3, 0, 0, 0);
+ }
+ dst += strpad(dst, end - dst, buf, 12, width, left, ' ');
+ break;
+ }
+ case 'h': { // buffer+length as hex dump (no field padding, but precision/maxsize truncation)
+ unsigned char *buf = va_arg(arg, unsigned char *);
+ int len = va_arg(arg, int);
+ char *top = (prec == 0 || dst + prec > end) ? end : dst + prec;
+ while (len--) {
+ if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) {
+ dst += debug_itoa(dst, *buf++, 16, 2, 0, 0, 0);
+ if(space && len && dst < top) *dst++ = ' ';
+ } else {
+ while (dst < top) *dst++ = '.';
+ }
+ }
+ break;
+ }
+ default: // (also catch '\0')
+ goto stop;
+ }
+ }
}
stop:
*dst++ = 0;
@@ -247,7 +259,7 @@ int debug_snprintf (char *str, int size, const char *format, ...) {
return length;
}
-void debug_printf (char const *format, ...) {
+void debug_printf_real (char const *format, ...) {
char buf[256];
va_list arg;
diff --git a/lmic/debug.h b/lmic/debug.h
index 23ced6c..205064e 100644
--- a/lmic/debug.h
+++ b/lmic/debug.h
@@ -9,18 +9,42 @@
#ifndef CFG_DEBUG
-#define debug_snprintf(s,n,f,...) do { } while (0)
-#define debug_printf(f,...) do { } while (0)
-#define debug_str(s) do { } while (0)
-#define debug_led(val) do { } while (0)
+#define debug_snprintf(s,n,f,...) do { } while (0)
+#define debug_printf(f,...) do { } while (0)
+#define debug_printf_continue(f,...) do { } while (0)
+#define debug_str(s) do { } while (0)
+#define debug_led(val) do { } while (0)
+#define debug_verbose_printf(f,...) do { } while (0)
+
+#ifdef CFG_DEBUG_VERBOSE
+#error CFG_DEBUG_VERBOSE requires CFG_DEBUG
+#endif
#else
+#include
+
+// When printing 16-bit values, the code uses %d and friends (which
+// accept an `int`-sized argument). This works, because these arguments
+// are integer-promoted automatically (provided int is at least 16-bits,
+// but the C language requires this
+// When printing 32-bit values, the code uses %ld (which accepts a
+// `long`-sized argument). This only works when `long` is actually
+// 32-bits. This is the case on at least ARM and AVR, but to be sure,
+// check at compiletime. Since there is no portable LONG_WIDTH, we use
+// LONG_MAX instead.
+#if LONG_MAX != ((1 << 31) - 1)
+#error "long is not exactly 32 bits, printing will fail"
+#endif
+
// write formatted string to buffer
int debug_snprintf (char *str, int size, const char *format, ...);
// write formatted string to USART
-void debug_printf (char const *format, ...);
+void debug_printf_real (char const *format, ...);
+#define debug_printf(format, ...) debug_printf_real("%10t: " format, os_getTime(), ## __VA_ARGS__)
+// To continue a line, omit the timestamp
+#define debug_printf_continue(format, ...) debug_printf_real(format, ## __VA_ARGS__)
// write nul-terminated string to USART
void debug_str (const char* str);
@@ -28,6 +52,14 @@ void debug_str (const char* str);
// set LED state
void debug_led (int val);
+#ifndef CFG_DEBUG_VERBOSE
+#define debug_verbose_printf(f,...) do { } while (0)
+#define debug_verbose_printf_continue(f,...) do { } while (0)
+#else
+#define debug_verbose_printf debug_printf
+#define debug_verbose_printf_continue debug_printf_continue
+#endif
+
#endif
#endif
diff --git a/lmic/hal.h b/lmic/hal.h
index 05737c2..acc9545 100644
--- a/lmic/hal.h
+++ b/lmic/hal.h
@@ -8,6 +8,10 @@
#ifndef _hal_hpp_
#define _hal_hpp_
+#ifdef __cplusplus
+extern "C"{
+#endif
+
#ifdef HAL_IMPL_INC
#include HAL_IMPL_INC
#endif
@@ -31,16 +35,19 @@ void hal_watchcount (int cnt);
#define HAL_ANTSW_TX2 3
void hal_ant_switch (u1_t val);
+#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
/*
* control radio TCXO power (0=off, 1=on)
* (return if TCXO is present and in use)
*/
bool hal_pin_tcxo (u1_t val);
+#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
/*
* control radio RST pin (0=low, 1=high, 2=floating)
+ * (return true if reset pin is connected)
*/
-void hal_pin_rst (u1_t val);
+bool hal_pin_rst (u1_t val);
/*
* wait until radio BUSY pin is low
@@ -56,6 +63,22 @@ void hal_pin_busy_wait (void);
#define HAL_IRQMASK_DIO3 (1<<3)
void hal_irqmask_set (int mask);
+#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+/*
+ * Returns true if DIO3 should control the TCXO.
+ * TODO: Reconsider this HAL function, maybe integrate with hal_pin_tcxo
+ * somehow?
+ */
+bool hal_dio3_controls_tcxo (void);
+
+/*
+ * Returns true if DIO2 should control the RXTX switch.
+ * TODO: Reconsider this HAL function, maybe integrate with
+ * hal_ant_switch somehow?
+ */
+bool hal_dio2_controls_rxtx (void);
+#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+
/*
* drive radio NSS pin (on=low, off=high).
*/
@@ -160,4 +183,8 @@ bool hal_set_update (void* ptr);
void hal_logEv (uint8_t evcat, uint8_t evid, uint32_t evparam);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
#endif // _hal_hpp_
diff --git a/lmic/lce.c b/lmic/lce.c
index 256f9b8..e7f89a3 100644
--- a/lmic/lce.c
+++ b/lmic/lce.c
@@ -21,17 +21,17 @@ bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce) {
#if defined(CFG_lorawan11)
u1_t optneg = jacc[OFF_JA_DLSET] & JA_DLS_OPTNEG;
if( optneg ) {
- os_moveMem(jacc+OFF_JA_JOINNONCE+2, jacc+OFF_JA_JOINNONCE, jacclen-OFF_JA_JOINNONCE);
- os_wlsbf2(jacc+OFF_JA_JOINNONCE, devnonce);
- jacclen += 2;
+ os_moveMem(jacc+OFF_JA_JOINNONCE+2, jacc+OFF_JA_JOINNONCE, jacclen-OFF_JA_JOINNONCE);
+ os_wlsbf2(jacc+OFF_JA_JOINNONCE, devnonce);
+ jacclen += 2;
}
#endif
os_getNwkKey(AESkey);
u4_t mic2 = os_aes(AES_MIC|AES_MICNOAUX, jacc, jacclen);
#if defined(CFG_lorawan11)
if( optneg ) { // Restore orig frame
- jacclen -= 2;
- os_moveMem(jacc+OFF_JA_JOINNONCE, jacc+OFF_JA_JOINNONCE+2, jacclen-OFF_JA_JOINNONCE);
+ jacclen -= 2;
+ os_moveMem(jacc+OFF_JA_JOINNONCE, jacc+OFF_JA_JOINNONCE+2, jacclen-OFF_JA_JOINNONCE);
os_wlsbf4(jacc+jacclen, mic1);
}
#endif
@@ -54,12 +54,12 @@ bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce) {
os_aes(AES_ENC, nwkskey, 16);
#if defined(CFG_lorawan11)
if( optneg ) {
- os_getNwkKey(AESkey);
- os_aes(AES_ENC, LMIC.lceCtx.nwkSKeyDn, 16);
- os_getAppKey(AESkey);
+ os_getNwkKey(AESkey);
+ os_aes(AES_ENC, LMIC.lceCtx.nwkSKeyDn, 16);
+ os_getAppKey(AESkey);
} else {
- os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16);
- os_getNwkKey(AESkey);
+ os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16);
+ os_getNwkKey(AESkey);
}
#else
os_getNwkKey(AESkey);
@@ -131,7 +131,7 @@ u4_t lce_micKey0 (u4_t devaddr, u4_t seqno, u1_t* pdu, int len) {
void lce_cipher (s1_t keyid, u4_t devaddr, u4_t seqno, int cat, u1_t* payload, int len) {
if(len <= 0 || (cat==LCE_SCC_UP && (LMIC.opmode & OP_NOCRYPT)) ) {
- return;
+ return;
}
const u1_t* key;
if( keyid == LCE_NWKSKEY ) {
diff --git a/lmic/lce.h b/lmic/lce.h
index fb91310..e863636 100644
--- a/lmic/lce.h
+++ b/lmic/lce.h
@@ -8,6 +8,10 @@
#include "oslmic.h"
+#ifdef __cplusplus
+extern "C"{
+#endif
+
// Some keyids:
#define LCE_APPSKEY (-2)
#define LCE_NWKSKEY (-1)
@@ -55,4 +59,8 @@ typedef struct lce_ctx {
} lce_ctx_t;
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
#endif // _lce_h_
diff --git a/lmic/lmic.c b/lmic/lmic.c
index 74517bd..6ebdef9 100644
--- a/lmic/lmic.c
+++ b/lmic/lmic.c
@@ -41,11 +41,9 @@
#endif
DEFINE_LMIC;
-DECL_ON_LMIC_EVENT;
// Fwd decls.
static void engineUpdate(void);
-static void startScan (void);
// ================================================================================
@@ -55,7 +53,7 @@ static void startScan (void);
#if !defined(os_rlsbf2)
u2_t os_rlsbf2 (const u1_t* buf) {
- return (u2_t)(buf[0] | (buf[1]<<8));
+ return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8));
}
#endif
@@ -67,14 +65,14 @@ u2_t os_rmsbf2 (const u1_t* buf) {
#if !defined(os_rlsbf4)
u4_t os_rlsbf4 (const u1_t* buf) {
- return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
+ return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
}
#endif
#if !defined(os_rmsbf4)
u4_t os_rmsbf4 (const u1_t* buf) {
- return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
+ return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
}
#endif
@@ -146,45 +144,49 @@ u2_t os_crc16 (u1_t* data, uint len) {
// ================================================================================
// BEG NEW REGION STUFF
+#define LORA_UP_RPS(sf,bw) (MAKE_LORA_RPS((sf),(bw),CR_4_5,0,0))
+#define LORA_DN_RPS(sf,bw) (MAKE_LORA_RPS((sf),(bw),CR_4_5,0,1))
+#define FSK_UP_RPS() (MAKE_FSK_RPS(0))
+
// data rate tables
#ifdef REG_DRTABLE_EU
static const u1_t DR2RPS_EU[16] = {
- LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125),
- LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW250), LWUPDR(FSK, BW125),
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125),
+ LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW250), FSK_UP_RPS(),
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
};
#endif
#ifdef REG_DRTABLE_125kHz
static const u1_t DR2RPS_125kHz[16] = {
- LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125),
- LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW250), ILLEGAL_RPS,
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125),
+ LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW250), ILLEGAL_RPS,
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
};
#endif
#ifdef REG_DRTABLE_US
static const u1_t DR2RPS_US[16] = {
- LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), LWUPDR(SF8, BW125), LWUPDR(SF7, BW125),
- LWUPDR(SF8, BW500), ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
- LWDNDR(SF12, BW500), LWDNDR(SF11, BW500), LWDNDR(SF10, BW500), LWDNDR(SF9, BW500),
- LWDNDR(SF8, BW500), LWDNDR(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125),
+ LORA_UP_RPS(SF8, BW500), ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_DN_RPS(SF12, BW500), LORA_DN_RPS(SF11, BW500), LORA_DN_RPS(SF10, BW500), LORA_DN_RPS(SF9, BW500),
+ LORA_DN_RPS(SF8, BW500), LORA_DN_RPS(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS,
};
#endif
#ifdef REG_DRTABLE_AU
static const u1_t DR2RPS_AU[16] = {
- LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125),
- LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW500), ILLEGAL_RPS,
- LWDNDR(SF12, BW500), LWDNDR(SF11, BW500), LWDNDR(SF10, BW500), LWDNDR(SF9, BW500),
- LWDNDR(SF8, BW500), LWDNDR(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125),
+ LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF8, BW500), ILLEGAL_RPS,
+ LORA_DN_RPS(SF12, BW500), LORA_DN_RPS(SF11, BW500), LORA_DN_RPS(SF10, BW500), LORA_DN_RPS(SF9, BW500),
+ LORA_DN_RPS(SF8, BW500), LORA_DN_RPS(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS,
};
#endif
#ifdef REG_DRTABLE_IN
static const u1_t DR2RPS_IN[16] = {
- LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125),
- LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), ILLEGAL_RPS, LWUPDR(FSK, BW125),
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
- ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125),
+ LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), ILLEGAL_RPS, FSK_UP_RPS(),
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
+ ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS,
};
#endif
#ifdef REG_DYN
@@ -204,7 +206,7 @@ static const rfuncs_t RFUNCS_FIX; // fwd decl
static const region_t REGIONS[REGIONS_COUNT] = {
#ifdef CFG_eu868
[REGION_EU868] = {
- .regcode = REGCODE_EU868,
+ .regcode = REGCODE_EU868,
.flags = 0,
.minFreq = 863000000,
.maxFreq = 870000000,
@@ -220,6 +222,7 @@ static const region_t REGIONS[REGIONS_COUNT] = {
{ 865000000, 868000000, CAP_CENTI, 16 }, // h1.4
{ 868000000, 868600000, CAP_CENTI, 16 }, // h1.5
{ 869700000, 870000000, CAP_CENTI, 16 }, // h1.9
+ { 862000000, 863000000, CAP_MILLI, 16 }, // h0, max 350kHz BW (not enforced)
{ 863000000, 865000000, CAP_MILLI, 16 }, // h1.3
{ 868700000, 869200000, CAP_MILLI, 16 }, // h1.6
},
@@ -236,14 +239,14 @@ static const region_t REGIONS[REGIONS_COUNT] = {
.rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4, 5,
ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff),
.dr2rps = DR2RPS_EU,
- .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0},
+ .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0},
.rfuncs = &RFUNCS_DYN,
},
#endif
#ifdef CFG_as923
[REGION_AS923] = {
- .regcode = REGCODE_AS923,
+ .regcode = REGCODE_AS923,
.flags = 0,
.minFreq = 920000000,
.maxFreq = 928000000,
@@ -264,13 +267,13 @@ static const region_t REGIONS[REGIONS_COUNT] = {
.maxEirp = 16,
.rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4, 5, -1, -2),
.dr2rps = DR2RPS_EU,
- .dr2maxAppPload = { 0, 0, 11, 53, 125, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0},
+ .dr2maxAppPload = { 0, 0, 11, 53, 125, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0},
.rfuncs = &RFUNCS_DYN,
},
#endif
#ifdef CFG_us915
[REGION_US915] = {
- .regcode = REGCODE_US915,
+ .regcode = REGCODE_US915,
.flags = REG_FIXED,
.minFreq = 902000000,
.maxFreq = 928000000,
@@ -292,13 +295,13 @@ static const region_t REGIONS[REGIONS_COUNT] = {
ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff,
ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff),
.dr2rps = DR2RPS_US,
- .dr2maxAppPload = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0},
+ .dr2maxAppPload = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0},
.rfuncs = &RFUNCS_FIX,
},
#endif
#ifdef CFG_au915
[REGION_AU915] = {
- .regcode = REGCODE_AU915,
+ .regcode = REGCODE_AU915,
.flags = REG_FIXED,
.minFreq = 915000000,
.maxFreq = 928000000,
@@ -320,13 +323,13 @@ static const region_t REGIONS[REGIONS_COUNT] = {
.rx1DrOff = RX1DR_OFFSETS(8, 0, 1, 2, 3, 4, 5,
ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff),
.dr2rps = DR2RPS_AU,
- .dr2maxAppPload = {0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0},
+ .dr2maxAppPload = {0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0},
.rfuncs = &RFUNCS_FIX,
},
#endif
#ifdef CFG_cn470
[REGION_CN470] = {
- .regcode = REGCODE_CN470,
+ .regcode = REGCODE_CN470,
.flags = REG_FIXED,
.minFreq = 470000000,
.maxFreq = 510000000,
@@ -347,7 +350,7 @@ static const region_t REGIONS[REGIONS_COUNT] = {
.rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4,
ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff),
.dr2rps = DR2RPS_125kHz,
- .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.rfuncs = &RFUNCS_FIX,
},
#endif
@@ -379,6 +382,8 @@ static const region_t REGIONS[REGIONS_COUNT] = {
#endif
};
+// Workaround for Lacuna LS200 core defining this on the gcc commandline
+#undef REGION
#define REGION (*LMIC.region)
#define isREGION(reg) (®ION == ®IONS[REGION_##reg])
@@ -413,7 +418,7 @@ static void adjAvail (avail_t* pavail, osxtime_t base) {
static void setAvail (avail_t* pavail, osxtime_t t) {
osxtime_t base = LMIC.baseAvail;
- int v;
+ u4_t v;
if( base > t ) {
t = base; // make sure t is not in the past
}
@@ -438,11 +443,9 @@ static void setAvail (avail_t* pavail, osxtime_t t) {
*pavail = v;
}
-static dr_t decDR (dr_t dr) { // move to slower DR
- return dr == 0 ? 0 : dr-1;
-}
-
static dr_t lowerDR (dr_t dr, u1_t n) {
+ if (dr == CUSTOM_DR)
+ return dr;
return dr <= n ? 0 : dr-n;
}
@@ -461,6 +464,8 @@ static inline bit_t validDR (dr_t dr) {
}
static rps_t updr2rps (dr_t dr) {
+ if (dr == CUSTOM_DR)
+ return LMIC.custom_rps;
return REGION.dr2rps[dr];
}
@@ -468,12 +473,14 @@ static rps_t dndr2rps (dr_t dr) {
return setNocrc(updr2rps(dr), 1);
}
+#if !defined(DISABLE_CLASSB)
static u1_t numBcnChannels() {
return REG_IS_FIX() ? 8 : 1;
}
+#endif // !defined(DISABLE_CLASSB)
static dr_t rps2dndr (rps_t rps) {
- for( dr_t dr = 15; dr >= 0; dr-- ) {
+ for( dr_t dr = 15; dr != (dr_t)-1; dr-- ) {
if( setNocrc(REGION.dr2rps[dr], 1) == rps ) {
return dr;
}
@@ -506,12 +513,12 @@ int getSensitivity (rps_t rps) {
}
ostime_t calcAirTime (rps_t rps, u1_t plen) {
- u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz
- u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12
- if( sf == FSK ) {
+ if( isFsk(rps) ) {
return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
* (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
}
+ u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz
+ u1_t sf = getSf(rps);
u1_t sfx = 4*(sf+(7-SF7));
u1_t q = sfx - 8*enDro(rps);
int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
@@ -543,6 +550,8 @@ ostime_t calcAirTime (rps_t rps, u1_t plen) {
return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
}
+extern inline rps_t makeLoraRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
+extern inline rps_t makeFskRps (int nocrc);
extern inline sf_t getSf (rps_t params);
extern inline rps_t setSf (rps_t params, sf_t sf);
extern inline bw_t getBw (rps_t params);
@@ -553,8 +562,8 @@ extern inline int getNocrc (rps_t params);
extern inline rps_t setNocrc (rps_t params, int nocrc);
extern inline int getIh (rps_t params);
extern inline rps_t setIh (rps_t params, int ih);
-extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
-extern inline int sameSfBw (rps_t r1, rps_t r2);
+extern inline sf_t isLora (rps_t params);
+extern inline sf_t isFsk (rps_t params);
extern inline int enDro (rps_t params);
@@ -585,14 +594,16 @@ static void decPollcnt (void) {
// 500kHz | 8 9 10 11 12 13
//
static ostime_t dr2hsym (dr_t dr, s1_t num) {
- rps_t rps = REGION.dr2rps[dr];
+ rps_t rps = updr2rps(dr);
u1_t sf = getSf(rps);
u1_t bw = getBw(rps);
- ASSERT(sf >= SF7 && sf <=SF12 && bw >= BW125 && bw <= BW500);
+ LMIC_STATIC_ASSERT(BW125 == 0, "BW125 assumed to be 0");
+ ASSERT(sf >= SF7 && sf <= SF12 && bw <= BW500);
s4_t us = num << (9 + sf - SF7 - bw); // 10 => 9 half symbol time
return us2osticks(us);
}
+#if !defined(DISABLE_CLASSB)
static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
ostime_t rxoff, err;
@@ -636,6 +647,7 @@ static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
LMIC.bcnRxsyms = wsyms;
LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks + cpre - whspan;
}
+#endif
static void iniRxdErr () {
// Avg(rxdErrs) == 0
@@ -655,6 +667,7 @@ static void addRxdErr (u1_t rxdelay) {
LMIC.rxdErrIdx = (LMIC.rxdErrIdx + 1) % RXDERR_NUM;
}
+#if defined(CFG_testpin) || defined(CFG_extapi)
static s4_t evalRxdErr (u4_t* span) {
s4_t min = 0x7FFFFFFF, min2=0x7FFFFFFF;
s4_t max = 0x80000000, max2=0x80000000;
@@ -672,6 +685,7 @@ static s4_t evalRxdErr (u4_t* span) {
*span = max2-min2;
return (sum - max - min + ((RXDERR_NUM-2)/2)) / (RXDERR_NUM-2);
}
+#endif
static void adjustByRxdErr (u1_t rxdelay, u1_t dr) {
#ifdef CFG_testpin
@@ -683,10 +697,13 @@ static void adjustByRxdErr (u1_t rxdelay, u1_t dr) {
span /= dr2hsym(dr,1); // additional half symbols
LMIC.rxsyms += (span + 1) >> 1;
LMIC.rxtime -= span*hsym;
+#else // CFG_testpin
+ (void)rxdelay; (void)dr; // unused
#endif // CFG_testpin
}
+#if !defined(DISABLE_CLASSB)
// Setup scheduled RX window (ping/multicast slot)
static void rxschedInit (rxsched_t* rxsched) {
// Relates to the standard in the following way:
@@ -725,6 +742,7 @@ static bit_t rxschedNext (rxsched_t* rxsched, ostime_t cando) {
rxsched->rxsyms = LMIC.rxsyms;
goto again;
}
+#endif
static ostime_t rndDelay (u1_t secSpan) {
@@ -748,11 +766,13 @@ static void txDelay (ostime_t reftime, u1_t secSpan) {
static void setDrJoin (u1_t reason, dr_t dr) {
+ (void)reason; // unused
LMIC.datarate = dr;
}
static void setDrTxpow (u1_t reason, dr_t dr, s1_t powadj) {
+ (void)reason; // unused
if( powadj != KEEP_TXPOWADJ )
LMIC.txPowAdj = powadj;
if( LMIC.datarate != dr ) {
@@ -762,6 +782,7 @@ static void setDrTxpow (u1_t reason, dr_t dr, s1_t powadj) {
}
+#if !defined(DISABLE_CLASSB)
void LMIC_stopPingable (void) {
LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI);
}
@@ -783,10 +804,11 @@ u1_t LMIC_setPingable (u1_t intvExp) {
LMIC_enableTracking(3);
return 2;
}
+#endif
static freq_t rdFreq (u1_t* p) {
- freq_t freq = ((p[2] << 16) | (p[1] << 8) | p[0]) * 100;
+ freq_t freq = (((freq_t)p[2] << 16) | ((freq_t)p[1] << 8) | (freq_t)p[0]) * 100;
if( freq != 0 && (freq < REGION.minFreq || freq > REGION.maxFreq) )
return -1;
return freq;
@@ -795,6 +817,8 @@ static freq_t rdFreq (u1_t* p) {
// Shard between REG_DYN/REG_FIX
static dr_t prepareDnDr (dr_t updr) {
+ ASSERT(updr != CUSTOM_DR);
+
s1_t dndr = (s1_t) updr + REGION.rx1DrOff[LMIC.dn1DrOffIdx];
dr_t dr8 = REGION.dr2rps[8];
s1_t mindr = (dr8 != ILLEGAL_RPS && getNocrc(dr8)) ? 8 : 0;
@@ -813,7 +837,6 @@ static dr_t prepareDnDr (dr_t updr) {
#ifdef REG_DYN
static void prepareDn_dyn () {
- LMIC.dndr = prepareDnDr(LMIC.dndr);
// Check reconfigured DN link freq
freq_t dnfreq = LMIC.dyn.chDnFreq[LMIC.txChnl];
if( dnfreq ) {
@@ -838,7 +861,7 @@ static drmap_t all125up () {
drmap_t map = 0;
for( u1_t dr=0; dr < 16; dr++ ) {
rps_t rps = REGION.dr2rps[dr];
- if( rps != ILLEGAL_RPS && getSf(rps) != FSK
+ if( rps != ILLEGAL_RPS && isLora(rps)
&& getBw(rps) == BW125 && !getNocrc(rps) ) // not DN only DR
map |= 1<= REGION.ccaThreshold) { // channel is not available
+ debug_verbose_printf("Channel %u not available due to CCA\r\n", chnl);
goto unavailable;
}
} else {
// channel decision is stable (i.e. won't change even if not used immediately)
LMIC.opmode &= ~OP_NEXTCHNL; // XXX - not sure if that's necessary, since we only consider channels that can be used NOW
}
+ debug_verbose_printf("Selected channel %u (%.2F)\r\n", chnl, LMIC.dyn.chUpFreq[chnl] & ~BAND_MASK, 6);
// good to go!
LMIC.txChnl = chnl;
return now;
@@ -1068,14 +1101,17 @@ static ostime_t nextTx_dyn (ostime_t now) {
txavail = os_getXTime() + ms2osticks(100);
}
// Earliest duty cycle expiry or earliest time a channel might be tested again
+ debug_verbose_printf("Channel(s) will become available at %t\r\n", (ostime_t)txavail);
return (ostime_t) txavail;
}
+#if !defined(DISABLE_CLASSB)
static void setBcnRxParams_dyn (void) {
LMIC.dataLen = 0;
LMIC.freq = LMIC.bcnFreq ? LMIC.bcnFreq : REGION.beaconFreq;
LMIC.rps = setIh(setNocrc(dndr2rps(REGION.beaconDr), 1), REGION.beaconLen);
}
+#endif
#endif // REG_DYN
@@ -1133,7 +1169,7 @@ static ostime_t nextJoinState (void) {
LMIC.txChnl = LMIC.fix.hoplist[LMIC.refChnl];
done:
LMIC.opmode &= ~OP_NEXTCHNL;
- delay = rndDelay(32);
+ delay = rndDelay(32);
#endif
} else {
#ifdef REG_DYN
@@ -1149,12 +1185,16 @@ static ostime_t nextJoinState (void) {
failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
}
else {
- setDrJoin(DRCHG_NOJACC, decDR(LMIC.datarate));
+ setDrJoin(DRCHG_NOJACC, lowerDR(LMIC.datarate, 1));
}
}
- delay = rndDelay(255 >> LMIC.datarate);
+ delay = rndDelay(255 >> LMIC.datarate);
#endif
}
+ if (failed)
+ debug_verbose_printf("Join failed\r\n");
+ else
+ debug_verbose_printf("Scheduling next join at %t\r\n", os_getTime() + delay);
// 1 - triggers EV_JOIN_FAILED event
return (delay & ~1) | failed;
@@ -1182,16 +1222,16 @@ u1_t prng_next (u1_t* prngbuf) {
// generate pseudo-random permutation from start..end-1
static void perm (unsigned char* p, int start, int end, u1_t* prng) {
for (int i = 0; i < (end - start); i++) {
- uint32_t j = (prng_next(prng) << 8 ) | prng_next(prng);
- j %= (i + 1); // has small bias
- p[i] = p[j];
- p[j] = i + start;
+ uint32_t j = (prng_next(prng) << 8 ) | prng_next(prng);
+ j %= (i + 1); // has small bias
+ p[i] = p[j];
+ p[j] = i + start;
}
}
// generate a pseudo-random hoplist
static void generateHopList (u1_t* hoplist, int nch) {
- int nb = nch >> 3; // number of 8-ch blocks
+ int nb = nch >> 3; // number of 8-ch blocks
u1_t prng[16]; // prng state
memcpy(prng, "\x10hoplist", 8);
@@ -1202,11 +1242,11 @@ static void generateHopList (u1_t* hoplist, int nch) {
perm(bp + 1, 1, nb, prng);
for (int b = 0; b < nb; b++) {
- unsigned char cp[8];
- perm(cp, 0, 8, prng); // channel permutation
- for (int c = 0; c < 8; c++) {
- hoplist[c*nb+b] = bp[b]*8+cp[c];
- }
+ unsigned char cp[8];
+ perm(cp, 0, 8, prng); // channel permutation
+ for (int c = 0; c < 8; c++) {
+ hoplist[c*nb+b] = bp[b]*8+cp[c];
+ }
}
}
@@ -1227,8 +1267,9 @@ static u1_t enableAllChannels_fix (void) {
}
}
if (nch & 0xf) {
- if (LMIC.fix.channelMap[nch >> 4] != (1 << (nch & 0xf)) - 1) {
- LMIC.fix.channelMap[nch >> 4] = (1 << (nch & 0xf)) - 1;
+ u1_t newval = (1 << (nch & 0xf)) - 1;
+ if (LMIC.fix.channelMap[nch >> 4] != newval) {
+ LMIC.fix.channelMap[nch >> 4] = newval;
rv = 1;
}
}
@@ -1265,7 +1306,6 @@ static void initDefaultChannels_fix (void) {
}
static void prepareDn_fix () {
- LMIC.dndr = prepareDnDr(LMIC.dndr);
LMIC.freq = REGION.baseFreqDn
+ (LMIC.txChnl % (REGION.numChDnBlocks*8))
* (REGION.baseFreqFix ? DNCHSPACING_500kHz : DNCHSPACING_125kHz);
@@ -1319,7 +1359,7 @@ static u1_t applyChannelMap_fix (u1_t chpage, u2_t chmap, u2_t* dest) {
return 0;
}
if ((nch & 15) && chpage == (REGION.numChBlocks >> 1)) { // partial map in last 16bit word
- chmap &= ~(~0 << (nch & 15));
+ chmap &= ~(0xffff << (nch & 15));
}
dest[chpage] = chmap;
}
@@ -1367,6 +1407,10 @@ static void updateTx_fix (ostime_t txbeg) { //XXX:BUG: this is US915/AU915 cent
if( LMIC.globalDutyRate != 0 ) {
LMIC.globalDutyAvail = txbeg + (airtime< maxDnLen(LMIC.rps) ||
(hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
(ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
// Basic sanity checks failed
norx:
+ debug_printf("Invalid downlink[window=%s]\r\n", window);
LMIC.dataLen = 0;
return 0;
}
@@ -1690,10 +1744,14 @@ static bit_t decodeFrame (void) {
debug_printf("ADR: p1=%02x,dr=%d,powadj=%d,chmap=%04x,chpage=%d,nbtrans=%d\r\n",
p1, dr, powadj, chmap, chpage, nbtrans);
#endif
- if( dr == 15 ) {
+ if( LMIC.datarate == CUSTOM_DR ) {
+ // Reject modifying a custom DR. The network probably
+ // does not really know how to handle this, but at least
+ // they know we did not change the DR.
+ ans &= ~MCMD_LADR_ANS_DRACK;
+ } else if( dr == 15 ) {
dr = LMIC.datarate; // Do not change DR
- }
- if( !validDR(dr) || ( REG_IS_FIX()
+ } else if( !validDR(dr) || ( REG_IS_FIX()
#ifdef REG_FIX
&& !checkChannel_fix(dmap, dr)
#endif
@@ -1740,7 +1798,7 @@ static bit_t decodeFrame (void) {
freq_t freq = rdFreq(&opts[oidx+2]);
oidx += 5;
u1_t ans = 0;
- if( validDR(dr) ) //XXX:BUG validDNDR
+ if( validDR(dr) && LMIC.dn2Dr != CUSTOM_DR) //XXX:BUG validDNDR
ans |= MCMD_DN2P_ANS_DRACK;
if( freq > 0 )
ans |= MCMD_DN2P_ANS_CHACK;
@@ -1834,6 +1892,7 @@ static bit_t decodeFrame (void) {
oidx += 2;
continue;
}
+#if !defined(DISABLE_CLASSB)
case MCMD_PITV_ANS: {
if( (LMIC.ping.intvExp & 0x80) ) {
LMIC.ping.intvExp &= 0x7F; // clear pending bit
@@ -1909,6 +1968,7 @@ static bit_t decodeFrame (void) {
oidx += 4;
continue;
}
+#endif
#if defined(CFG_lorawan11)
case MCMD_ADRP_REQ: {
LMIC_setLinkCheck(1 << (opts[oidx+1] >> 4), 1 << (opts[oidx+1] & 0xf));
@@ -1967,10 +2027,10 @@ static bit_t decodeFrame (void) {
LMIC.dataBeg = poff;
LMIC.dataLen = pend-poff;
}
+ debug_printf("Received downlink[window=%s,port=%d,ack=%d]\r\n", window, port, ackup);
return 1;
}
-
static bit_t decodeMultiCastFrame (void) {
u1_t* d = LMIC.frame;
u1_t hdr = d[0];
@@ -2065,7 +2125,7 @@ static void setupRx2 (void) {
static void schedRx2 (u1_t delay, osjobcb_t func) {
#if defined(CFG_eu868) && !defined(CFG_kr920)
- if( getSf(dndr2rps(LMIC.dn2Dr)) == FSK ) {
+ if( isFsk(dndr2rps(LMIC.dn2Dr)) ) {
LMIC.rxtime = LMIC.txend + delay*sec2osticks(1) - PRERX_FSK*us2osticksRound(160); // (8bit/50kbps=160us)
LMIC.rxsyms = RXLEN_FSK;
}
@@ -2091,14 +2151,16 @@ static void setupRx1 (osjobcb_t func) {
// Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
static void txDone (u1_t delay, osjobcb_t func) {
+#if !defined(DISABLE_CLASSB)
if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
LMIC.opmode |= OP_PINGINI;
}
+#endif
prepareDn();
LMIC.rps = dndr2rps(LMIC.dndr);
- if( getSf(LMIC.rps) == FSK ) {
+ if( isFsk(LMIC.rps) ) {
LMIC.rxtime = LMIC.txend + delay*sec2osticks(1) - PRERX_FSK*us2osticksRound(160); // (8bit/50kbps=160us)
LMIC.rxsyms = RXLEN_FSK;
} else {
@@ -2114,6 +2176,7 @@ static void txDone (u1_t delay, osjobcb_t func) {
static void onJoinFailed (osjob_t* osjob) {
+ (void)osjob; // unused
// Notify app - must call LMIC_reset() to stop joining
// otherwise join procedure continues.
reportEvent(EV_JOIN_FAILED);
@@ -2137,15 +2200,15 @@ static bit_t processJoinAccept (void) {
}
LMIC.opmode &= ~OP_TXRXPEND;
ostime_t delay = nextJoinState();
- // update txend
- LMIC.txend = os_getTime() + delay;
+ // update txend
+ LMIC.txend = os_getTime() + delay;
// Build next JOIN REQUEST with next engineUpdate call
// Optionally, report join failed.
// Both after a random/chosen amount of ticks.
os_setApproxTimedCallback(&LMIC.osjob, LMIC.txend,
- ((delay&1) != 0)
- ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
- : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
+ ((delay&1) != 0)
+ ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
+ : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
return 1;
}
u1_t hdr = LMIC.frame[0];
@@ -2194,6 +2257,7 @@ static bit_t processJoinAccept (void) {
s4_t freq = rdFreq(&LMIC.frame[dlen]);
if( freq > 0 ) {
setupChannel_dyn(chidx, freq, 0);
+ debug_printf("Setup channel[idx=%d,freq=%.1F]\r\n", chidx, freq, 6);
}
}
#endif // REG_DYN
@@ -2213,6 +2277,7 @@ static bit_t processJoinAccept (void) {
static void processRx2Jacc (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen == 0 )
LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
processJoinAccept();
@@ -2220,23 +2285,27 @@ static void processRx2Jacc (osjob_t* osjob) {
static void setupRx2Jacc (osjob_t* osjob) {
+ (void)osjob; // unused
LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
setupRx2();
}
static void processRx1Jacc (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen == 0 || !processJoinAccept() )
schedRx2(DELAY_JACC2, FUNC_ADDR(setupRx2Jacc));
}
static void setupRx1Jacc (osjob_t* osjob) {
+ (void)osjob; // unused
setupRx1(FUNC_ADDR(processRx1Jacc));
}
static void jreqDone (osjob_t* osjob) {
+ (void)osjob; // unused
txDone(DELAY_JACC1, FUNC_ADDR(setupRx1Jacc));
reportEvent(EV_TXDONE);
}
@@ -2247,10 +2316,12 @@ static void jreqDone (osjob_t* osjob) {
static bit_t processDnData(void);
static void processRx2DnDataDelay (osjob_t* osjob) {
+ (void)osjob; // unused
processDnData();
}
static void processRx2DnData (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen == 0 ) {
LMIC.txrxFlags = (LMIC.txrxFlags & TXRX_NOTX) | 0; // nothing in 1st/2nd DN slot
// Delay callback processing to avoid up TX while gateway is txing our missed frame!
@@ -2265,21 +2336,24 @@ static void processRx2DnData (osjob_t* osjob) {
static void setupRx2DnData (osjob_t* osjob) {
+ (void)osjob; // unused
LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
setupRx2();
}
static void processRx1DnData (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen == 0 || !processDnData() ) {
schedRx2(LMIC.dn1Dly+DELAY_EXTDNW2, FUNC_ADDR(setupRx2DnData));
}
}
static void processRx2ClassC (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen != 0 ) {
LMIC.txrxFlags = TXRX_DNW2;
- if ((LMIC.devaddr == os_rlsbf4(&LMIC.frame[OFF_DAT_ADDR]) && decodeFrame()) || decodeMultiCastFrame() ) {
+ if ((LMIC.devaddr == os_rlsbf4(&LMIC.frame[OFF_DAT_ADDR]) && decodeFrame()) || decodeMultiCastFrame() ) {
reportEvent(EV_RXCOMPLETE);
return;
}
@@ -2297,16 +2371,19 @@ static void setupRx2ClassC () {
}
static void processRx1ClassC (osjob_t* osjob) {
+ (void)osjob; // unused
if( !processDnData() ) {
setupRx2ClassC();
}
}
static void setupRx1DnData (osjob_t* osjob) {
+ (void)osjob; // unused
setupRx1(FUNC_ADDR(processRx1DnData));
}
static void setupRx1ClassC (osjob_t* osjob) {
+ (void)osjob; // unused
setupRx1(FUNC_ADDR(processRx1ClassC));
}
@@ -2321,7 +2398,7 @@ static void txError (void) {
static void updataDone (osjob_t* osjob) {
- reportEvent(EV_TXDONE);
+ (void)osjob; // unused
// check if rx window is to be scheduled
// (dataLen is reset by radio if tx didn't complete regularly and txend is unknown)
if( LMIC.pendTxNoRx || LMIC.dataLen == 0 ) {
@@ -2334,6 +2411,7 @@ static void updataDone (osjob_t* osjob) {
? FUNC_ADDR(setupRx1DnData)
: FUNC_ADDR(setupRx1ClassC));
}
+ reportEvent(EV_TXDONE);
}
// ========================================
@@ -2363,18 +2441,20 @@ static void buildDataFrame (void) {
os_copyMem(&LMIC.frame[end], LMIC.foptsUp, n);
end += n;
}
+#if !defined(DISABLE_CLASSB)
if( LMIC.ping.intvExp & 0x80 ) {
// Announce ping interval - LNS hasn't acked it yet
LMIC.frame[end] = MCMD_PITV_REQ;
LMIC.frame[end+1] = LMIC.ping.intvExp & 0x7;
end += 2;
}
+#endif
if( LMIC.dutyCapAns ) {
if( end <= OFF_DAT_OPTS + 15 - 1 ) {
LMIC.frame[end] = MCMD_DCAP_ANS;
- end += 1;
- }
- LMIC.dutyCapAns = 0;
+ end += 1;
+ }
+ LMIC.dutyCapAns = 0;
}
if( LMIC.dn2Ans ) {
// Note: this is cleared with reception of a frame in a class A RX1/RX2 window
@@ -2410,6 +2490,7 @@ static void buildDataFrame (void) {
LMIC.devsAns = 0;
end += 3;
}
+#if !defined(DISABLE_CLASSB)
if( LMIC.askForTime > 0 ) {
LMIC.frame[end] = MCMD_TIME_REQ;
end += 1;
@@ -2420,6 +2501,7 @@ static void buildDataFrame (void) {
LMIC.bcnfAns = 0;
end += 2;
}
+#endif
if( LMIC.gwmargin == 255 ) {
LMIC.frame[end] = MCMD_LCHK_REQ;
end += 1;
@@ -2428,12 +2510,15 @@ static void buildDataFrame (void) {
int foptslen = end - OFF_DAT_OPTS;
if( foptslen && txdata && LMIC.pendTxPort == 0 ) {
+ debug_printf("Payload and MAC commands on port 0, sending only MAC commands\n");
// app layer wants to transmit on port 0, but we have fopts
txdata = 0;
LMIC.txrxFlags |= TXRX_NOTX;
}
- if( foptslen > 15 ) {
+ int foptslen_max = 15;
+ if( foptslen > foptslen_max ) {
+ debug_printf("MAC commands large (%u > %u), sending only MAC commands\n", foptslen, foptslen_max);
// too big for FOpts, send as MAC frame with port=0 (cancels application payload)
memcpy(LMIC.pendTxData, LMIC.frame+OFF_DAT_OPTS, foptslen);
dlen = foptslen;
@@ -2447,7 +2532,9 @@ static void buildDataFrame (void) {
end = OFF_DAT_OPTS;
}
- int flen, flen_max = LMIC_maxAppPayload() + 13;
+ int flen, flen_max = MAX_LEN_FRAME;
+ if (LMIC.datarate != CUSTOM_DR)
+ flen_max = LMIC_maxAppPayload() + 13;
again:
flen = end + (txdata ? 5+dlen : 4);
if( flen > flen_max ) {
@@ -2455,11 +2542,13 @@ static void buildDataFrame (void) {
if( txdata ) {
if( LMIC.pendTxPort ) {
if( foptslen ) {
+ debug_printf("Frame too large (%u > %u), sending only MAC commands\n", flen, flen_max);
// cancel application payload
txdata = 0;
LMIC.txrxFlags |= TXRX_NOTX;
goto again;
} else {
+ debug_printf("Frame too large (%u > %u), not sending\n", flen, flen_max);
// cancel transmission completely
LMIC.dataLen = 0;
return;
@@ -2503,6 +2592,7 @@ static void buildDataFrame (void) {
}
+#if !defined(DISABLE_CLASSB)
// Callback from HAL during scan mode or when job timer expires.
static void onBcnScanRx (osjob_t* job) {
// stop radio and its job
@@ -2648,6 +2738,7 @@ int LMIC_track (ostime_t when) {
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, track_start);
return 1;
}
+#endif
// ================================================================================
@@ -2670,6 +2761,7 @@ static void buildJoinRequest (u1_t ftype) {
}
static void startJoining (osjob_t* osjob) {
+ (void)osjob; // unused
// reset and calibrate radio
LMIC.freq = (REGION.maxFreq - REGION.minFreq) / 2;
os_radio(RADIO_INIT);
@@ -2706,7 +2798,9 @@ bit_t LMIC_startJoining (void) {
//
// ================================================================================
+#if !defined(DISABLE_CLASSB)
static void processPingRx (osjob_t* osjob) {
+ (void)osjob; // unused
if( LMIC.dataLen != 0 ) {
LMIC.txrxFlags = TXRX_PING;
if( decodeFrame() ) {
@@ -2717,6 +2811,7 @@ static void processPingRx (osjob_t* osjob) {
// Pick next ping slot
engineUpdate();
}
+#endif
static bit_t processDnData (void) {
@@ -2761,8 +2856,8 @@ static bit_t processDnData (void) {
if (LMIC.txPowAdj) {
setDrTxpow(DRCHG_NOADRACK, LMIC.datarate, 0);
}
- if (decDR(LMIC.datarate) != LMIC.datarate) {
- setDrTxpow(DRCHG_NOADRACK, decDR(LMIC.datarate), KEEP_TXPOWADJ);
+ if (lowerDR(LMIC.datarate, 1) != LMIC.datarate) {
+ setDrTxpow(DRCHG_NOADRACK, lowerDR(LMIC.datarate, 1), KEEP_TXPOWADJ);
} else if (REG_IS_FIX()
#ifdef REG_FIX
&& enableAllChannels_fix()
@@ -2780,6 +2875,7 @@ static bit_t processDnData (void) {
}
reportEvent((LMIC.opmode & OP_LINKDEAD) ? EV_LINK_DEAD : EV_ADR_BACKOFF);
}
+#if !defined(DISABLE_CLASSB)
// If this falls to zero the NWK did not answer our MCMD_TIME_REQ commands - try full scan
if( LMIC.askForTime > 0 ) {
if( --LMIC.askForTime == 0 ) {
@@ -2790,6 +2886,7 @@ static bit_t processDnData (void) {
opmodePoll();
}
}
+#endif
txcontinue:
return 1;
}
@@ -2813,7 +2910,9 @@ static bit_t processDnData (void) {
}
+#if !defined(DISABLE_CLASSB)
static void processBeacon (osjob_t* osjob) {
+ (void)osjob; // unused
ostime_t lasttx = LMIC.bcninfo.txtime; // save previous - decodeBeacon overwrites
u1_t flags = LMIC.bcninfo.flags;
ev_t ev;
@@ -2866,21 +2965,25 @@ static void processBeacon (osjob_t* osjob) {
reportEvent(ev);
}
-
static void startRxBcn (osjob_t* osjob) {
+ (void)osjob; // unused
LMIC.osjob.func = FUNC_ADDR(processBeacon);
os_radio(RADIO_RX);
}
static void startRxPing (osjob_t* osjob) {
+ (void)osjob; // unused
LMIC.osjob.func = FUNC_ADDR(processPingRx);
os_radio(RADIO_RX);
}
+#endif
+
// Decide what to do next for the MAC layer of a device
static void engineUpdate (void) {
+ debug_printf("engineUpdate[opmode=0x%x]\r\n", LMIC.opmode);
// Check for ongoing state: scan or TX/RX transaction
if( (LMIC.opmode & (OP_NOENGINE|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) {
return;
@@ -2894,9 +2997,11 @@ static void engineUpdate (void) {
#endif // CFG_autojoin
ostime_t now = os_getTime();
- ostime_t rxtime = 0;
ostime_t txbeg = 0;
+#if !defined(DISABLE_CLASSB)
+ ostime_t rxtime = 0;
+
if( (LMIC.opmode & OP_SCAN) != 0 ) {
// Looking for a beacon - LMIC.bcninfo.txtime is timeout for scan
// Cancel onging TX/RX transaction
@@ -2923,6 +3028,7 @@ static void engineUpdate (void) {
ASSERT( (ostime_t)(rxtime-now) >= 0 );
#endif
}
+#endif
if( LMIC.pollcnt )
opmodePoll();
@@ -2931,27 +3037,41 @@ static void engineUpdate (void) {
// Need to TX some data...
// Assuming txChnl points to channel which first becomes available again.
bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
+ if (jacc)
+ debug_verbose_printf("Uplink join pending\r\n", os_getTime());
+ else
+ debug_verbose_printf("Uplink data pending\r\n", os_getTime());
// Find next suitable channel and return availability time
if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
- txbeg = LMIC.txend = nextTx(now);
+ txbeg = LMIC.txend = nextTx(now+TX_RAMPUP);
+ debug_verbose_printf("Airtime available at %t (channel duty limit)\r\n", txbeg);
} else {
txbeg = LMIC.txend;
+ debug_verbose_printf("Airtime available at %t (previously determined)\r\n", txbeg);
}
// Delayed TX or waiting for duty cycle?
- if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
+ if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) {
txbeg = LMIC.globalDutyAvail;
+ debug_verbose_printf("Airtime available at %t (global duty limit)\r\n", txbeg);
+ }
+#if !defined(DISABLE_CLASSB)
// If we're tracking a beacon...
// then make sure TX-RX transaction is complete before beacon
if( (LMIC.opmode & OP_TRACK) != 0 &&
txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
+
+ debug_verbose_printf("Awaiting beacon before uplink\r\n");
+
// Not enough time to complete TX-RX before beacon - postpone after beacon.
// In order to avoid clustering of postponed TX right after beacon randomize start!
txDelay(rxtime + BCN_RESERVE_osticks, 16);
txbeg = 0;
goto checkrx;
}
+#endif
// Earliest possible time vs overhead to setup radio
if( txbeg - (now + TX_RAMPUP) <= 0 ) {
+ debug_verbose_printf("Ready for uplink\r\n");
// We could send right now!
txbeg = now + TX_RAMPUP;
dr_t txdr = LMIC.datarate;
@@ -2972,11 +3092,13 @@ static void engineUpdate (void) {
// Imminent roll over - proactively reset MAC
// Device has to react! NWK will not roll over and just stop sending.
// Thus, we have N frames to detect a possible lock up.
+ debug_printf("Down FCNT about to rollover (0x%lx), resetting session\n", LMIC.seqnoDn);
reset:
os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
return;
}
if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
+ debug_printf("Up FCNT about to rollover (0x%lx), resetting session\n", LMIC.seqnoUp);
// Roll over of up seq counter
// Do not run RESET event callback from here!
// App code might do some stuff after send unaware of RESET.
@@ -2985,19 +3107,27 @@ static void engineUpdate (void) {
LMIC.txrxFlags = 0;
buildDataFrame();
if( LMIC.dataLen == 0 ) {
+ debug_printf("Zero data length, not sending\n");
txError();
return;
}
LMIC.osjob.func = FUNC_ADDR(updataDone);
}
- LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
- LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
+ LMIC.rps = updr2rps(txdr);
+ // For CUSTOM_DR, assume that rps already has the right CR
+ // and that dndr is also preset to the right DR.
+ if (txdr != CUSTOM_DR) {
+ LMIC.rps = setCr(LMIC.rps, LMIC.errcr);
+ // Calculate dndr to use based on txdr (can be != LMIC.datarate for joins)
+ LMIC.dndr = prepareDnDr(txdr);
+ }
LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
updateTx(txbeg);
reportEvent(EV_TXSTART);
os_radio(RADIO_TX);
return;
}
+ debug_verbose_printf("Uplink delayed until %t\r\n", txbeg);
// Cannot yet TX
if( (LMIC.opmode & OP_TRACK) == 0 )
goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
@@ -3014,6 +3144,7 @@ static void engineUpdate (void) {
}
}
+#if !defined(DISABLE_CLASSB)
// Are we pingable?
checkrx:
if( (LMIC.opmode & OP_PINGINI) != 0 ) {
@@ -3046,6 +3177,7 @@ static void engineUpdate (void) {
}
os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
return;
+#endif
txdelay:
if( (LMIC.clmode & CLASS_C) ) {
@@ -3062,8 +3194,26 @@ void LMIC_setAdrMode (bit_t enabled) {
// Should we have/need an ext. API like this?
void LMIC_setDrTxpow (dr_t dr, s1_t txpowadj) {
+ ASSERT(dr != CUSTOM_DR); // Use setCustomDr() for that
setDrTxpow(DRCHG_SET, dr, txpowadj);
syncDatarate();
+ // Make sure dndr is not CUSTOM_DR anymore (actual value does not
+ // matter, it will be set later *if* not CUSTOM_DR).
+ LMIC.dndr = dr;
+}
+
+dr_t LMIC_setCustomDr (rps_t custom_rps, dr_t dndr) {
+ dr_t old_dr = LMIC.datarate;
+ setDrTxpow(DRCHG_SET, CUSTOM_DR, KEEP_TXPOWADJ);
+ LMIC.dndr = dndr;
+ LMIC.custom_rps = custom_rps;
+ return old_dr;
+}
+
+void LMIC_selectChannel(u1_t channel) {
+ LMIC.opmode &= ~OP_NEXTCHNL;
+ LMIC.txChnl = channel;
+ LMIC.txend = os_getTime();
}
@@ -3079,9 +3229,9 @@ int LMIC_regionIdx (u1_t regionCode) {
return 0;
}
for( int idx = 0; idx < REGIONS_COUNT; idx++ ) {
- if (REGIONS[idx].regcode == regionCode) {
- return idx;
- }
+ if (REGIONS[idx].regcode == regionCode) {
+ return idx;
+ }
}
return -1;
}
@@ -3113,9 +3263,11 @@ void LMIC_reset_ex (u1_t regionCode) {
LMIC.dn1Dly = 1;
LMIC.dn2Dr = REGION.rx2Dr; // we need this for 2nd DN window of join accept
LMIC.dn2Freq = REGION.rx2Freq; // ditto
+#if !defined(DISABLE_CLASSB)
LMIC.ping.freq = REGION.pingFreq; // defaults for ping
LMIC.ping.dr = REGION.pingDr; // ditto
LMIC.ping.intvExp = 8; // no ping interval ever sent up
+#endif
LMIC.refChnl = REG_IS_FIX() ? 0 : os_getRndU1(); // fix uses randomized hoplist starting with block0
LMIC.adrAckLimit = ADR_ACK_LIMIT;
LMIC.adrAckDelay = ADR_ACK_DELAY;
@@ -3188,8 +3340,10 @@ void LMIC_setClassC (u1_t enabled) {
return; // already in that mode
if( (LMIC.clmode & PEND_CLASS_C) )
return; // change is already pending
+#if !defined(DISABLE_CLASSB)
if( enabled )
LMIC_stopPingable(); // stop class B
+#endif
#if defined(CFG_lorawan11)
if( enabled != UNILATERAL_CLASS_C ) {
LMIC.clmode |= PEND_CLASS_C;
@@ -3249,19 +3403,19 @@ int LMIC_setMultiCastSession (devaddr_t grpaddr, const u1_t* nwkKeyDn, const u1_
session_t* s;
for(s = LMIC.sessions; sgrpaddr!=0 && s->grpaddr!=grpaddr; s++);
if (s >= LMIC.sessions+MAX_MULTICAST_SESSIONS)
- return 0;
+ return 0;
s->grpaddr = grpaddr;
s->seqnoADn = seqnoADn;
if( nwkKeyDn != (u1_t*)0 ) {
os_copyMem(s->nwkKeyDn, nwkKeyDn, 16);
- os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].nwkSKeyDn, nwkKeyDn, 16);
+ os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].nwkSKeyDn, nwkKeyDn, 16);
}
if( appKey != (u1_t*)0 ) {
os_copyMem(s->appKey, appKey, 16);
- os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].appSKey, appKey, 16);
+ os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].appSKey, appKey, 16);
}
return 1;
}
@@ -3286,6 +3440,7 @@ void LMIC_setLinkCheck (u4_t limit, u4_t delay) {
bit_t LMIC_setupChannel (u1_t chidx, freq_t freq, u2_t drmap) {
if (REG_IS_FIX()) {
+ (void)chidx; (void)freq; (void)drmap; // unused
return 0;
} else {
#ifdef REG_DYN
@@ -3342,6 +3497,11 @@ ostime_t LMIC_nextTx (ostime_t now) {
return nextTx(now);
}
+// Remove duty cycle limitations
+void LMIC_disableDC (void) {
+ LMIC.noDC = 1;
+}
+
#if defined(CFG_simul)
#include "addr2func.h"
#include "arr2len.h"
@@ -3356,11 +3516,6 @@ void LMIC_enableFastJoin (void) {
// XXX deprecated
}
-// Remove duty cycle limitations
-void LMIC_disableDC (void) {
- LMIC.noDC = 1;
-}
-
/// Used for regression testing
ostime_t LMIC_dr2hsym (dr_t dr, s1_t num) {
return dr2hsym(dr,num);
@@ -3389,7 +3544,9 @@ static const rfuncs_t RFUNCS_DYN = {
__dyn(syncDatarate),
__dyn(updateTx),
__dyn(nextTx),
+#if !defined(DISABLE_CLASSB)
__dyn(setBcnRxParams),
+#endif
};
#undef __dyn
#endif // REG_DYN
@@ -3404,7 +3561,9 @@ static const rfuncs_t RFUNCS_FIX = {
__fix(syncDatarate),
__fix(updateTx),
__fix(nextTx),
+#if !defined(DISABLE_CLASSB)
__fix(setBcnRxParams),
+#endif
};
#undef __fix
#endif // REG_FIX
diff --git a/lmic/lmic.h b/lmic/lmic.h
index e6758a5..7fb8542 100644
--- a/lmic/lmic.h
+++ b/lmic/lmic.h
@@ -15,6 +15,10 @@
#include "lorabase.h"
#include "lce.h"
+#ifdef __cplusplus
+extern "C"{
+#endif
+
// LMIC version
#define LMIC_VERSION_MAJOR 2
#define LMIC_VERSION_MINOR 1
@@ -129,7 +133,7 @@ typedef struct {
u1_t beaconLen; // beacon length
ostime_t beaconAirtime; // beacon air time
eirp_t maxEirp; // max. EIRP (initial value)
- u1_t rx1DrOff[8]; // RX1 data rate offsets
+ s1_t rx1DrOff[8]; // RX1 data rate offsets
u1_t dr2maxAppPload[16]; // max application payload (assuming no repeater and no fopts)
u1_t regcode; // external region code
@@ -150,8 +154,8 @@ enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
#define RXDERR_INI 50 // ppm
#endif
-#define LINK_CHECK_OFF (0x80000000)
-#define LINK_CHECK_INIT (-LMIC.adrAckLimit)
+#define LINK_CHECK_OFF ((s4_t)0x80000000)
+#define LINK_CHECK_INIT ((s4_t)(-LMIC.adrAckLimit))
#define LINK_CHECK_DEAD (LMIC.adrAckDelay)
enum { TIME_RESYNC = 6*128 }; // secs
@@ -165,6 +169,7 @@ enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
enum { KEEP_TXPOWADJ = -128 };
+#if !defined(DISABLE_CLASSB)
//! \internal
typedef struct {
u1_t dr;
@@ -176,7 +181,6 @@ typedef struct {
u4_t freq;
} rxsched_t;
-
//! Parsing and tracking states of beacons.
enum { BCN_NONE = 0x00, //!< No beacon received
BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
@@ -195,11 +199,13 @@ typedef struct {
s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
} bcninfo_t;
+#endif
+
// purpose of receive window - lmic_t.rxState
enum { RADIO_STOP=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TXCW, RADIO_CCA, RADIO_INIT, RADIO_CAD, RADIO_TXCONT };
// Netid values / lmic_t.netid
-enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
+enum { NETID_NONE=~0U, NETID_MASK=0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
enum { OP_NONE = 0x0000,
OP_SCAN = 0x0001, // radio scan to find a beacon
@@ -235,7 +241,7 @@ enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, EV_RXCOMPLETE,
EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND, EV_TXSTART,
- EV_TXDONE, EV_DATARATE, EV_START_SCAN, EV_ADR_BACKOFF, EV_SHUTDOWN };
+ EV_TXDONE, EV_DATARATE, EV_START_SCAN, EV_ADR_BACKOFF };
typedef enum _ev_t ev_t;
@@ -290,6 +296,7 @@ struct lmic_t {
s1_t rssi;
s1_t snr;
rps_t rps;
+ rps_t custom_rps; // Used for CUSTOM_DR
u1_t rxsyms;
u1_t dndr;
s1_t txpow; // dBm -- needs to be combined with brdTxPowOff
@@ -336,7 +343,7 @@ struct lmic_t {
s1_t txPowAdj; // adjustment for txpow (ADR controlled)
s1_t brdTxPowOff; // board-specific power adjustment offset
dr_t datarate; // current data rate
- u1_t errcr; // error coding rate (used for TX only)
+ cr_t errcr; // error coding rate (used for TX only)
u1_t rejoinCnt; // adjustment for rejoin datarate
s2_t drift; // last measured drift
s2_t lastDriftDiff;
@@ -393,11 +400,13 @@ struct lmic_t {
u1_t opts; // negotiated protocol options
#endif
+#if !defined(DISABLE_CLASSB)
// Class B state
u1_t missedBcns; // unable to track last N beacons
s1_t askForTime; // how often to ask for time
//XXX:old: u1_t pingSetAns; // answer set cmd and ACK bits
rxsched_t ping; // pingable setup
+#endif
// Public part of MAC state
u1_t txCnt;
@@ -406,12 +415,14 @@ struct lmic_t {
u1_t dataLen; // 0 no data or zero length data, >0 byte count of data
u1_t frame[MAX_LEN_FRAME];
+#if !defined(DISABLE_CLASSB)
u1_t bcnfAns; // mcmd beacon freq: bit7:pending, bit0:ACK/NACK
u1_t bcnChnl;
u4_t bcnFreq; // 0=default, !=0: specific BCN freq/no hopping
u1_t bcnRxsyms; //
ostime_t bcnRxtime;
bcninfo_t bcninfo; // Last received beacon info
+#endif
u1_t noRXIQinversion;
@@ -441,6 +452,80 @@ DECLARE_LMIC; //!< \internal
bit_t LMIC_setupChannel (u1_t channel, freq_t freq, u2_t drmap);
void LMIC_disableChannel (u1_t channel);
+// Manually select the next channel for the next transmission.
+//
+// Warning: This does not do any checking. In particular, this bypasses
+// duty cycle limits, allows selecting a channel that is not configured
+// for the current datarate, and breaks when you select an invalid or
+// disabled channel.
+//
+// The selected channel applies only to the next transmission. Call this
+// *after* setting the datarate (if needed) with LMIC_setDrTxpow(),
+// since that forces a new channel to be selected automatically.
+void LMIC_selectChannel(u1_t channel);
+
+// Use a custom datrate and rps value.
+//
+// This causes the uplink to use the radio settings described by the
+// given rps value, which can be any valid rps setting (even when the
+// region does not normally enable it). The rps setting is used
+// unmodified for uplink, and will have nocrc set for downlink.
+//
+// While the custom datarate is active, it will not be modified
+// automatically (e.g. LinkADRReq is rejected and lowring DR for ADR is
+// suspended), except when it is not enabled for any channel (in dynamic
+// regions).
+//
+// However, if you call this function again to change the rps value for
+// RX1 or RX2 (see below), it will also apply to subsequent uplinks, so
+// you might need to set a new rps or standard datarate before the next
+// uplink.
+//
+// This returns the old uplink DR, which can be later be passed to
+// LMIC_setDrTxpow() to disable the custom datarate again, if needed.
+//
+// RX1
+//
+// Normally, the RX1 datarate is derived from the uplink datarate. When
+// using a custom datarate, it must be set explicitly using the dndr
+// parameter to this function. This can be either a standard datarate
+// value, or CUSTOM_DR to use the same custom rps value as the uplink.
+//
+// To use a custom rps for RX1 that is different from the uplink (or
+// use a custom rps just for RX1), call this function (again) after the
+// EV_TXSTART event (but before EV_TXDONE).
+//
+//
+// RX2
+//
+// To also use a custom datarate for the RX2 window, call this function
+// and set `LMIC.dn2Dr` to CUSTOM_DR. This also causes RXParamSetupReq
+// to be rejected, keeping dn2Dr unmodified.
+//
+// To use a custom rps for RX2 that is different from the uplink and/or
+// RX1 (or use a custom rps just for RX2), call this function (again)
+// after the EV_TXDONE event (but before RX2 starts).
+//
+//
+// Channel selection as normal
+//
+// For fixed regions, any enabled (125kHz) channel will be used.
+//
+// For dynamic regions, any channel that supports CUSTOM_DR will be
+// considered. LMIC_setupChannel() can be used normally, to create one
+// or more channels enabled for CUSTOM_DR. Since the network can
+// potentially disable or reconfigure channels, it is recommended to set
+// up these channels again before every transmission.
+//
+// Disabling CUSTOM_DR
+//
+// To revert uplink and RX1 back to a normal datarate and allow ADR to
+// work again (if enabled), call LMIC_setDrTxpow as normal, passing the
+// DR to use.
+//
+// To revert RX2 back to a normal datarate, just set LMIC.dn2Dr to the
+// appropriate datarate directly.
+dr_t LMIC_setCustomDr (rps_t custom_rps, dr_t dndr);
void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
bit_t LMIC_startJoining (void);
@@ -456,16 +541,22 @@ void LMIC_setTxData (void);
int LMIC_setTxData2 (u1_t port, u1_t* data, u1_t dlen, u1_t confirmed);
void LMIC_sendAlive (void);
+#if !defined(DISABLE_CLASSB)
u1_t LMIC_enableTracking (u1_t tryBcnInfo);
void LMIC_disableTracking (void);
+#endif
void LMIC_setClassC (u1_t enabled);
+#if !defined(DISABLE_CLASSB)
void LMIC_stopPingable (void);
u1_t LMIC_setPingable (u1_t intvExp);
+#endif
void LMIC_tryRejoin (void);
+#if !defined(DISABLE_CLASSB)
int LMIC_scan (ostime_t timeout);
int LMIC_track (ostime_t when);
+#endif
int LMIC_setMultiCastSession (devaddr_t grpaddr, const u1_t* nwkKeyDn, const u1_t* appKey, u4_t seqnoAdn);
void LMIC_setSession (u4_t netid, devaddr_t devaddr, const u1_t* nwkKey,
@@ -484,6 +575,7 @@ rps_t LMIC_dndr2rps (u1_t dr);
ostime_t LMIC_calcAirTime (rps_t rps, u1_t plen);
u1_t LMIC_maxAppPayload();
ostime_t LMIC_nextTx (ostime_t now);
+void LMIC_disableDC (void);
// Simulation only APIs
#if defined(CFG_simul)
@@ -491,11 +583,14 @@ const char* LMIC_addr2func (void* addr);
int LMIC_arr2len (const char* name);
#endif
+// Declare onEvent() function, to make sure any definition will have the
+// C conventions, even when in a C++ file.
+DECL_ON_LMIC_EVENT;
+
// Special APIs - for development or testing
// !!!See implementation for caveats!!!
#if defined(CFG_extapi)
void LMIC_enableFastJoin (void);
-void LMIC_disableDC (void);
ostime_t LMIC_dr2hsym (dr_t dr, s1_t num);
void LMIC_updateTx (ostime_t now);
void LMIC_getRxdErrInfo (s4_t* skew, u4_t* span);
@@ -512,4 +607,8 @@ void LMIC_getRxdErrInfo (s4_t* skew, u4_t* span);
#define TRACE_ADDR(a)
#endif
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
#endif // _lmic_h_
diff --git a/lmic/lorabase.h b/lmic/lorabase.h
index a2c7ee0..0bdad15 100644
--- a/lmic/lorabase.h
+++ b/lmic/lorabase.h
@@ -7,6 +7,10 @@
#ifndef _lorabase_h_
#define _lorabase_h_
+#ifdef __cplusplus
+extern "C"{
+#endif
+
enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 };
enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu };
enum _bw_t { BW125=0, BW250, BW500, BWrfu };
@@ -23,6 +27,11 @@ typedef s1_t eirp_t;
enum { ILLEGAL_DR = 0xFF };
enum { ILLEGAL_RPS = 0xFF };
enum { ILLEGAL_MAS = 0x00 };
+// DR 15 is used in the LinkADRReq to indicate "no DR changes", so it
+// will never be allocated as a standard DR. So it is used here for a
+// custom DR. By using 15, rather than 0xFF or similar, it can be used
+// in per-channel drmap as normal.
+enum { CUSTOM_DR = 0xF };
// Global maximum frame length
enum { BCN_PREAMBLE_LEN = 10 }; // length in symbols - actual time depends on DR
@@ -196,7 +205,7 @@ enum {
MCMD_RKEY_CNF = 0x0B, // - reset confirmation : u1: opt1, [n opts...]
MCMD_ADRP_REQ = 0x0C, // - adr params : u1: 7-4: limit_exp, 3-0: delay_exp
MCMD_TIME_ANS = 0x0D, // - time answer : u4:epoch_secs, u1:fracs
- // Class B -
+ // Class B -
MCMD_PITV_ANS = 0x10, // - ping interval ack : -
MCMD_PNGC_REQ = 0x11, // - set ping freq/dr : u3: freq, u1:7-4:RFU/3-0:datarate
MCMD_BCNI_ANS = 0x12, // - next beacon start : u2: delay(in TUNIT millis), u1:channel -- DEPRECATED
@@ -286,6 +295,12 @@ typedef u4_t devaddr_t;
// RX quality (device)
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
+#define MAKE_LORA_RPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
+// FSK uses a subset of LORA fields, so just use MAKE_LORA_RPS here
+#define MAKE_FSK_RPS(nocrc) (MAKE_LORA_RPS(FSK, 0, 0, 0, nocrc))
+
+inline rps_t makeLoraRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { return MAKE_LORA_RPS(sf, bw, cr, ih, nocrc); }
+inline rps_t makeFskRps (int nocrc) { return MAKE_FSK_RPS(nocrc); }
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
@@ -296,14 +311,8 @@ inline int getNocrc(rps_t params) { return ((params >> 7) &
inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
-inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
- return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
-}
-#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
-#define LWUPDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,0))
-#define LWDNDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,1))
-// Two frames with params r1/r2 would interfere on air: same SFx + BWx
-inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
+inline sf_t isLora (rps_t params) { return getSf(params) >= SF7 && getSf(params) <= SF12; }
+inline sf_t isFsk (rps_t params) { return getSf(params) == FSK; }
// return 1 for low data rate optimize should be enabled (symbol time equal or above 16.384 ms) else 0
// Must be enabled for: SF11/BW125, SF12/BW125, SF12/BW250
@@ -321,5 +330,8 @@ ostime_t calcAirTime (rps_t rps, u1_t plen);
// Sensitivity at given SF/BW
int getSensitivity (rps_t rps);
+#ifdef __cplusplus
+} // extern "C"
+#endif
#endif // _lorabase_h_
diff --git a/lmic/oslmic.c b/lmic/oslmic.c
index 7137e84..d67b039 100644
--- a/lmic/oslmic.c
+++ b/lmic/oslmic.c
@@ -18,12 +18,15 @@ static struct {
} /* anonymous */;
} OS;
+void rng_init (void);
+
void os_init (void* bootarg) {
memset(&OS, 0x00, sizeof(OS));
hal_init(bootarg);
#ifndef CFG_noradio
radio_init(false);
#endif
+ rng_init();
LMIC_init();
}
@@ -33,21 +36,21 @@ void os_init (void* bootarg) {
void rng_init (void) {
#ifdef PERIPH_TRNG
trng_next(OS.randwrds, 4);
+#elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+ radio_generate_random(OS.randwrds, 4);
#else
memcpy(OS.randbuf, __TIME__, 8);
os_getDevEui(OS.randbuf + 8);
#endif
+ OS.randbuf[0] = 16;
}
u1_t os_getRndU1 (void) {
u1_t i = OS.randbuf[0];
- switch( i ) {
- case 0:
- rng_init(); // lazy initialization
- // fall-thru
- case 16:
- os_aes(AES_ENC, OS.randbuf, 16); // encrypt seed with any key
- i = 0;
+ ASSERT(i != 0);
+ if (i == 16) {
+ os_aes(AES_ENC, OS.randbuf, 16); // encrypt seed with any key
+ i = 0;
}
u1_t v = OS.randbuf[i++];
OS.randbuf[0] = i;
@@ -55,6 +58,7 @@ u1_t os_getRndU1 (void) {
}
bit_t os_cca (u2_t rps, u4_t freq) { //XXX:this belongs into os_radio module
+ (void) rps; (void)freq; // unused
return 0; // never grant access
}
@@ -79,9 +83,9 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
for( ; *pnext; pnext = &((*pnext)->next)) {
if(*pnext == job) { // unlink
*pnext = job->next;
- if ((job->flags & OSJOB_FLAG_APPROX) == 0) {
- OS.exact -= 1;
- }
+ if ((job->flags & OSJOB_FLAG_APPROX) == 0) {
+ OS.exact -= 1;
+ }
return 1;
}
}
@@ -97,11 +101,11 @@ static void extendedjobcb (osxjob_t* xjob) {
hal_disableIRQs();
osxtime_t now = os_getXTime();
if (xjob->deadline - now > XJOBTIME_MAX_DIFF) {
- // schedule intermediate callback
- os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) (now + XJOBTIME_MAX_DIFF), (osjobcb_t) extendedjobcb, OSJOB_FLAG_APPROX);
+ // schedule intermediate callback
+ os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) (now + XJOBTIME_MAX_DIFF), (osjobcb_t) extendedjobcb, OSJOB_FLAG_APPROX);
} else {
- // schedule final callback
- os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) xjob->deadline, xjob->func, OSJOB_FLAG_APPROX);
+ // schedule final callback
+ os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) xjob->deadline, xjob->func, OSJOB_FLAG_APPROX);
}
hal_enableIRQs();
}
@@ -114,6 +118,9 @@ void os_setExtendedTimedCallback (osxjob_t* xjob, osxtime_t xtime, osjobcb_t cb)
xjob->deadline = xtime;
extendedjobcb(xjob);
hal_enableIRQs();
+#ifdef DEBUG_JOBS
+ debug_verbose_printf("Scheduled job %u, cb %u at %t\r\n", (unsigned)xjob, (unsigned)cb, xtime);
+#endif // DEBUG_JOBS
}
// clear scheduled job, return 1 if job was removed
@@ -121,6 +128,10 @@ int os_clearCallback (osjob_t* job) {
hal_disableIRQs();
int r = unlinkjob(&OS.scheduledjobs, job);
hal_enableIRQs();
+#ifdef DEBUG_JOBS
+ if (r)
+ debug_verbose_printf("Cleared job %u\r\n", (unsigned)job);
+#endif // DEBUG_JOBS
return r;
}
@@ -135,14 +146,14 @@ void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned
if( flags & OSJOB_FLAG_NOW ) {
time = now;
} else if ( time - now <= 0 ) {
- flags |= OSJOB_FLAG_NOW;
+ flags |= OSJOB_FLAG_NOW;
}
job->deadline = time;
job->func = cb;
job->next = NULL;
job->flags = flags;
if ((flags & OSJOB_FLAG_APPROX) == 0) {
- OS.exact += 1;
+ OS.exact += 1;
}
// insert into schedule
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
@@ -154,6 +165,12 @@ void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned
}
*pnext = job;
hal_enableIRQs();
+#ifdef DEBUG_JOBS
+ if (flags & OSJOB_FLAG_NOW)
+ debug_verbose_printf("Scheduled job %u, cb %u ASAP\r\n", (unsigned)job, (unsigned)cb);
+ else
+ debug_verbose_printf("Scheduled job %u, cb %u%s at %s%t\r\n", (unsigned)job, (unsigned)cb, flags & OSJOB_FLAG_IRQDISABLED ? " (irq disabled)" : "", flags & OSJOB_FLAG_APPROX ? "approx " : "", time);
+#endif // DEBUG_JOBS
}
void os_runstep (void) {
@@ -166,12 +183,20 @@ void os_runstep (void) {
deadline = j->deadline;
if( (deadline - now) <= 0 ) {
OS.scheduledjobs = j->next; // de-queue
- if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0 ) {
+ if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) {
hal_enableIRQs();
+#ifdef DEBUG_JOBS
+ debug_verbose_printf("Running job 0x%08x, cb 0x%08x, deadline %t\r\n",
+ (unsigned int) j, (unsigned int) j->func, (ostime_t)j->deadline);
+#endif
}
hal_watchcount(30); // max 60 sec XXX
j->func(j);
hal_watchcount(0);
+#ifdef DEBUG_JOBS
+ debug_verbose_printf("Ran job 0x%08x, cb 0x%08x, deadline %t\r\n",
+ (unsigned int) j, (unsigned int) j->func, (ostime_t)j->deadline);
+#endif
return;
}
} else {
@@ -184,7 +209,7 @@ void os_runstep (void) {
// execute jobs from timer and from run queue
void os_runloop (void) {
while (1) {
- os_runstep();
+ os_runstep();
}
}
diff --git a/lmic/oslmic.h b/lmic/oslmic.h
index a84bd55..af1961a 100644
--- a/lmic/oslmic.h
+++ b/lmic/oslmic.h
@@ -20,17 +20,17 @@
// Target platform as C library
#include
#include
-typedef uint8_t bit_t;
-typedef uint8_t u1_t;
-typedef int8_t s1_t;
-typedef uint16_t u2_t;
-typedef int16_t s2_t;
-typedef uint32_t u4_t;
-typedef int32_t s4_t;
-typedef uint64_t u8_t;
-typedef int64_t s8_t;
-typedef unsigned int uint;
-typedef const char* str_t;
+typedef uint8_t bit_t;
+typedef uint8_t u1_t;
+typedef int8_t s1_t;
+typedef uint16_t u2_t;
+typedef int16_t s2_t;
+typedef uint32_t u4_t;
+typedef int32_t s4_t;
+typedef uint64_t u8_t;
+typedef int64_t s8_t;
+typedef unsigned int uint;
+typedef const char* str_t;
#include
#if !defined(CFG_simul)
@@ -41,9 +41,9 @@ typedef const char* str_t;
#include
#define ASSERT(cond) do { \
if(!(cond)) { fprintf(stderr, "ASSERTION FAILED: %s at %s:%d\n", \
- #cond, __FILE__, __LINE__); hal_failed(); } } while (0)
+ #cond, __FILE__, __LINE__); hal_failed(); } } while (0)
#elif defined(CFG_DEBUG)
-#define ASSERT(cond) do { if(!(cond)) { debug_printf("%s:%d: assertion failed\r\n", __FILE__, __LINE__); hal_failed(); } } while (0)
+#define ASSERT(cond) do { if(!(cond)) { hal_enableIRQs(); debug_printf("%s:%d: assertion failed\r\n", __FILE__, __LINE__); hal_failed(); } } while (0)
#else
#define ASSERT(cond) do { if(!(cond)) hal_failed(); } while (0)
#endif
@@ -51,17 +51,28 @@ typedef const char* str_t;
#define ASSERT(cond) do { } while (0)
#endif
-#define max(a,b) \
+#include
+#if __cplusplus >= 201103L || defined(static_assert)
+#define LMIC_STATIC_ASSERT static_assert
+#else
+#define LMIC_STATIC_ASSERT(expr, msg)
+#endif
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define os_max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
-#define min(a,b) \
+#define os_min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
-#define os_minmax(vmin,v,vmax) (max(vmin,min(v,vmax)))
+#define os_minmax(vmin,v,vmax) (os_max(vmin,os_min(v,vmax)))
#define os_clearMem(a,b) memset(a,0,b)
#define os_copyMem(a,b,c) memcpy(a,b,c)
#define os_moveMem(a,b,c) memmove(a,b,c)
@@ -133,10 +144,10 @@ extern u4_t AESKEY[];
#define APP(t) (APPDATA)
#endif
-#define LOGCHECK(lvl,block) do { \
- if( lvl <= log_lvl ) { \
- block; \
- } \
+#define LOGCHECK(lvl,block) do { \
+ if( lvl <= log_lvl ) { \
+ block; \
+ } \
} while(0)
#if defined(CFG_simul)
extern int log_lvl;
@@ -184,8 +195,8 @@ u1_t os_getRndU1 (void);
typedef s4_t ostime_t;
typedef s8_t osxtime_t;
-#define OSXTIME_MAX INT64_MAX
-#define OSTIME_MAX_DIFF ((1U << 31) - 1)
+#define OSXTIME_MAX INT64_MAX
+#define OSTIME_MAX_DIFF INT32_MAX
#if !HAS_ostick_conv
#define us2osticks(us) ((ostime_t)( ((s8_t)(us) * OSTICKS_PER_SEC) / 1000000))
@@ -358,5 +369,10 @@ void radio_sleep (void);
void radio_cca (void);
void radio_cad (void);
void radio_cw (void);
+void radio_generate_random (u4_t *words, u1_t len);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
#endif // _oslmic_h_
diff --git a/lmic/persodata.c b/lmic/persodata.c
index b388737..a314f6b 100644
--- a/lmic/persodata.c
+++ b/lmic/persodata.c
@@ -34,7 +34,7 @@ static struct {
static persodata_v1* pd_check_v1 (void* ptr) {
persodata_v1* ppd = ptr;
if( ppd->magic == PERSODATA_MAGIC_V1 ) {
- uint32_t hash[8];
+ uint32_t hash[8];
sha256(hash, ptr, sizeof(persodata_v1) - 32);
if( memcmp(hash, ppd->hash, 32) != 0 ) {
return NULL;
diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c
index 45ccb3e..67ad4dd 100644
--- a/lmic/radio-sx126x.c
+++ b/lmic/radio-sx126x.c
@@ -13,122 +13,138 @@
// ----------------------------------------
// Commands Selecting the Operating Modes of the Radio
-#define CMD_SETSLEEP 0x84
-#define CMD_SETSTANDBY 0x80
-#define CMD_SETFS 0xC1
-#define CMD_SETTX 0x83
-#define CMD_SETRX 0x82
-#define CMD_STOPTIMERONPREAMBLE 0x9F
-#define CMD_SETRXDUTYCYCLE 0x94
-#define CMD_SETCAD 0xC5
-#define CMD_SETTXCONTINUOUSWAVE 0xD1
-#define CMD_SETTXINFINITEPREAMBLE 0xD2
-#define CMD_SETREGULATORMODE 0x96
-#define CMD_CALIBRATE 0x89
-#define CMD_CALIBRATEIMAGE 0x98
-#define CMD_SETPACONFIG 0x95
-#define CMD_SETRXTXFALLBACKMODE 0x93
+#define CMD_SETSLEEP 0x84
+#define CMD_SETSTANDBY 0x80
+#define CMD_SETFS 0xC1
+#define CMD_SETTX 0x83
+#define CMD_SETRX 0x82
+#define CMD_STOPTIMERONPREAMBLE 0x9F
+#define CMD_SETRXDUTYCYCLE 0x94
+#define CMD_SETCAD 0xC5
+#define CMD_SETTXCONTINUOUSWAVE 0xD1
+#define CMD_SETTXINFINITEPREAMBLE 0xD2
+#define CMD_SETREGULATORMODE 0x96
+#define CMD_CALIBRATE 0x89
+#define CMD_CALIBRATEIMAGE 0x98
+#define CMD_SETPACONFIG 0x95
+#define CMD_SETRXTXFALLBACKMODE 0x93
// Commands to Access the Radio Registers and FIFO Buffer
-#define CMD_WRITEREGISTER 0x0D
-#define CMD_READREGISTER 0x1D
-#define CMD_WRITEBUFFER 0x0E
-#define CMD_READBUFFER 0x1E
+#define CMD_WRITEREGISTER 0x0D
+#define CMD_READREGISTER 0x1D
+#define CMD_WRITEBUFFER 0x0E
+#define CMD_READBUFFER 0x1E
// Commands Controlling the Radio IRQs and DIOs
-#define CMD_SETDIOIRQPARAMS 0x08
-#define CMD_GETIRQSTATUS 0x12
-#define CMD_CLEARIRQSTATUS 0x02
-#define CMD_SETDIO2ASRFSWITCHCTRL 0x9D
-#define CMD_SETDIO3ASTCXOCTRL 0x97
+#define CMD_SETDIOIRQPARAMS 0x08
+#define CMD_GETIRQSTATUS 0x12
+#define CMD_CLEARIRQSTATUS 0x02
+#define CMD_SETDIO2ASRFSWITCHCTRL 0x9D
+#define CMD_SETDIO3ASTCXOCTRL 0x97
// Commands Controlling the RF and Packets Settings
-#define CMD_SETRFFREQUENCY 0x86
-#define CMD_SETPACKETTYPE 0x8A
-#define CMD_GETPACKETTYPE 0x11
-#define CMD_SETTXPARAMS 0x8E
-#define CMD_SETMODULATIONPARAMS 0x8B
-#define CMD_SETPACKETPARAMS 0x8C
-#define CMD_SETCADPARAMS 0x88
-#define CMD_SETBUFFERBASEADDRESS 0x8F
-#define CMD_SETLORASYMBNUMTIMEOUT 0xA0
+#define CMD_SETRFFREQUENCY 0x86
+#define CMD_SETPACKETTYPE 0x8A
+#define CMD_GETPACKETTYPE 0x11
+#define CMD_SETTXPARAMS 0x8E
+#define CMD_SETMODULATIONPARAMS 0x8B
+#define CMD_SETPACKETPARAMS 0x8C
+#define CMD_SETCADPARAMS 0x88
+#define CMD_SETBUFFERBASEADDRESS 0x8F
+#define CMD_SETLORASYMBNUMTIMEOUT 0xA0
// Commands Returning the Radio Status
-#define CMD_GETSTATUS 0xC0
-#define CMD_GETRSSIINST 0x15
-#define CMD_GETRXBUFFERSTATUS 0x13
-#define CMD_GETPACKETSTATUS 0x14
-#define CMD_GETDEVICEERRORS 0x17
-#define CMD_CLEARDEVICEERRORS 0x07
-#define CMD_GETSTATS 0x10
-#define CMD_RESETSTATS 0x00
+#define CMD_GETSTATUS 0xC0
+#define CMD_GETRSSIINST 0x15
+#define CMD_GETRXBUFFERSTATUS 0x13
+#define CMD_GETPACKETSTATUS 0x14
+#define CMD_GETDEVICEERRORS 0x17
+#define CMD_CLEARDEVICEERRORS 0x07
+#define CMD_GETSTATS 0x10
+#define CMD_RESETSTATS 0x00
// ----------------------------------------
// List of Registers
-#define REG_WHITENINGMSB 0x06B8
-#define REG_WHITENINGLSB 0x06B9
-#define REG_CRCINITVALMSB 0x06BC
-#define REG_CRCINITVALLSB 0x06BD
-#define REG_CRCPOLYVALMSB 0x06BE
-#define REG_CRCPOLYVALLSB 0x06BF
-#define REG_SYNCWORD0 0x06C0
-#define REG_SYNCWORD1 0x06C1
-#define REG_SYNCWORD2 0x06C2
-#define REG_SYNCWORD3 0x06C3
-#define REG_SYNCWORD4 0x06C4
-#define REG_SYNCWORD5 0x06C5
-#define REG_SYNCWORD6 0x06C6
-#define REG_SYNCWORD7 0x06C7
-#define REG_NODEADDRESS 0x06CD
-#define REG_BROADCASTADDR 0x06CE
-#define REG_LORASYNCWORDMSB 0x0740
-#define REG_LORASYNCWORDLSB 0x0741
-#define REG_RANDOMNUMBERGEN0 0x0819
-#define REG_RANDOMNUMBERGEN1 0x081A
-#define REG_RANDOMNUMBERGEN2 0x081B
-#define REG_RANDOMNUMBERGEN3 0x081C
-#define REG_RXGAIN 0x08AC
-#define REG_OCPCONFIG 0x08E7
-#define REG_XTATRIM 0x0911
-#define REG_XTBTRIM 0x0912
+#define REG_WHITENINGMSB 0x06B8
+#define REG_WHITENINGLSB 0x06B9
+#define REG_CRCINITVALMSB 0x06BC
+#define REG_CRCINITVALLSB 0x06BD
+#define REG_CRCPOLYVALMSB 0x06BE
+#define REG_CRCPOLYVALLSB 0x06BF
+#define REG_SYNCWORD0 0x06C0
+#define REG_SYNCWORD1 0x06C1
+#define REG_SYNCWORD2 0x06C2
+#define REG_SYNCWORD3 0x06C3
+#define REG_SYNCWORD4 0x06C4
+#define REG_SYNCWORD5 0x06C5
+#define REG_SYNCWORD6 0x06C6
+#define REG_SYNCWORD7 0x06C7
+#define REG_NODEADDRESS 0x06CD
+#define REG_BROADCASTADDR 0x06CE
+#define REG_LORASYNCWORDMSB 0x0740
+#define REG_LORASYNCWORDLSB 0x0741
+#define REG_RANDOMNUMBERGEN0 0x0819
+#define REG_RANDOMNUMBERGEN1 0x081A
+#define REG_RANDOMNUMBERGEN2 0x081B
+#define REG_RANDOMNUMBERGEN3 0x081C
+#define REG_RXGAIN 0x08AC
+#define REG_OCPCONFIG 0x08E7
+#define REG_XTATRIM 0x0911
+#define REG_XTBTRIM 0x0912
// sleep modes
-#define SLEEP_COLD 0x00 // (no rtc timeout)
-#define SLEEP_WARM 0x04 // (no rtc timeout)
+#define SLEEP_COLD 0x00 // (no rtc timeout)
+#define SLEEP_WARM 0x04 // (no rtc timeout)
// standby modes
-#define STDBY_RC 0x00
-#define STDBY_XOSC 0x01
+#define STDBY_RC 0x00
+#define STDBY_XOSC 0x01
// regulator modes
-#define REGMODE_LDO 0x00
-#define REGMODE_DCDC 0x01
+#define REGMODE_LDO 0x00
+#define REGMODE_DCDC 0x01
// packet types
-#define PACKET_TYPE_FSK 0x00
-#define PACKET_TYPE_LORA 0x01
+#define PACKET_TYPE_FSK 0x00
+#define PACKET_TYPE_LORA 0x01
// crc types
-#define CRC_OFF 0x01
-#define CRC_1_BYTE 0x00
-#define CRC_2_BYTE 0x02
-#define CRC_1_BYTE_INV 0x04
-#define CRC_2_BYTE_INV 0x06
+#define CRC_OFF 0x01
+#define CRC_1_BYTE 0x00
+#define CRC_2_BYTE 0x02
+#define CRC_1_BYTE_INV 0x04
+#define CRC_2_BYTE_INV 0x06
// irq types
-#define IRQ_TXDONE (1 << 0)
-#define IRQ_RXDONE (1 << 1)
-#define IRQ_PREAMBLEDETECTED (1 << 2)
-#define IRQ_SYNCWORDVALID (1 << 3)
-#define IRQ_HEADERVALID (1 << 4)
-#define IRQ_HEADERERR (1 << 5)
-#define IRQ_CRCERR (1 << 6)
-#define IRQ_CADDONE (1 << 7)
-#define IRQ_CADDETECTED (1 << 8)
-#define IRQ_TIMEOUT (1 << 9)
-#define IRQ_ALL 0x3FF
+#define IRQ_TXDONE (1 << 0)
+#define IRQ_RXDONE (1 << 1)
+#define IRQ_PREAMBLEDETECTED (1 << 2)
+#define IRQ_SYNCWORDVALID (1 << 3)
+#define IRQ_HEADERVALID (1 << 4)
+#define IRQ_HEADERERR (1 << 5)
+#define IRQ_CRCERR (1 << 6)
+#define IRQ_CADDONE (1 << 7)
+#define IRQ_CADDETECTED (1 << 8)
+#define IRQ_TIMEOUT (1 << 9)
+#define IRQ_ALL 0x3FF
+
+// TCXO voltages (limited to VDD - 200mV)
+#define TCXO_VOLTAGE1_6V 0x00
+#define TCXO_VOLTAGE1_7V 0x01
+#define TCXO_VOLTAGE1_8V 0x02
+#define TCXO_VOLTAGE2_2V 0x03
+#define TCXO_VOLTAGE2_4V 0x04
+#define TCXO_VOLTAGE2_7V 0x05
+#define TCXO_VOLTAGE3_0V 0x06
+#define TCXO_VOLTAGE3_3V 0x07
+
+// XXX: These should probably be configurable
+// XXX: The startup time delays TX/RX by 320*15.625=5ms, maybe switch on
+// TCXO early?
+#define TCXO_VOLTAGE TCXO_VOLTAGE1_7V
+#define TCXO_STARTUP_TIME 320 // In multiples of 15.625μs
#define LORA_TXDONE_FIXUP us2osticks(269) // determined by lwtestapp using device pin wired to sx1301 pps...
#define FSK_TXDONE_FIXUP us2osticks(0) // XXX
@@ -190,11 +206,10 @@ static void WriteRegs (uint16_t addr, const uint8_t* data, uint8_t len) {
hal_spi_select(0);
}
-#if 0 // not used
+static void WriteReg (uint16_t addr, uint8_t val) __attribute__((__unused__)); // Ok if this is unused
static void WriteReg (uint16_t addr, uint8_t val) {
WriteRegs(addr, &val, 1);
}
-#endif
static void WriteBuffer (uint8_t off, const uint8_t* data, uint8_t len) {
uint8_t buf[2+len];
@@ -275,6 +290,15 @@ static void SetDIO2AsRfSwitchCtrl (uint8_t enable) {
writecmd(CMD_SETDIO2ASRFSWITCHCTRL, &enable, 1);
}
+// use DIO3 to drive crystal enable switch
+static void SetDIO3AsTcxoCtrl () {
+ uint32_t timeout = TCXO_STARTUP_TIME;
+ uint8_t voltage = TCXO_VOLTAGE1_7V;
+ uint8_t data[] = {voltage, (timeout >> 16) & 0xff, (timeout >> 8) & 0xff, timeout & 0xff };
+
+ writecmd(CMD_SETDIO3ASTCXOCTRL, data, sizeof(data));
+}
+
// write payload to fifo buffer at offset 0
static void WriteFifo (uint8_t *buf, uint8_t len) {
static const uint8_t txrxbase[] = { 0, 0 };
@@ -328,20 +352,20 @@ static void SetPacketType (uint8_t type) {
// calibrate the image rejection
static void CalibrateImage (uint32_t freq) {
static const struct {
- uint32_t min;
- uint32_t max;
- uint8_t freq[2];
+ uint32_t min;
+ uint32_t max;
+ uint8_t freq[2];
} bands[] = {
- { 430000000, 440000000, (uint8_t[]) { 0x6B, 0x6F } },
- { 470000000, 510000000, (uint8_t[]) { 0x75, 0x81 } },
- { 779000000, 787000000, (uint8_t[]) { 0xC1, 0xC5 } },
- { 863000000, 870000000, (uint8_t[]) { 0xD7, 0xDB } },
- { 902000000, 928000000, (uint8_t[]) { 0xE1, 0xE9 } },
+ { 430000000, 440000000, { 0x6B, 0x6F } },
+ { 470000000, 510000000, { 0x75, 0x81 } },
+ { 779000000, 787000000, { 0xC1, 0xC5 } },
+ { 863000000, 870000000, { 0xD7, 0xDB } },
+ { 902000000, 928000000, { 0xE1, 0xE9 } },
};
- for (int i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) {
- if (freq >= bands[i].min && freq <= bands[i].max) {
- writecmd(CMD_CALIBRATEIMAGE, bands[i].freq, 2);
- }
+ for (size_t i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) {
+ if (freq >= bands[i].min && freq <= bands[i].max) {
+ writecmd(CMD_CALIBRATEIMAGE, bands[i].freq, 2);
+ }
}
}
@@ -356,9 +380,9 @@ static void SetRfFrequency (uint32_t freq) {
// configure modulation parameters for LoRa
static void SetModulationParamsLora (u2_t rps) {
uint8_t param[4];
- param[0] = getSf(rps) + 6; // SF (sf7=1)
- param[1] = getBw(rps) + 4; // BW (bw125=0)
- param[2] = getCr(rps) + 1; // CR (cr45=0)
+ param[0] = getSf(rps) - SF7 + 7; // SF (sf7 -> 7)
+ param[1] = getBw(rps) - BW125 + 4; // BW (bw125 -> 4)
+ param[2] = getCr(rps) - CR_4_5 + 1; // CR (cr45 -> 1)
param[3] = enDro(rps); // low-data-rate-opt (symbol time equal or above 16.38 ms)
writecmd(CMD_SETMODULATIONPARAMS, param, 4);
}
@@ -501,32 +525,43 @@ static void SetCrc16 (uint16_t seed, uint16_t polynomial) {
WriteRegs(REG_CRCPOLYVALMSB, buf, 2);
}
-#if 0 // not used
+void radio_sleep (void) {
+ // cache sleep state to avoid unneccessary wakeup (waking up from cold sleep takes about 4ms)
+ if (state.sleeping == 0) {
+ SetSleep(SLEEP_COLD);
+ state.sleeping = 1;
+ }
+}
+
+// Do config common to all RF modes
+static void CommonSetup (void) {
+ SetRegulatorMode(REGMODE_DCDC);
+ if (hal_dio2_controls_rxtx())
+ SetDIO2AsRfSwitchCtrl(1);
+ if (hal_dio3_controls_tcxo())
+ SetDIO3AsTcxoCtrl();
+}
+
+static uint32_t GetRandom (void) __attribute__((__unused__)); // Ok if unused
static uint32_t GetRandom (void) {
- uint8_t buf[4];
+ uint32_t value;
+
+ // Set up oscillator and rx/tx
+ CommonSetup();
+
// continuous rx
SetRx(0xFFFFFF);
// wait 1ms
hal_waitUntil(os_getTime() + ms2osticks(1));
// read random register
- ReadRegs(REG_RANDOMNUMBERGEN0, buf, 4);
+ ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)&value, sizeof(value));
// standby
SetStandby(STDBY_RC);
- return *((uint32_t*) buf);
-}
-#endif
-
-void radio_sleep (void) {
- // cache sleep state to avoid unneccessary wakeup (waking up from cold sleep takes about 4ms)
- if (state.sleeping == 0) {
- SetSleep(SLEEP_COLD);
- state.sleeping = 1;
- }
+ return value;
}
static void txlora (void) {
- SetRegulatorMode(REGMODE_DCDC);
- SetDIO2AsRfSwitchCtrl(1);
+ CommonSetup();
SetStandby(STDBY_RC);
SetPacketType(PACKET_TYPE_LORA);
SetRfFrequency(LMIC.freq);
@@ -550,8 +585,7 @@ static void txlora (void) {
}
static void txfsk (void) {
- SetRegulatorMode(REGMODE_DCDC);
- SetDIO2AsRfSwitchCtrl(1);
+ CommonSetup();
SetStandby(STDBY_RC);
SetPacketType(PACKET_TYPE_FSK);
SetRfFrequency(LMIC.freq);
@@ -576,9 +610,8 @@ static void txfsk (void) {
SetTx(64000); // timeout 1s (should not happen, TXDONE irq will be raised)
}
-static void txcw (void) {
- SetRegulatorMode(REGMODE_DCDC);
- SetDIO2AsRfSwitchCtrl(1);
+void radio_cw (void) {
+ CommonSetup();
SetStandby(STDBY_RC);
SetRfFrequency(LMIC.freq);
SetTxPower(LMIC.txpow + LMIC.brdTxPowOff);
@@ -595,23 +628,21 @@ static void txcw (void) {
void radio_starttx (bool txcontinuous) {
if (txcontinuous) {
ASSERT(0); // not yet supported (continuous packet mode)
- txcw();
} else {
- if (getSf(LMIC.rps) == FSK) { // FSK modem
- txfsk();
- } else { // LoRa modem
- txlora();
- }
- // the radio will go back to STANDBY mode as soon as the TX is finished
- // the corresponding IRQ will inform us about completion.
+ if (isFsk(LMIC.rps)) { // FSK modem
+ txfsk();
+ } else { // LoRa modem
+ txlora();
+ }
+ // the radio will go back to STANDBY mode as soon as the TX is finished
+ // the corresponding IRQ will inform us about completion.
}
}
static void rxfsk (bool rxcontinuous) {
// configure radio (needs rampup time)
ostime_t t0 = os_getTime();
- SetRegulatorMode(REGMODE_DCDC);
- SetDIO2AsRfSwitchCtrl(1);
+ CommonSetup();
SetStandby(STDBY_RC);
SetPacketType(PACKET_TYPE_FSK);
SetRfFrequency(LMIC.freq);
@@ -631,27 +662,28 @@ static void rxfsk (bool rxcontinuous) {
// enable IRQs in HAL
hal_irqmask_set(HAL_IRQMASK_DIO1);
+ ostime_t now = os_getTime();
+ if (!rxcontinuous && LMIC.rxtime - now < 0) {
+ debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n",
+ now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
+ }
+
// now receive (lock interrupts only for final fine tuned rx timing...)
hal_disableIRQs();
if (rxcontinuous) { // continous rx
- BACKTRACE();
- // enable antenna switch for RX (and account power consumption)
- hal_ant_switch(HAL_ANTSW_RX);
- // rx infinitely (no timeout, until rxdone, will be restarted)
- SetRx(0);
+ BACKTRACE();
+ // enable antenna switch for RX (and account power consumption)
+ hal_ant_switch(HAL_ANTSW_RX);
+ // rx infinitely (no timeout, until rxdone, will be restarted)
+ SetRx(0);
} else { // single rx
- BACKTRACE();
- // busy wait until exact rx time
- ostime_t now = os_getTime();
- if (LMIC.rxtime - now < 0) {
- debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n",
- now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
- }
+ BACKTRACE();
+ // busy wait until exact rx time
hal_waitUntil(LMIC.rxtime);
- // enable antenna switch for RX (and account power consumption)
- hal_ant_switch(HAL_ANTSW_RX);
- // rx for max LMIC.rxsyms symbols (rxsyms = nbytes for FSK)
- SetRx((LMIC.rxsyms << 9) / 50); // nbytes * 8 * 64 * 1000 / 50000
+ // enable antenna switch for RX (and account power consumption)
+ hal_ant_switch(HAL_ANTSW_RX);
+ // rx for max LMIC.rxsyms symbols (rxsyms = nbytes for FSK)
+ SetRx((LMIC.rxsyms << 9) / 50); // nbytes * 8 * 64 * 1000 / 50000
}
hal_enableIRQs();
}
@@ -659,8 +691,7 @@ static void rxfsk (bool rxcontinuous) {
static void rxlora (bool rxcontinuous) {
// configure radio (needs rampup time)
ostime_t t0 = os_getTime();
- SetRegulatorMode(REGMODE_DCDC);
- SetDIO2AsRfSwitchCtrl(1);
+ CommonSetup();
SetStandby(STDBY_RC);
SetPacketType(PACKET_TYPE_LORA);
SetRfFrequency(LMIC.freq);
@@ -679,27 +710,30 @@ static void rxlora (bool rxcontinuous) {
// enable IRQs in HAL
hal_irqmask_set(HAL_IRQMASK_DIO1);
+ ostime_t now = os_getTime();
+ if (!rxcontinuous && LMIC.rxtime - now < 0) {
+ // Print before disabling IRQs, to work around deadlock on some
+ // Arduino cores that doe not really support printing without IRQs
+ debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n",
+ now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
+ }
+
// now receive (lock interrupts only for final fine tuned rx timing...)
hal_disableIRQs();
if (rxcontinuous) { // continous rx
- BACKTRACE();
- // enable antenna switch for RX (and account power consumption)
- hal_ant_switch(HAL_ANTSW_RX);
- // rx infinitely (no timeout, until rxdone, will be restarted)
- SetRx(0);
+ BACKTRACE();
+ // enable antenna switch for RX (and account power consumption)
+ hal_ant_switch(HAL_ANTSW_RX);
+ // rx infinitely (no timeout, until rxdone, will be restarted)
+ SetRx(0);
} else { // single rx
- BACKTRACE();
- // busy wait until exact rx time
- ostime_t now = os_getTime();
- if (LMIC.rxtime - now < 0) {
- debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n",
- now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
- }
+ BACKTRACE();
+ // busy wait until exact rx time
hal_waitUntil(LMIC.rxtime);
- // enable antenna switch for RX (and account power consumption)
- hal_ant_switch(HAL_ANTSW_RX);
- // rx for max LMIC.rxsyms symbols
- SetRx(0); // (infinite, timeout set via SetLoRaSymbNumTimeout)
+ // enable antenna switch for RX (and account power consumption)
+ hal_ant_switch(HAL_ANTSW_RX);
+ // rx for max LMIC.rxsyms symbols
+ SetRx(0); // (infinite, timeout set via SetLoRaSymbNumTimeout)
}
hal_enableIRQs();
}
@@ -718,7 +752,7 @@ void radio_cw (void) {
}
void radio_startrx (bool rxcontinuous) {
- if (getSf(LMIC.rps) == FSK) { // FSK modem
+ if (isFsk(LMIC.rps)) { // FSK modem
rxfsk(rxcontinuous);
} else { // LoRa modem
rxlora(rxcontinuous);
@@ -728,7 +762,11 @@ void radio_startrx (bool rxcontinuous) {
// reset radio
static void radio_reset (void) {
// drive RST pin low
- hal_pin_rst(0);
+ bool has_reset = hal_pin_rst(0);
+
+ // If reset is not connected, just continue and hope for the best
+ if (!has_reset)
+ return;
// wait > 100us
hal_waitUntil(os_getTime() + ms2osticks(1));
@@ -756,7 +794,7 @@ void radio_init (bool calibrate) {
ASSERT( ReadReg(REG_LORASYNCWORDLSB) == 0x24 );
if (calibrate) {
- CalibrateImage(LMIC.freq);
+ CalibrateImage(LMIC.freq);
}
// go to SLEEP mode
@@ -765,62 +803,69 @@ void radio_init (bool calibrate) {
hal_enableIRQs();
}
+void radio_generate_random(u4_t *words, u1_t len) {
+ while (len--)
+ *words++ = GetRandom ();
+}
+
// (run by irqjob)
bool radio_irq_process (ostime_t irqtime, u1_t diomask) {
+ (void)diomask; // unused
+
uint16_t irqflags = GetIrqStatus();
// dispatch modem
- if (getSf(LMIC.rps) == FSK) { // FSK modem
- if (irqflags & IRQ_TXDONE) { // TXDONE
- BACKTRACE();
+ if (isFsk(LMIC.rps)) { // FSK modem
+ if (irqflags & IRQ_TXDONE) { // TXDONE
+ BACKTRACE();
// save exact tx time
LMIC.txend = irqtime - FSK_TXDONE_FIXUP;
} else if (irqflags & IRQ_RXDONE) { // RXDONE
- BACKTRACE();
+ BACKTRACE();
// read rx quality parameters
- LMIC.rssi = GetPacketStatusFsk();
- LMIC.snr = 0; // N/A
+ LMIC.rssi = GetPacketStatusFsk();
+ LMIC.snr = 0; // N/A
- // read FIFO
- LMIC.dataLen = ReadFifo(LMIC.frame);
+ // read FIFO
+ LMIC.dataLen = ReadFifo(LMIC.frame);
// save exact rx timestamps
LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp
- LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
+ LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n",
- LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen);
+ debug_printf("RX[rssi=%d,len=%d]: %h\r\n",
+ LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen);
#endif
- } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT
- BACKTRACE();
+ } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT
+ BACKTRACE();
// indicate timeout
LMIC.dataLen = 0;
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,FSK]: TIMEOUT\r\n", LMIC.freq, 6);
+ debug_printf("RX: TIMEOUT\r\n");
#endif
} else {
- // unexpected irq
- debug_printf("UNEXPECTED RADIO IRQ %04x (after %d ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3);
- TRACE_VAL(irqflags);
- ASSERT(0);
- }
+ // unexpected irq
+ debug_printf("UNEXPECTED RADIO IRQ %04x (after %ld ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3);
+ TRACE_VAL(irqflags);
+ ASSERT(0);
+ }
} else { // LORA modem
- if (irqflags & IRQ_TXDONE) { // TXDONE
- BACKTRACE();
+ if (irqflags & IRQ_TXDONE) { // TXDONE
+ BACKTRACE();
// save exact tx time
LMIC.txend = irqtime - LORA_TXDONE_FIXUP;
} else if (irqflags & IRQ_RXDONE) { // RXDONE
- BACKTRACE();
+ BACKTRACE();
// read rx quality parameters
- GetPacketStatusLora(&LMIC.rssi, &LMIC.snr);
+ GetPacketStatusLora(&LMIC.rssi, &LMIC.snr);
- // read FIFO
- LMIC.dataLen = ReadFifo(LMIC.frame);
+ // read FIFO
+ LMIC.dataLen = ReadFifo(LMIC.frame);
// save exact rx timestamps
LMIC.rxtime = irqtime; // end of frame timestamp
@@ -830,27 +875,25 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) {
else if (getBw(LMIC.rps) == BW500) {
LMIC.rxtime -= LORA_RXDONE_FIXUP_500[getSf(LMIC.rps)];
}
- LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
+ LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %h\r\n",
- LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps),
- LMIC.rssi - RSSI_OFF, LMIC.snr * 100 / SNR_SCALEUP, 2,
- LMIC.dataLen, LMIC.frame, LMIC.dataLen);
+ debug_printf("RX[rssi=%d,snr=%.2F,len=%d]: %h\r\n",
+ LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2,
+ LMIC.dataLen, LMIC.frame, LMIC.dataLen);
#endif
- } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT
- BACKTRACE();
+ } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT
+ BACKTRACE();
// indicate timeout
LMIC.dataLen = 0;
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT\r\n",
- LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps));
+ debug_printf("RX: TIMEOUT\r\n");
#endif
} else {
- // unexpected irq
- debug_printf("UNEXPECTED RADIO IRQ %04x\r\n", irqflags);
- TRACE_VAL(irqflags);
- ASSERT(0);
- }
+ // unexpected irq
+ debug_printf("UNEXPECTED RADIO IRQ %04x\r\n", irqflags);
+ TRACE_VAL(irqflags);
+ ASSERT(0);
+ }
}
// mask all IRQs
diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c
index 5d5a364..9e43bf9 100644
--- a/lmic/radio-sx127x.c
+++ b/lmic/radio-sx127x.c
@@ -373,35 +373,35 @@ static void setopmode (u1_t opmode) {
writeReg(RegOpMode, opmode);
ostime_t t0 = os_getTime();
while (readReg(RegOpMode) != opmode) {
- if (os_getTime() - t0 > ms2osticks(20)) {
- // panic when opmode is not reached within 20ms
- debug_printf("FAILED TO SET OPMODE %02x within 20ms\r\n", opmode);
- ASSERT(0);
- }
+ if (os_getTime() - t0 > ms2osticks(20)) {
+ // panic when opmode is not reached within 20ms
+ debug_printf("FAILED TO SET OPMODE %02x within 20ms\r\n", opmode);
+ ASSERT(0);
+ }
}
}
// fill fifo when empty
static void LoadFifo (void) {
if (state.fifolen > 0) {
- int n = (state.fifolen > FIFOTHRESH) ? FIFOTHRESH : state.fifolen;
- radio_writeBuf(RegFifo, state.fifoptr, n);
- state.fifoptr += n;
- state.fifolen -= n;
+ int n = (state.fifolen > FIFOTHRESH) ? FIFOTHRESH : state.fifolen;
+ radio_writeBuf(RegFifo, state.fifoptr, n);
+ state.fifoptr += n;
+ state.fifolen -= n;
}
}
// read fifo when level or ready
static void UnloadFifo (void) {
if (state.fifolen < 0) { // first byte
- state.fifolen = 0;
- radio_readBuf(RegFifo, &LMIC.dataLen, 1);
+ state.fifolen = 0;
+ radio_readBuf(RegFifo, &LMIC.dataLen, 1);
}
int n = (LMIC.dataLen - state.fifolen > (FIFOTHRESH-1)) ? (FIFOTHRESH-1) : (LMIC.dataLen - state.fifolen); // errata: unload one byte less
if (n) {
- radio_readBuf(RegFifo, state.fifoptr, n);
- state.fifoptr += n;
- state.fifolen += n;
+ radio_readBuf(RegFifo, state.fifoptr, n);
+ state.fifoptr += n;
+ state.fifolen += n;
}
}
@@ -410,47 +410,47 @@ static void configLoraModem (bool txcont) {
#if defined(BRD_sx1276_radio)
// set ModemConfig1 'bbbbccch' (bw=xxxx, cr=xxx, implicitheader=x)
writeReg(LORARegModemConfig1,
- ((getBw(LMIC.rps) + 7) << 4) | // BW125=0 --> 7
- ((getCr(LMIC.rps) + 1) << 1) | // CR4_5=0 --> 1
- (getIh(LMIC.rps) != 0)); // implicit header
+ ((getBw(LMIC.rps) - BW125 + 7) << 4) | // BW125 --> 7
+ ((getCr(LMIC.rps) - CR_4_5 + 1) << 1) | // CR_4_5 --> 1
+ (getIh(LMIC.rps) != 0)); // implicit header
// set ModemConfig2 'sssstcmm' (sf=xxxx, txcont=x, rxpayloadcrc=x, symtimeoutmsb=00)
writeReg(LORARegModemConfig2,
- ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7
+ ((getSf(LMIC.rps)-SF7+7) << 4) | // SF7 --> 7
(txcont ? 0x08 : 0x00) | // txcont: 0x08
- ((getNocrc(LMIC.rps) == 0) << 2)); // rxcrc
+ ((getNocrc(LMIC.rps) == 0) << 2)); // rxcrc
// set ModemConfig3 'uuuuoarr' (unused=0000, lowdatarateoptimize=x, agcauto=1, reserved=00)
writeReg(LORARegModemConfig3,
- (enDro(LMIC.rps) << 3) | // symtime >= 16ms
- (1 << 2)); // autoagc
+ (enDro(LMIC.rps) << 3) | // symtime >= 16ms
+ (1 << 2)); // autoagc
// SX1276 Errata: 2.1 Sensitivity Optimization with a 500kHz Bandwith
if (getBw(LMIC.rps) == BW500) {
- writeReg(0x36, 0x02);
- writeReg(0x3A, 0x64);
+ writeReg(0x36, 0x02);
+ writeReg(0x3A, 0x64);
} else {
- writeReg(0x36, 0x03);
- // no need to reset register 0x3a
+ writeReg(0x36, 0x03);
+ // no need to reset register 0x3a
}
#elif defined(BRD_sx1272_radio)
// set ModemConfig1 'bbccchco' (bw=xx, cr=xxx, implicitheader=x, rxpayloadcrc=x, lowdatarateoptimize=x)
writeReg(LORARegModemConfig1,
- (getBw(LMIC.rps) << 6) | // BW125=0 --> 0
- ((getCr(LMIC.rps) + 1) << 3) | // CR4_5=0 --> 1
- ((getIh(LMIC.rps) != 0) << 2) | // implicit header
- ((getNocrc(LMIC.rps) == 0) << 1) | // rxcrc
- enDro(LMIC.rps)); // symtime >= 16ms
+ ((getBw(LMIC.rps) - BW125) << 6) | // BW125 --> 0
+ ((getCr(LMIC.rps) - CR_4_5 + 1) << 3) | // CR_4_5 --> 1
+ ((getIh(LMIC.rps) != 0) << 2) | // implicit header
+ ((getNocrc(LMIC.rps) == 0) << 1) | // rxcrc
+ enDro(LMIC.rps)); // symtime >= 16ms
// set ModemConfig2 'sssstamm' (sf=xxxx, txcont=x, agcauto=1 symtimeoutmsb=00)
writeReg(LORARegModemConfig2,
- ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7
+ ((getSf(LMIC.rps)-SF7+7) << 4) | // SF7 --> 7
(txcont ? 0x08 : 0x00) | // txcont: 0x08
- (1 << 2)); // autoagc
+ (1 << 2)); // autoagc
#endif // BRD_sx1272_radio
if (getIh(LMIC.rps)) {
- writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+ writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
}
}
@@ -514,6 +514,7 @@ static void setRadioConsumption_ua (bool boost, u1_t pow) {
ua = RFOPOW[pow];
}
#else
+ (void)boost; (void)pow; // unused
ua = 120000; // something better than nothing?
#endif
LMIC.radioPwr_ua = ua;
@@ -552,47 +553,47 @@ static void configPower (int pw) {
// XXX - TODO - externalize this somehow
// wailmer/wailord can only use 17dBm at DR4 (US)
if (getBw(LMIC.rps) == BW500 && pw > 17) {
- pw = 17;
+ pw = 17;
}
#endif
if (BRD_PABOOSTSEL(LMIC.freq, pw)) { // use PA_BOOST
- if (pw > 17) { // use high-power +20dBm option
- if (pw > 20) {
- pw = 20;
- }
- writeReg(RegPaDac, 0x87); // high power
- writeReg(RegPaConfig, 0x80 | (pw - 5)); // BOOST (5..20dBm)
- } else {
- if (pw < 2) {
- pw = 2;
- }
- writeReg(RegPaDac, 0x84); // normal power
- writeReg(RegPaConfig, 0x80 | (pw - 2)); // BOOST (2..17dBm)
- }
+ if (pw > 17) { // use high-power +20dBm option
+ if (pw > 20) {
+ pw = 20;
+ }
+ writeReg(RegPaDac, 0x87); // high power
+ writeReg(RegPaConfig, 0x80 | (pw - 5)); // BOOST (5..20dBm)
+ } else {
+ if (pw < 2) {
+ pw = 2;
+ }
+ writeReg(RegPaDac, 0x84); // normal power
+ writeReg(RegPaConfig, 0x80 | (pw - 2)); // BOOST (2..17dBm)
+ }
setRadioConsumption_ua(true, pw);
} else { // use PA_RFO
#if defined(BRD_sx1276_radio)
- if (pw > 0) {
- if (pw > 15) {
- pw = 15;
- }
- writeReg(RegPaConfig, 0x70 | pw); // RFO, maxpower=111 (0..15dBm)
- } else {
- if (pw < -4) {
- pw = -4;
- }
- writeReg(RegPaConfig, pw + 4); // RFO, maxpower=000 (-4..11dBm)
- }
- writeReg(RegPaDac, 0x84); // normal power
+ if (pw > 0) {
+ if (pw > 15) {
+ pw = 15;
+ }
+ writeReg(RegPaConfig, 0x70 | pw); // RFO, maxpower=111 (0..15dBm)
+ } else {
+ if (pw < -4) {
+ pw = -4;
+ }
+ writeReg(RegPaConfig, pw + 4); // RFO, maxpower=000 (-4..11dBm)
+ }
+ writeReg(RegPaDac, 0x84); // normal power
#elif defined(BRD_sx1272_radio)
- if (pw < -1) {
- pw = -1;
- } else if (pw > 14) {
- pw = 14;
- }
- writeReg(RegPaConfig, pw + 1); // RFO (-1..14dBm)
- writeReg(RegPaDac, 0x84); // normal power
+ if (pw < -1) {
+ pw = -1;
+ } else if (pw > 14) {
+ pw = 14;
+ }
+ writeReg(RegPaConfig, pw + 1); // RFO (-1..14dBm)
+ writeReg(RegPaDac, 0x84); // normal power
#endif
setRadioConsumption_ua(false, (pw < 0) ? 0 : pw);
}
@@ -604,9 +605,9 @@ static void configPower (int pw) {
static void power_tcxo (void) {
// power-up TCXO and set tcxo as input
if ( hal_pin_tcxo(1) ) {
- writeReg(RegTcxo, 0b00011001); // reserved=000, tcxo=1, reserved=1001
- // delay to allow TCXO to wake up
- hal_waitUntil(os_getTime() + ms2osticks(1));
+ writeReg(RegTcxo, 0b00011001); // reserved=000, tcxo=1, reserved=1001
+ // delay to allow TCXO to wake up
+ hal_waitUntil(os_getTime() + ms2osticks(1));
}
}
@@ -694,11 +695,11 @@ static void txfsk (bool txcont) {
LoadFifo();
if (!txcont) {
- // enable IRQs in HAL
- hal_irqmask_set(HAL_IRQMASK_DIO0 | HAL_IRQMASK_DIO1);
+ // enable IRQs in HAL
+ hal_irqmask_set(HAL_IRQMASK_DIO0 | HAL_IRQMASK_DIO1);
- // set tx timeout
- radio_set_irq_timeout(os_getTime() + us2osticks((FIFOTHRESH+10)*8*1000/50));
+ // set tx timeout
+ radio_set_irq_timeout(os_getTime() + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50));
}
// enable antenna switch for TX
@@ -859,8 +860,8 @@ static void rxlorasingle (void) {
hal_enableIRQs();
// warn about delayed rx
if( rxtime - now < 0 ) {
- debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n",
- now - rxtime, osticks2ms(now - t0), now - t0);
+ debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n",
+ now - rxtime, osticks2ms(now - t0), now - t0);
}
}
@@ -980,22 +981,22 @@ static void rxfsk (bool rxcontinuous) {
hal_disableIRQs();
if (rxcontinuous) {
- BACKTRACE();
- // XXX not suppported - receiver does not automatically restart
- radio_set_irq_timeout(os_getTime() + sec2osticks(5)); // time out after 5 sec
+ BACKTRACE();
+ // XXX not suppported - receiver does not automatically restart
+ radio_set_irq_timeout(os_getTime() + sec2osticks(5)); // time out after 5 sec
} else {
- BACKTRACE();
- // set preamble timeout
- writeReg(FSKRegRxTimeout2, (LMIC.rxsyms + 1) / 2); // (TimeoutRxPreamble * 16 * Tbit)
- // set rx timeout
- radio_set_irq_timeout(LMIC.rxtime + us2osticks((2*FIFOTHRESH)*8*1000/50));
- // busy wait until exact rx time
- ostime_t now = os_getTime();
- if (LMIC.rxtime - now < 0) {
- debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n",
- now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
- }
- hal_waitUntil(LMIC.rxtime);
+ BACKTRACE();
+ // set preamble timeout
+ writeReg(FSKRegRxTimeout2, (LMIC.rxsyms + 1) / 2); // (TimeoutRxPreamble * 16 * Tbit)
+ // set rx timeout
+ radio_set_irq_timeout(LMIC.rxtime + us2osticks((u4_t)(2*FIFOTHRESH)*8*1000/50));
+ // busy wait until exact rx time
+ ostime_t now = os_getTime();
+ if (LMIC.rxtime - now < 0) {
+ debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n",
+ now - LMIC.rxtime, osticks2ms(now - t0), now - t0);
+ }
+ hal_waitUntil(LMIC.rxtime);
}
// enable antenna switch for RX (and account power consumption)
@@ -1012,14 +1013,14 @@ void radio_startrx (bool rxcontinuous) {
// set power consumption for statistics
LMIC.radioPwr_ua = 11500;
- if (getSf(LMIC.rps) == FSK) { // FSK modem
+ if (isFsk(LMIC.rps)) { // FSK modem
rxfsk(rxcontinuous);
} else { // LoRa modem
if (rxcontinuous) {
- rxloracont();
- } else {
- rxlorasingle();
- }
+ rxloracont();
+ } else {
+ rxlorasingle();
+ }
}
}
@@ -1031,7 +1032,7 @@ void radio_cad (void) {
void radio_starttx (bool txcontinuous) {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
- if (getSf(LMIC.rps) == FSK) { // FSK modem
+ if (isFsk(LMIC.rps)) { // FSK modem
txfsk(txcontinuous);
} else { // LoRa modem
txlora(txcontinuous);
@@ -1058,8 +1059,8 @@ void radio_cca (void) {
writeReg(RegLna, 0b00100011); // highest gain, boost enable
// set receiver bandwidth (SSB)
- writeReg(FSKRegRxBw, (getSf(LMIC.rps) == FSK) ? 0x0B /* 50kHz SSB */ :
- 3 - getBw(LMIC.rps)); // 62.5/125/250kHz SSB (RxBwMant=0, RxBwExp 3/2/1)
+ writeReg(FSKRegRxBw, (isFsk(LMIC.rps)) ? 0x0B /* 50kHz SSB */ :
+ 3 - getBw(LMIC.rps)); // 62.5/125/250kHz SSB (RxBwMant=0, RxBwExp 3/2/1)
// set power consumption for statistics
LMIC.radioPwr_ua = 11500;
@@ -1079,9 +1080,9 @@ void radio_cca (void) {
// sample rssi values
do {
rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF;
- if (rssi > rssi_max) {
- rssi_max = rssi;
- }
+ if (rssi > rssi_max) {
+ rssi_max = rssi;
+ }
} while (rssi < rssi_th && os_getTime() - t0 < LMIC.rxtime);
// return max observed rssi value
@@ -1100,7 +1101,14 @@ void radio_cca (void) {
// reset radio
static void radio_reset (void) {
// drive RST pin
- hal_pin_rst(RST_PIN_RESET_STATE);
+ bool has_reset = hal_pin_rst(RST_PIN_RESET_STATE);
+
+ // power-down TCXO
+ hal_pin_tcxo(0);
+
+ // If reset is not connected, just continue and hope for the best
+ if (!has_reset)
+ return;
// wait > 100us
hal_waitUntil(os_getTime() + ms2osticks(1));
@@ -1133,12 +1141,12 @@ void radio_init (bool calibrate) {
// optionally perform receiver chain calibration in FSK/STANDBY mode
if (calibrate) {
- // set band/frequency
- configChannel();
+ // set band/frequency
+ configChannel();
- // run receiver chain calibration
- writeReg(FSKRegImageCal, RF_IMAGECAL_IMAGECAL_START); // (clear auto-cal)
- while ( readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING );
+ // run receiver chain calibration
+ writeReg(FSKRegImageCal, RF_IMAGECAL_IMAGECAL_START); // (clear auto-cal)
+ while ( readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING );
}
// go to SLEEP mode
@@ -1152,96 +1160,98 @@ void radio_init (bool calibrate) {
// (run by irqjob)
bool radio_irq_process (ostime_t irqtime, u1_t diomask) {
+ (void)diomask; //unused
+
// dispatch modem
- if (getSf(LMIC.rps) == FSK) { // FSK modem
- u1_t irqflags1 = readReg(FSKRegIrqFlags1);
- u1_t irqflags2 = readReg(FSKRegIrqFlags2);
+ if (isFsk(LMIC.rps)) { // FSK modem
+ u1_t irqflags1 = readReg(FSKRegIrqFlags1);
+ u1_t irqflags2 = readReg(FSKRegIrqFlags2);
- if (irqflags2 & IRQ_FSK2_PACKETSENT_MASK) { // TXDONE
- BACKTRACE();
+ if (irqflags2 & IRQ_FSK2_PACKETSENT_MASK) { // TXDONE
+ BACKTRACE();
// save exact tx time
LMIC.txend = irqtime - FSK_TXDONE_FIXUP;
- } else if (irqflags2 & IRQ_FSK2_PAYLOADREADY_MASK) { // RXDONE
- BACKTRACE();
+ } else if (irqflags2 & IRQ_FSK2_PAYLOADREADY_MASK) { // RXDONE
+ BACKTRACE();
// read rx quality parameters (at end of packet, not optimal since energy might already be gone)
- // (unfortunately in SX1272/SX1276 no averaged RSSI available in FSK mode, better in SX1261)
- LMIC.rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF;
- LMIC.snr = 0; // N/A
+ // (unfortunately in SX1272/SX1276 no averaged RSSI available in FSK mode, better in SX1261)
+ LMIC.rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF;
+ LMIC.snr = 0; // N/A
- // read FIFO
- UnloadFifo();
+ // read FIFO
+ UnloadFifo();
// save exact rx timestamps
LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp
- LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
+ LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n",
- LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen);
+ debug_printf("RX[rssi=%d,len=%d]: %h\r\n",
+ LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen);
#endif
- } else if (irqflags1 & IRQ_FSK1_TIMEOUT_MASK) { // TIMEOUT
- BACKTRACE();
+ } else if (irqflags1 & IRQ_FSK1_TIMEOUT_MASK) { // TIMEOUT
+ BACKTRACE();
// indicate timeout
LMIC.dataLen = 0;
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,FSK]: TIMEOUT (%d us)\r\n", LMIC.freq, 6, osticks2us(irqtime - LMIC.rxtime));
+ debug_printf("RX: TIMEOUT (%d us)\r\n", osticks2us(irqtime - LMIC.rxtime));
#endif
- } else if( irqflags2 & IRQ_FSK2_FIFOEMPTY_MASK ) { // FIFOEMPTY (TX)
- BACKTRACE();
+ } else if( irqflags2 & IRQ_FSK2_FIFOEMPTY_MASK ) { // FIFOEMPTY (TX)
+ BACKTRACE();
- // fill FIFO buffer
- LoadFifo();
+ // fill FIFO buffer
+ LoadFifo();
- // update tx timeout
- radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50));
+ // update tx timeout
+ radio_set_irq_timeout(irqtime + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50));
- // keep waiting for FifoEmpty or PacketSent interrupt
- return false;
+ // keep waiting for FifoEmpty or PacketSent interrupt
+ return false;
- } else if( irqflags2 & IRQ_FSK2_FIFOLEVEL_MASK ) { // FIFOLEVEL (RX)
- BACKTRACE();
+ } else if( irqflags2 & IRQ_FSK2_FIFOLEVEL_MASK ) { // FIFOLEVEL (RX)
+ BACKTRACE();
- // read FIFO buffer
- UnloadFifo();
+ // read FIFO buffer
+ UnloadFifo();
- // update rx timeout
- radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50));
+ // update rx timeout
+ radio_set_irq_timeout(irqtime + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50));
- // keep waiting for FifoLevel or PayloadReady interrupt
- return false;
+ // keep waiting for FifoLevel or PayloadReady interrupt
+ return false;
- } else {
- // unexpected irq
- debug_printf("UNEXPECTED FSK IRQ %02x %02x\r\n", irqflags1, irqflags2);
- ASSERT(0);
- }
+ } else {
+ // unexpected irq
+ debug_printf("UNEXPECTED FSK IRQ %02x %02x\r\n", irqflags1, irqflags2);
+ ASSERT(0);
+ }
- // clear FSK IRQ flags
- writeReg(FSKRegIrqFlags1, 0xFF);
- writeReg(FSKRegIrqFlags2, 0xFF);
+ // clear FSK IRQ flags
+ writeReg(FSKRegIrqFlags1, 0xFF);
+ writeReg(FSKRegIrqFlags2, 0xFF);
} else { // LORA modem
- u1_t irqflags = readReg(LORARegIrqFlags);
+ u1_t irqflags = readReg(LORARegIrqFlags);
if (irqflags & IRQ_LORA_TXDONE_MASK) { // TXDONE
- BACKTRACE();
+ BACKTRACE();
// save exact tx time
LMIC.txend = irqtime - LORA_TXDONE_FIXUP;
} else if (irqflags & IRQ_LORA_RXDONE_MASK) { // RXDONE (rx or scan)
- BACKTRACE();
+ BACKTRACE();
// read rx quality parameters (averaged over packet)
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
- LMIC.rssi = readReg(LORARegPktRssiValue); // final values -128..127 correspond to -196...+63 dBm (subtract RSSI_OFF)
- if (LMIC.snr < 0) {
- LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi + LMIC.snr/4 + RSSI_OFF;
- } else {
- LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi * 16/15 + RSSI_OFF;
- }
+ LMIC.rssi = readReg(LORARegPktRssiValue); // final values -128..127 correspond to -196...+63 dBm (subtract RSSI_OFF)
+ if (LMIC.snr < 0) {
+ LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi + LMIC.snr/4 + RSSI_OFF;
+ } else {
+ LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi * 16/15 + RSSI_OFF;
+ }
// get PDU length
LMIC.dataLen = readReg(LORARegRxNbBytes);
@@ -1254,49 +1264,47 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) {
else if (getBw(LMIC.rps) == BW500) {
LMIC.rxtime -= LORA_RXDONE_FIXUP_500[getSf(LMIC.rps)];
}
- LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
+ LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp
- // set FIFO read address pointer (to address of last packet received)
- writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
+ // set FIFO read address pointer (to address of last packet received)
+ writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
- // read FIFO
- radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+ // read FIFO
+ radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %.80h\r\n",
- LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps),
- LMIC.rssi - RSSI_OFF, LMIC.snr * 100 / SNR_SCALEUP, 2,
- LMIC.dataLen, LMIC.frame, LMIC.dataLen);
+ debug_printf("RX[rssi=%d,snr=%.2F,len=%d]: %.80h\r\n",
+ LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2,
+ LMIC.dataLen, LMIC.frame, LMIC.dataLen);
#endif
- } else if (irqflags & IRQ_LORA_RXTOUT_MASK) { // RXTOUT
- BACKTRACE();
+ } else if (irqflags & IRQ_LORA_RXTOUT_MASK) { // RXTOUT
+ BACKTRACE();
// indicate timeout
LMIC.dataLen = 0;
#ifdef DEBUG_RX
- debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT (%d us)\r\n",
- LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), osticks2us(irqtime - LMIC.rxtime));
+ debug_printf("RX: TIMEOUT (%d us)\r\n");
#endif
- } else if (irqflags & IRQ_LORA_CDDONE_MASK) { // CDDONE
- BACKTRACE();
- // check if preamble symbol was detected
- if (irqflags & IRQ_LORA_CDDETD_MASK) {
- // switch to receiving (continuous)
- writeReg(RegOpMode, OPMODE_LORA_RX);
- // continue waiting
- return false;
- } else {
- // indicate timeout
- LMIC.dataLen = 0;
- }
+ } else if (irqflags & IRQ_LORA_CDDONE_MASK) { // CDDONE
+ BACKTRACE();
+ // check if preamble symbol was detected
+ if (irqflags & IRQ_LORA_CDDETD_MASK) {
+ // switch to receiving (continuous)
+ writeReg(RegOpMode, OPMODE_LORA_RX);
+ // continue waiting
+ return false;
+ } else {
+ // indicate timeout
+ LMIC.dataLen = 0;
+ }
} else {
- // unexpected irq
- ASSERT(0);
- }
+ // unexpected irq
+ ASSERT(0);
+ }
- // mask all LoRa IRQs
- writeReg(LORARegIrqFlagsMask, 0xFF);
+ // mask all LoRa IRQs
+ writeReg(LORARegIrqFlagsMask, 0xFF);
- // clear LoRa IRQ flags
- writeReg(LORARegIrqFlags, 0xFF);
+ // clear LoRa IRQ flags
+ writeReg(LORARegIrqFlags, 0xFF);
}
// radio operation completed
diff --git a/lmic/radio.c b/lmic/radio.c
index e1e702b..6bab834 100644
--- a/lmic/radio.c
+++ b/lmic/radio.c
@@ -26,8 +26,11 @@ static void radio_stop (void) {
radio_sleep();
// disable antenna switch
hal_ant_switch(HAL_ANTSW_OFF);
+#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
// power-down TCXO
hal_pin_tcxo(0);
+#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
+ // disable antenna switch
// disable IRQs in HAL
hal_irqmask_set(0);
// cancel radio job
@@ -41,13 +44,14 @@ static void radio_stop (void) {
// protected job - runs with irqs disabled!
static void radio_irq_timeout (osjob_t* j) {
BACKTRACE();
+ (void)j; // unused
// stop everything (antenna switch, hal irqs, sleep, irq job)
radio_stop();
// re-initialize radio if tx operation timed out
if (state.txmode) {
- radio_init(true);
+ radio_init(true);
}
// enable IRQs!
@@ -69,13 +73,14 @@ void radio_set_irq_timeout (ostime_t timeout) {
// (run by irqjob)
static void radio_irq_func (osjob_t* j) {
+ (void)j; // unused
// call radio-specific processing function
if( radio_irq_process(state.irqtime, state.diomask) ) {
- // current radio operation has completed
- radio_stop(); // (disable antenna switch and HAL irqs, make radio sleep)
+ // current radio operation has completed
+ radio_stop(); // (disable antenna switch and HAL irqs, make radio sleep)
- // run LMIC job (use preset func ptr)
- os_setCallback(&LMIC.osjob, LMIC.osjob.func);
+ // run LMIC job (use preset func ptr)
+ os_setCallback(&LMIC.osjob, LMIC.osjob.func);
}
// clear irq state (job has been run)
@@ -101,71 +106,104 @@ void radio_irq_handler (u1_t diomask, ostime_t ticks) {
void os_radio (u1_t mode) {
switch (mode) {
- case RADIO_STOP:
- radio_stop();
- break;
+ case RADIO_STOP:
+ radio_stop();
+ break;
- case RADIO_TX:
- radio_stop();
+ case RADIO_TX:
+ radio_stop();
#ifdef DEBUG_TX
- debug_printf("TX[fcnt=%d,freq=%.1F,sf=%d,bw=%d,pow=%d,len=%d%s]: %.80h\r\n",
- LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps),
- LMIC.txpow, LMIC.dataLen,
- (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "",
- LMIC.frame, LMIC.dataLen);
+ if( isFsk(LMIC.rps) ) {
+ debug_printf("TX[mod=FSK,nocrc=%d", getNocrc(LMIC.rps));
+ } else {
+ ASSERT(isLora(LMIC.rps));
+ debug_printf("TX[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d",
+ getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125),
+ getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps));
+ }
+ debug_printf_continue(",fcnt=%lu,freq=%.1F,pow=%d,len=%d%s]: %.80h\r\n",
+ (LMIC.seqnoUp ? LMIC.seqnoUp - 1 : 0),
+ LMIC.freq, 6,
+ LMIC.txpow, LMIC.dataLen,
+ (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "",
+ LMIC.frame, LMIC.dataLen);
+#endif
+ // transmit frame now (wait for completion interrupt)
+ radio_starttx(false);
+ // set timeout for tx operation (should not happen)
+ state.txmode = 1;
+ radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100);
+ break;
+
+ case RADIO_RX:
+ radio_stop();
+#ifdef DEBUG_RX
+ if( isFsk(LMIC.rps) ) {
+ debug_printf("RX_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps));
+ } else {
+ ASSERT(isLora(LMIC.rps));
+ debug_printf("RX_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d",
+ getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125),
+ getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps));
+ }
+ debug_printf_continue(",freq=%.1F,rxtime=%.0F]\r\n",
+ LMIC.freq, 6,
+ LMIC.rxtime, 0);
+#endif
+ // receive frame at rxtime/now (wait for completion interrupt)
+ radio_startrx(false);
+ // set timeout for rx operation (should not happen, might be updated by radio driver)
+ state.txmode = 0;
+ radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100);
+ break;
+
+ case RADIO_RXON:
+ radio_stop();
+#ifdef DEBUG_RX
+ if( isFsk(LMIC.rps) ) {
+ debug_printf("RXON_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps));
+ } else {
+ ASSERT(isLora(LMIC.rps));
+ debug_printf("RXON_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d",
+ getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125),
+ getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps));
+ }
+ debug_printf_continue(",freq=%.1F]\r\n", LMIC.freq, 6);
#endif
- // set timeout for tx operation (should not happen)
- state.txmode = 1;
- radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100);
- // transmit frame now (wait for completion interrupt)
- radio_starttx(false);
- break;
-
- case RADIO_RX:
- radio_stop();
- // set timeout for rx operation (should not happen, might be updated by radio driver)
- state.txmode = 0;
- radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100);
- // receive frame at rxtime/now (wait for completion interrupt)
- radio_startrx(false);
- break;
-
- case RADIO_RXON:
- radio_stop();
- // start scanning for frame now (wait for completion interrupt)
- state.txmode = 0;
- radio_startrx(true);
- break;
-
- case RADIO_TXCW:
- radio_stop();
- // transmit continuous wave (until abort)
- radio_cw();
- break;
-
- case RADIO_CCA:
- radio_stop();
- // clear channel assessment
- radio_cca();
- break;
-
- case RADIO_INIT:
- // reset and calibrate radio (uses LMIC.freq)
- radio_init(true);
- break;
-
- case RADIO_TXCONT:
- radio_stop();
- radio_starttx(true);
- break;
-
- case RADIO_CAD:
- radio_stop();
- // set timeout for cad/rx operation (should not happen, might be updated by radio driver)
- state.txmode = 0;
- radio_set_irq_timeout(os_getTime() + ms2osticks(10) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100);
- // channel activity detection and rx if preamble symbol found
- radio_cad();
- break;
+ // start scanning for frame now (wait for completion interrupt)
+ state.txmode = 0;
+ radio_startrx(true);
+ break;
+
+ case RADIO_TXCW:
+ radio_stop();
+ // transmit continuous wave (until abort)
+ radio_cw();
+ break;
+
+ case RADIO_CCA:
+ radio_stop();
+ // clear channel assessment
+ radio_cca();
+ break;
+
+ case RADIO_INIT:
+ // reset and calibrate radio (uses LMIC.freq)
+ radio_init(true);
+ break;
+
+ case RADIO_TXCONT:
+ radio_stop();
+ radio_starttx(true);
+ break;
+
+ case RADIO_CAD:
+ radio_stop();
+ // set timeout for cad/rx operation (should not happen, might be updated by radio driver)
+ state.txmode = 0;
+ radio_set_irq_timeout(os_getTime() + ms2osticks(10) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100);
+ // channel activity detection and rx if preamble symbol found
+ radio_cad();
+ break;
}
}
diff --git a/lmic/region.h b/lmic/region.h
index 6dd4f43..7a4bd58 100644
--- a/lmic/region.h
+++ b/lmic/region.h
@@ -6,6 +6,8 @@
#ifndef _region_h_
#define _region_h_
+#include "board.h"
+
// public region codes - DO NOT CHANGE!
enum {
REGCODE_UNDEF = 0,
@@ -138,7 +140,7 @@ enum {
#define MAX_FIX_CHNLS_500 0
#endif
-#define MAX_FIX_CHNLS (MAX_FIX_CHNLS_125 + MAX_FIX_CHNLS_500)
+#define MAX_FIX_CHNLS (MAX_FIX_CHNLS_125 + MAX_FIX_CHNLS_500)
#endif
diff --git a/projects/ex-join/Makefile b/projects/ex-join/Makefile
index 40130ba..769fb4c 100644
--- a/projects/ex-join/Makefile
+++ b/projects/ex-join/Makefile
@@ -20,6 +20,7 @@ SVCS += app
DEFS += -DDEBUG_RX
DEFS += -DDEBUG_TX
+DEFS += -DUSE_IDEETRON_AES
DEFS += -DSVC_FRAG_TEST # enable proprietary test command in frag service
diff --git a/projects/projects.gmk b/projects/projects.gmk
index 1b9f2d7..d4abb45 100644
--- a/projects/projects.gmk
+++ b/projects/projects.gmk
@@ -14,6 +14,7 @@ include $(TOPDIR)/projects/platform.mk
#TARGET ?= $(error "TARGET not specified (See platform.gmk for possible settings)")
LMICDIR := $(TOPDIR)/lmic
+AESDIR := $(TOPDIR)/aes
TOOLSDIR := $(TOPDIR)/tools
COMMONDIR := $(TOPDIR)/common
SVCSDIR := $(TOPDIR)/services
@@ -36,6 +37,7 @@ ASDEFS += $(PDEFS)
CFLAGS += -I$(LMICDIR) -I$(HALDIR)
CFLAGS += -I$(COMMONDIR)
+CFLAGS += -Werror
BUILDTIME := $(shell date -u +'%FT%TZ')
@@ -46,9 +48,9 @@ else
PATCHFLAGS += -v
endif
-SRCS += $(notdir $(wildcard $(LMICDIR)/*.c $(HALDIR)/*.c *.c $(HALDIR)/*.S))
+SRCS += $(notdir $(wildcard $(LMICDIR)/*.c $(AESDIR)/*.c $(HALDIR)/*.c *.c $(HALDIR)/*.S))
-VPATH += $(LMICDIR) $(HALDIR)
+VPATH += $(LMICDIR) $(AESDIR) $(HALDIR)
ifneq ($(COMMON),)
SRCS += $(COMMON)
diff --git a/services/appstart/main.c b/services/appstart/main.c
index ab330f3..2b32f6d 100644
--- a/services/appstart/main.c
+++ b/services/appstart/main.c
@@ -25,11 +25,11 @@ static void initfunc (osjob_t* job) {
fwi.blversion,
PROJECT_STR, VARIANT_STR, fwi.version, fwi.crc,
#if 0
- (BOOT_DEVINFO->bootmode == TABS_BOOT_UFT) ? "uft" :
- (BOOT_DEVINFO->bootmode == TABS_BOOT_SHIPPING) ? "ship" :
- (BOOT_DEVINFO->bootmode == TABS_BOOT_FLIGHT) ? "flight" :
+ (BOOT_DEVINFO->bootmode == TABS_BOOT_UFT) ? "uft" :
+ (BOOT_DEVINFO->bootmode == TABS_BOOT_SHIPPING) ? "ship" :
+ (BOOT_DEVINFO->bootmode == TABS_BOOT_FLIGHT) ? "flight" :
#endif
- "normal");
+ "normal");
#endif
#ifdef SVC_backtrace
diff --git a/services/lwmux/lwmux.c b/services/lwmux/lwmux.c
index 7cb1e0b..a5a4f09 100644
--- a/services/lwmux/lwmux.c
+++ b/services/lwmux/lwmux.c
@@ -9,23 +9,23 @@
DECL_ON_LMIC_EVENT;
enum {
- FLAG_MODESWITCH = (1 << 0), // mode switch pending
- FLAG_BUSY = (1 << 1), // radio is busy
- FLAG_JOINING = (1 << 2), // trying to join
- FLAG_SHUTDOWN = (1 << 3), // shutdown reqested
+ FLAG_MODESWITCH = (1 << 0), // mode switch pending
+ FLAG_BUSY = (1 << 1), // radio is busy
+ FLAG_JOINING = (1 << 2), // trying to join
+ FLAG_SHUTDOWN = (1 << 3), // shutdown reqested
};
static struct {
- unsigned int flags; // flags (FLAG_*)
- unsigned int mode; // current mode (LWM_MODE_*)
- unsigned int nextmode; // next mode (if FLAG_MODESWITCH is set)
+ unsigned int flags; // flags (FLAG_*)
+ unsigned int mode; // current mode (LWM_MODE_*)
+ unsigned int nextmode; // next mode (if FLAG_MODESWITCH is set)
- lwm_job* queue; // job queue head
+ lwm_job* queue; // job queue head
unsigned int runprio; // minimum priority level for runnning jobs
- lwm_complete completefunc; // current job completion function
- osjob_t job; // tx opportunity job
+ lwm_complete completefunc; // current job completion function
+ osjob_t job; // tx opportunity job
- unsigned int jcnt; // join attempt counter
+ unsigned int jcnt; // join attempt counter
struct {
bool use_profile; // Use ADR profile instead of network-managed
@@ -37,21 +37,21 @@ static struct {
#ifdef LWM_SLOTTED
struct {
- ostime_t interval; // beacon interval
- u4_t freq; // beacon frequency
- dr_t dndr; // beacon datarate
+ ostime_t interval; // beacon interval
+ u4_t freq; // beacon frequency
+ dr_t dndr; // beacon datarate
- u1_t slotsz; // slot size (payload length in bytes)
- ostime_t t_slots; // time span available for uplink slots
- ostime_t off_slots; // offset to first slot relative to beacon start
+ u1_t slotsz; // slot size (payload length in bytes)
+ ostime_t t_slots; // time span available for uplink slots
+ ostime_t off_slots; // offset to first slot relative to beacon start
- ostime_t nextrx; // time of next beacon
+ ostime_t nextrx; // time of next beacon
- int missed; // number of missed beacons
- int missed_max; // max. number of missed beacons before going back to scanning
+ int missed; // number of missed beacons
+ int missed_max; // max. number of missed beacons before going back to scanning
- int timeouts; // number of beacon scan timeouts
- int timeouts_max; // max. number of beacon scan timeouts before creating an unaligned TX opportunity
+ int timeouts; // number of beacon scan timeouts
+ int timeouts_max; // max. number of beacon scan timeouts before creating an unaligned TX opportunity
} bcn;
#endif
} state;
@@ -253,13 +253,13 @@ static void reschedule_join (void) {
os_setApproxTimedCallback(&state.job,
os_getTime() + (
#if defined(CFG_eu868) || defined(CFG_in865)
- (state.jcnt < 10) ? sec2osticks(360) : // first hour: every 6 minutes
- (state.jcnt < 20) ? sec2osticks(3600) : // next 10 hours: every hour
- sec2osticks(3600 * 12) // after: every 12 hours
+ (state.jcnt < 10) ? sec2osticks(360) : // first hour: every 6 minutes
+ (state.jcnt < 20) ? sec2osticks(3600) : // next 10 hours: every hour
+ sec2osticks(3600 * 12) // after: every 12 hours
#elif defined(CFG_us915)
- (state.jcnt < 6) ? sec2osticks(600) : // first hour: every 10 minutes
- (state.jcnt < 12) ? sec2osticks(6000) : // next 10 hours: every 100 minutes
- sec2osticks(3600 * 12) // after: every 12 hours
+ (state.jcnt < 6) ? sec2osticks(600) : // first hour: every 10 minutes
+ (state.jcnt < 12) ? sec2osticks(6000) : // next 10 hours: every 100 minutes
+ sec2osticks(3600 * 12) // after: every 12 hours
#else
#warning "Unsupported region"
sec2osticks(3600)
@@ -301,7 +301,7 @@ static bool mode_switch (void) {
#ifdef LWM_SLOTTED
else if (state.nextmode == LWM_MODE_SLOTTED) {
debug_printf("slotted\r\n");
- state.bcn.missed = state.bcn.missed_max; // start with scanning
+ state.bcn.missed = state.bcn.missed_max; // start with scanning
state.bcn.timeouts = 0;
if (!(state.flags & (FLAG_BUSY | FLAG_JOINING))) {
bcn_continue();
@@ -433,7 +433,7 @@ DECL_ON_LMIC_EVENT {
if (e == EV_TXCOMPLETE || e == EV_RXCOMPLETE) {
if ((LMIC.txrxFlags & TXRX_PORT) && LMIC.frame[LMIC.dataBeg-1]) {
SVCHOOK_lwm_downlink(LMIC.frame[LMIC.dataBeg-1],
- LMIC.frame + LMIC.dataBeg, LMIC.dataLen, LMIC.txrxFlags & LWM_FLAG_MASK);
+ LMIC.frame + LMIC.dataBeg, LMIC.dataLen, LMIC.txrxFlags & LWM_FLAG_MASK);
}
}
diff --git a/services/lwtest/testmode.c b/services/lwtest/testmode.c
index a0fdb8c..b2677d0 100644
--- a/services/lwtest/testmode.c
+++ b/services/lwtest/testmode.c
@@ -74,28 +74,28 @@ static void stoptestmode (void) {
static bool txfunc (lwm_txinfo* txi) {
if (testmode.confirmed && (LMIC.txrxFlags & TXRX_NACK) && testmode.retrans < 8) {
- // no ACK received - retransmit last uplink data in LMIC.pendTxData with same seqnoUp
- txi->dlen = LMIC.pendTxLen;
- LMIC.seqnoUp -= 1;
- testmode.retrans += 1;
+ // no ACK received - retransmit last uplink data in LMIC.pendTxData with same seqnoUp
+ txi->dlen = LMIC.pendTxLen;
+ LMIC.seqnoUp -= 1;
+ testmode.retrans += 1;
} else if (LMIC.frame[LMIC.dataBeg] == TESTCMD_ECHO) {
txi->data[0] = TESTCMD_ECHO;
for( int i = 1; i < LMIC.dataLen; i++ ) {
txi->data[i] = LMIC.frame[LMIC.dataBeg + i] + 1;
}
- txi->dlen = LMIC.dataLen;
- testmode.retrans = 0;
+ txi->dlen = LMIC.dataLen;
+ testmode.retrans = 0;
} else {
- txi->data[0] = testmode.dncnt >> 8; // fill in downlink_counter
- txi->data[1] = testmode.dncnt; // (2 bytes, big endian)
- txi->dlen = 2;
- testmode.retrans = 0;
+ txi->data[0] = testmode.dncnt >> 8; // fill in downlink_counter
+ txi->data[1] = testmode.dncnt; // (2 bytes, big endian)
+ txi->dlen = 2;
+ testmode.retrans = 0;
}
txi->port = TESTMODE_PORT;
txi->confirmed = testmode.confirmed;
LMIC_setAdrMode(1);
debug_printf("TESTMODE UPLINK #%d (%sconfirmed, seq=%d, len=%d): %h\r\n",
- testmode.uptotal++, (testmode.confirmed) ? "" : "un", LMIC.seqnoUp, txi->dlen, txi->data, txi->dlen);
+ testmode.uptotal++, (testmode.confirmed) ? "" : "un", LMIC.seqnoUp, txi->dlen, txi->data, txi->dlen);
return true;
}
@@ -113,104 +113,104 @@ static void stopcw (osjob_t* job) {
// referenced by tabs / rm_event()
void testmode_handleEvent (ev_t ev) {
switch (ev) {
- case EV_TXCOMPLETE: {
- // check for downlink
- if (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2)) {
- unsigned char *buf = LMIC.frame + LMIC.dataBeg;
- int port = -1;
- if (LMIC.txrxFlags & TXRX_PORT) {
- port = LMIC.frame[LMIC.dataBeg - 1];
- }
-
- // save timestamp of last downlink
- testmode.dntime = os_getTime();
-
- // reset uplink-without-downlink counter
- testmode.upcnt = 0;
-
- if (testmode.active) {
- debug_printf("TESTMODE DOWNLINK (seq=%d, port=%d, len=%d%s%s%s%s): %h\r\n",
- LMIC.seqnoDn, port, LMIC.dataLen,
- (LMIC.txrxFlags & TXRX_DNW1) ? ", RX1" : "",
- (LMIC.txrxFlags & TXRX_DNW2) ? ", RX2" : "",
- (LMIC.txrxFlags & TXRX_ACK) ? ", ACK" : "",
- (LMIC.txrxFlags & TXRX_NACK) ? ", NACK" : "",
- buf, LMIC.dataLen);
-
- // update downlink counter
+ case EV_TXCOMPLETE: {
+ // check for downlink
+ if (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2)) {
+ unsigned char *buf = LMIC.frame + LMIC.dataBeg;
+ int port = -1;
+ if (LMIC.txrxFlags & TXRX_PORT) {
+ port = LMIC.frame[LMIC.dataBeg - 1];
+ }
+
+ // save timestamp of last downlink
+ testmode.dntime = os_getTime();
+
+ // reset uplink-without-downlink counter
+ testmode.upcnt = 0;
+
+ if (testmode.active) {
+ debug_printf("TESTMODE DOWNLINK (seq=%d, port=%d, len=%d%s%s%s%s): %h\r\n",
+ LMIC.seqnoDn, port, LMIC.dataLen,
+ (LMIC.txrxFlags & TXRX_DNW1) ? ", RX1" : "",
+ (LMIC.txrxFlags & TXRX_DNW2) ? ", RX2" : "",
+ (LMIC.txrxFlags & TXRX_ACK) ? ", ACK" : "",
+ (LMIC.txrxFlags & TXRX_NACK) ? ", NACK" : "",
+ buf, LMIC.dataLen);
+
+ // update downlink counter
if( testmode.lastdf != LMIC.seqnoDn) {
testmode.lastdf = LMIC.seqnoDn;
testmode.dncnt += 1;
}
- if (port == TESTMODE_PORT && LMIC.dataLen > 0) {
-
- // dispatch test commands
- switch (buf[0]) {
-
- case TESTCMD_STOP: // deactivate test mode
- stoptestmode();
- break;
-
- case TESTCMD_CONFIRMED: // activate confirmations
- testmode.confirmed = 1;
- break;
-
- case TESTCMD_UNCONFIRMED: // deactivate confirmations
- testmode.confirmed = 0;
- break;
-
- case TESTCMD_LINKCHECK: // XXX undocumented?!?
- // XXX
- break;
-
- case TESTCMD_JOIN: // trigger join request
- stoptestmode(); // (activation command will be resent after join)
- lwm_setmode(LWM_MODE_SHUTDOWN);
- lwm_setmode(LWM_MODE_NORMAL);
- break;
-
- case TESTCMD_ECHO: // modify and echo frame
- LMIC.pendTxData[0] = buf[0];
- for (int i = 1; i < LMIC.dataLen; i++) {
- LMIC.pendTxData[i] = buf[i] + 1;
- }
- LMIC.pendTxLen = LMIC.dataLen;
- break;
-
- case TESTCMD_CW: // continous wave
- // set timeout and parameters
- os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks((buf[1] << 8) | buf[2]), stopcw); // duration [s]
- LMIC.freq = ((buf[3] << 16) | (buf[4] << 8) | buf[5]) * 100; // [Hz]
- LMIC.txpow = buf[6]; // dBm
- // start continuous wave
- os_radio(RADIO_TXCW);
- return; // no uplink now
- }
- }
- } else { // test mode not active
- if (port == TESTMODE_PORT && LMIC.dataLen == 4 && os_rlsbf4(buf) == 0x01010101) {
- // activate test mode
- starttestmode();
- }
- }
- } else { // no downlink
- if (testmode.active &&
- (++testmode.upcnt > TESTMODE_MAXCNT ||
- (os_getTime() - testmode.dntime) > sec2osticks(TESTMODE_TIMEOUT))) {
- // test mode timed out
- debug_printf("TEST MODE TIMEOUT\r\n");
- stoptestmode();
- }
- }
-
- if (testmode.active) {
- // schedule next uplink
- os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks(TESTMODE_INTERVAL), uplink);
- }
- }
-
- default: // ignore other events
- break;
+ if (port == TESTMODE_PORT && LMIC.dataLen > 0) {
+
+ // dispatch test commands
+ switch (buf[0]) {
+
+ case TESTCMD_STOP: // deactivate test mode
+ stoptestmode();
+ break;
+
+ case TESTCMD_CONFIRMED: // activate confirmations
+ testmode.confirmed = 1;
+ break;
+
+ case TESTCMD_UNCONFIRMED: // deactivate confirmations
+ testmode.confirmed = 0;
+ break;
+
+ case TESTCMD_LINKCHECK: // XXX undocumented?!?
+ // XXX
+ break;
+
+ case TESTCMD_JOIN: // trigger join request
+ stoptestmode(); // (activation command will be resent after join)
+ lwm_setmode(LWM_MODE_SHUTDOWN);
+ lwm_setmode(LWM_MODE_NORMAL);
+ break;
+
+ case TESTCMD_ECHO: // modify and echo frame
+ LMIC.pendTxData[0] = buf[0];
+ for (int i = 1; i < LMIC.dataLen; i++) {
+ LMIC.pendTxData[i] = buf[i] + 1;
+ }
+ LMIC.pendTxLen = LMIC.dataLen;
+ break;
+
+ case TESTCMD_CW: // continous wave
+ // set timeout and parameters
+ os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks((buf[1] << 8) | buf[2]), stopcw); // duration [s]
+ LMIC.freq = ((buf[3] << 16) | (buf[4] << 8) | buf[5]) * 100; // [Hz]
+ LMIC.txpow = buf[6]; // dBm
+ // start continuous wave
+ os_radio(RADIO_TXCW);
+ return; // no uplink now
+ }
+ }
+ } else { // test mode not active
+ if (port == TESTMODE_PORT && LMIC.dataLen == 4 && os_rlsbf4(buf) == 0x01010101) {
+ // activate test mode
+ starttestmode();
+ }
+ }
+ } else { // no downlink
+ if (testmode.active &&
+ (++testmode.upcnt > TESTMODE_MAXCNT ||
+ (os_getTime() - testmode.dntime) > sec2osticks(TESTMODE_TIMEOUT))) {
+ // test mode timed out
+ debug_printf("TEST MODE TIMEOUT\r\n");
+ stoptestmode();
+ }
+ }
+
+ if (testmode.active) {
+ // schedule next uplink
+ os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks(TESTMODE_INTERVAL), uplink);
+ }
+ }
+
+ default: // ignore other events
+ break;
}
}
diff --git a/services/pwrman/pwrman.c b/services/pwrman/pwrman.c
index b6ab0e0..921a8f2 100644
--- a/services/pwrman/pwrman.c
+++ b/services/pwrman/pwrman.c
@@ -48,7 +48,7 @@ static void add (ptime* ppt, uint64_t uaticks) {
uint32_t uah = uaticks / (OSTICKS_PER_SEC * 60 * 60);
if( uah != 0 ) {
ppt->hr += uah;
- uaticks -= ((int64_t) uah * (OSTICKS_PER_SEC * 60 * 60));
+ uaticks -= ((int64_t) uah * (OSTICKS_PER_SEC * 60 * 60));
}
ppt->ticks = uaticks;
}
diff --git a/stm32/eeprom.c b/stm32/eeprom.c
index 9b14aec..badbcde 100644
--- a/stm32/eeprom.c
+++ b/stm32/eeprom.c
@@ -32,7 +32,7 @@ void eeprom_write (void* dest, unsigned int val) {
FLASH->PECR |= FLASH_PECR_PELOCK;
// verify value
- ASSERT( *((volatile u4_t*) addr) == val );
+ ASSERT( *((volatile u4_t*) addr) == val );
}
}
diff --git a/stm32/gpio.c b/stm32/gpio.c
index 48b8f94..3a15372 100644
--- a/stm32/gpio.c
+++ b/stm32/gpio.c
@@ -80,10 +80,10 @@ void gpio_cfg_extirq_ex (int port, int pin, bool rising, bool falling) {
EXTI->RTSR &= ~mask; // clear trigger
EXTI->FTSR &= ~mask; // clear trigger
if( rising ) {
- EXTI->RTSR |= mask;
+ EXTI->RTSR |= mask;
}
if( falling ) {
- EXTI->FTSR |= mask;
+ EXTI->FTSR |= mask;
}
// configure the NVIC
@@ -104,10 +104,10 @@ void gpio_cfg_extirq (int port, int pin, int irqcfg) {
void gpio_set_extirq (int pin, int on) {
if (on) {
- EXTI->PR = (1 << pin);
- EXTI->IMR |= (1 << pin);
+ EXTI->PR = (1 << pin);
+ EXTI->IMR |= (1 << pin);
} else {
- EXTI->IMR &= ~(1 << pin);
+ EXTI->IMR &= ~(1 << pin);
}
}
diff --git a/stm32/hal.c b/stm32/hal.c
index dd78e51..d92b2bc 100644
--- a/stm32/hal.c
+++ b/stm32/hal.c
@@ -589,7 +589,7 @@ static u4_t sleep0 (u4_t hticks, u4_t htt, u4_t ltt) {
}
static void sleep (int stype, u4_t htt, u4_t ltt) {
- static const u4_t(*sleepfuncs[])(u4_t,u4_t,u4_t) = {
+ static u4_t(* const sleepfuncs[])(u4_t,u4_t,u4_t) = {
sleep0,
sleep1,
sleep2,
@@ -744,6 +744,7 @@ static void hal_io_init () {
#endif
}
+#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
bool hal_pin_tcxo (u1_t val) {
#if defined(GPIO_TCXO_PWR)
if (val != 0) {
@@ -758,6 +759,7 @@ bool hal_pin_tcxo (u1_t val) {
return false;
#endif
}
+#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
void hal_ant_switch (u1_t val) {
#ifdef SVC_pwrman
@@ -796,13 +798,18 @@ void hal_ant_switch (u1_t val) {
}
// set radio RST pin to given value (or keep floating!)
-void hal_pin_rst (u1_t val) {
+bool hal_pin_rst (u1_t val) {
+ #ifdef GPIO_RST
if(val == 0 || val == 1) { // drive pin
SET_PIN(GPIO_RST, val);
CFG_PIN(GPIO_RST, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE);
} else { // keep pin floating
CFG_PIN(GPIO_RST, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE);
}
+ return true;
+ #else
+ return false;
+ #endif
}
void hal_pin_busy_wait (void) {
@@ -815,6 +822,24 @@ void hal_pin_busy_wait (void) {
#endif
}
+#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+bool hal_dio3_controls_tcxo (void) {
+ #if defined(LMIC_DIO3_CONTROLS_TCXO)
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool hal_dio2_controls_rxtx (void) {
+ #if defined(LMIC_DIO2_CONTROLS_RXTX)
+ return true;
+ #else
+ return false;
+ #endif
+}
+#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+
#define DIO_UPDATE(dio,mask,time) do { \
if( (EXTI->PR & (1 << BRD_PIN(GPIO_DIO ## dio))) ) { \
EXTI->PR = (1 << BRD_PIN(GPIO_DIO ## dio)); \
diff --git a/stm32/hal_stm32.h b/stm32/hal_stm32.h
index 1e34c10..dee2985 100644
--- a/stm32/hal_stm32.h
+++ b/stm32/hal_stm32.h
@@ -44,8 +44,8 @@ void hal_rtstats_collect (hal_rtstats* stats);
// NVIC interrupt definition
typedef struct {
- uint32_t num; // NVIC interrupt number
- void* handler; // Pointer to handler function
+ uint32_t num; // NVIC interrupt number
+ void* handler; // Pointer to handler function
} irqdef;
extern const irqdef HAL_irqdefs[];
diff --git a/stm32/hw.h b/stm32/hw.h
index 9e835fa..60f0f50 100644
--- a/stm32/hw.h
+++ b/stm32/hw.h
@@ -80,63 +80,63 @@ enum { GPIO_LOHI = 0, GPIO_HILO = 1 };
// generate a transition, then switch to HiZ
int gpio_transition (int port, int pin, int type, int duration, unsigned int config);
-#define GPIO_DEFAULT_CFG (GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE)
-#define SET_PIN(gpio, val) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (val))
-#define GET_PIN(gpio) gpio_get_pin(BRD_PORT(gpio), BRD_PIN(gpio))
-#define CFG_PIN(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), (opts))
-#define CFG_PIN_VAL(gpio, o, s) gpio_cfg_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (o), (s))
-#define CFG_PIN_AF(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIOCFG_MODE_ALT | BRD_AF(gpio) | (opts))
-#define CFG_PIN_DEFAULT(gpio) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIO_DEFAULT_CFG)
-#define IRQ_PIN(gpio, opts) gpio_cfg_extirq(BRD_PORT(gpio), BRD_PIN(gpio), (opts))
-#define IRQ_PIN_SET(gpio, on) gpio_set_extirq(BRD_PIN(gpio), (on))
-#define TXN_PIN(gpio, t, d, c) gpio_transition(BRD_PORT(gpio), BRD_PIN(gpio), (t), (d), (c))
+#define GPIO_DEFAULT_CFG (GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE)
+#define SET_PIN(gpio, val) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (val))
+#define GET_PIN(gpio) gpio_get_pin(BRD_PORT(gpio), BRD_PIN(gpio))
+#define CFG_PIN(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), (opts))
+#define CFG_PIN_VAL(gpio, o, s) gpio_cfg_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (o), (s))
+#define CFG_PIN_AF(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIOCFG_MODE_ALT | BRD_AF(gpio) | (opts))
+#define CFG_PIN_DEFAULT(gpio) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIO_DEFAULT_CFG)
+#define IRQ_PIN(gpio, opts) gpio_cfg_extirq(BRD_PORT(gpio), BRD_PIN(gpio), (opts))
+#define IRQ_PIN_SET(gpio, on) gpio_set_extirq(BRD_PIN(gpio), (on))
+#define TXN_PIN(gpio, t, d, c) gpio_transition(BRD_PORT(gpio), BRD_PIN(gpio), (t), (d), (c))
#define SET_PIN_ONOFF(gpio, on) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (((gpio) & BRD_GPIO_ACTIVE_LOW) ? 1 : 0) ^ ((on) ? 1 : 0))
// Convenience macros to set GPIO configuration registers
-#define GPIO_AF_BITS 4 // width of bit field
-#define GPIO_AF_MASK 0x0F // mask in AFR[0/1]
-#define GPIO_AFRLR(i) ((i) >> 3)
-#define GPIO_AF_PINi(i,af) ((af) << (((i) & 7) * GPIO_AF_BITS))
-#define GPIO_AF_set(p,i,af) do { \
+#define GPIO_AF_BITS 4 // width of bit field
+#define GPIO_AF_MASK 0x0F // mask in AFR[0/1]
+#define GPIO_AFRLR(i) ((i) >> 3)
+#define GPIO_AF_PINi(i,af) ((af) << (((i) & 7) * GPIO_AF_BITS))
+#define GPIO_AF_set(p,i,af) do { \
(p)->AFR[GPIO_AFRLR(i)] = ((p)->AFR[GPIO_AFRLR(i)] \
- & ~GPIO_AF_PINi(i, GPIO_AF_MASK)) | GPIO_AF_PINi(i, af); \
+ & ~GPIO_AF_PINi(i, GPIO_AF_MASK)) | GPIO_AF_PINi(i, af); \
} while (0)
-#define HW_CFG_PIN(p,i,cfg) do { \
+#define HW_CFG_PIN(p,i,cfg) do { \
if (((cfg) & GPIOCFG_MODE_MASK) == GPIOCFG_MODE_ALT) { \
- GPIO_AF_set(p, i, (cfg) & GPIOCFG_AF_MASK); \
- (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \
+ GPIO_AF_set(p, i, (cfg) & GPIOCFG_AF_MASK); \
+ (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \
} \
(p)->OSPEEDR = ((p)->OSPEEDR & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_OSPEED_SHIFT) & 3) << (2*(i))); \
(p)->OTYPER = ((p)->OTYPER & ~(1 << (1*(i)))) | ((((cfg) >> GPIOCFG_OTYPE_SHIFT ) & 1) << (1*(i))); \
(p)->PUPDR = ((p)->PUPDR & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_PUPD_SHIFT ) & 3) << (2*(i))); \
if (((cfg) & GPIOCFG_MODE_MASK) != GPIOCFG_MODE_ALT) { \
- (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \
+ (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \
} \
} while (0)
-#define HW_SET_PIN(p,i,state) do { \
+#define HW_SET_PIN(p,i,state) do { \
(p)->BSRR |= (1 << (i + ((state) ? 0 : 16))); \
} while (0)
-#define HW_GET_PIN(p,i) ((p)->IDR & (1 << (i)))
+#define HW_GET_PIN(p,i) ((p)->IDR & (1 << (i)))
// Theses macros manipulate the GPIO registers directly, without generating a function call
-#define SET_PIN_DIRECT(gpio, val) HW_SET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (val))
-#define GET_PIN_DIRECT(gpio) HW_GET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio))
-#define CFG_PIN_DIRECT(gpio, opts) HW_CFG_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (opts))
+#define SET_PIN_DIRECT(gpio, val) HW_SET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (val))
+#define GET_PIN_DIRECT(gpio) HW_GET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio))
+#define CFG_PIN_DIRECT(gpio, opts) HW_CFG_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (opts))
// Determine RCC enable bit for GPIO port
#if defined(STM32L0)
-#define GPIO_RCC_ENR (RCC->IOPENR)
-#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_IOPENR_GPIOAEN \
- : ((gpio) == PORT_B) ? RCC_IOPENR_GPIOBEN \
- : ((gpio) == PORT_C) ? RCC_IOPENR_GPIOCEN \
- : 0)
+#define GPIO_RCC_ENR (RCC->IOPENR)
+#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_IOPENR_GPIOAEN \
+ : ((gpio) == PORT_B) ? RCC_IOPENR_GPIOBEN \
+ : ((gpio) == PORT_C) ? RCC_IOPENR_GPIOCEN \
+ : 0)
#elif defined(STM32L1)
-#define GPIO_RCC_ENR (RCC->AHBENR)
-#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_AHBENR_GPIOAEN \
- : ((gpio) == PORT_B) ? RCC_AHBENR_GPIOBEN \
- : ((gpio) == PORT_C) ? RCC_AHBENR_GPIOCEN \
- : 0)
+#define GPIO_RCC_ENR (RCC->AHBENR)
+#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_AHBENR_GPIOAEN \
+ : ((gpio) == PORT_B) ? RCC_AHBENR_GPIOBEN \
+ : ((gpio) == PORT_C) ? RCC_AHBENR_GPIOCEN \
+ : 0)
#endif
typedef struct {
@@ -163,13 +163,13 @@ typedef struct {
#define UNIQUE_ID2 (UNIQUE_ID_BASE+0x14)
// EEPROM (these macros are addresses, type uint32_t)
-#define EEPROM_BASE DATA_EEPROM_BASE
+#define EEPROM_BASE DATA_EEPROM_BASE
#ifdef DATA_EEPROM_BANK2_END
-#define EEPROM_END (DATA_EEPROM_BANK2_END + 1)
+#define EEPROM_END (DATA_EEPROM_BANK2_END + 1)
#else
-#define EEPROM_END DATA_EEPROM_END
+#define EEPROM_END DATA_EEPROM_END
#endif
-#define EEPROM_SZ (EEPROM_END - EEPROM_BASE)
+#define EEPROM_SZ (EEPROM_END - EEPROM_BASE)
// EEPROM layout for STM32
@@ -288,8 +288,8 @@ u1_t spi_xfer (u1_t out);
#define PERIPH_FLASH
-#define FLASH_PAGE_SZ 128
-#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2)
+#define FLASH_PAGE_SZ 128
+#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2)
#ifdef FLASH_END
#undef FLASH_END // already defined by some STM32L0XXXxx header files
diff --git a/stm32/i2c.c b/stm32/i2c.c
index 077a3df..2b7319c 100644
--- a/stm32/i2c.c
+++ b/stm32/i2c.c
@@ -8,10 +8,10 @@
#ifdef BRD_I2C
#if BRD_I2C == 1
-#define I2Cx I2C1
-#define I2Cx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; } while (0)
-#define I2Cx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN; } while (0)
-#define I2Cx_IRQn I2C1_IRQn
+#define I2Cx I2C1
+#define I2Cx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; } while (0)
+#define I2Cx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN; } while (0)
+#define I2Cx_IRQn I2C1_IRQn
#else
#error "Unsupported I2C peripheral"
#endif
@@ -47,9 +47,9 @@ static void i2c_stop (int status) {
// schedule callback
*(xfr.pstatus) = status;
if (xfr.job != NULL) {
- os_setCallback(xfr.job, xfr.cb);
+ os_setCallback(xfr.job, xfr.cb);
} else {
- xfr.cb(NULL);
+ xfr.cb(NULL);
}
// re-enable sleep
hal_clearMaxSleep(HAL_SLEEP_S0);
@@ -75,47 +75,47 @@ static void i2c_start (int addr) {
static void i2c_cont (void) {
if (xfr.wlen) {
- // calculate length; TODO: handle >255
- int n = xfr.wlen & 0xff;
- xfr.wlen -= n;
- // set direction & number of bytes
- I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | (n << 16);
- // enable interrupts
- I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_TXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE;
- // start TX
- I2Cx->CR2 |= I2C_CR2_START;
+ // calculate length; TODO: handle >255
+ int n = xfr.wlen & 0xff;
+ xfr.wlen -= n;
+ // set direction & number of bytes
+ I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | (n << 16);
+ // enable interrupts
+ I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_TXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE;
+ // start TX
+ I2Cx->CR2 |= I2C_CR2_START;
} else if (xfr.rlen) {
- // calculate length; TODO: handle >255
- int n = xfr.rlen & 0xff;
- xfr.rlen -= n;
- // set direction & number of bytes
- I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | I2C_CR2_RD_WRN | (n << 16);
- // enable interrupts
- I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_RXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE;
- // start RX
- I2Cx->CR2 |= I2C_CR2_START;
+ // calculate length; TODO: handle >255
+ int n = xfr.rlen & 0xff;
+ xfr.rlen -= n;
+ // set direction & number of bytes
+ I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | I2C_CR2_RD_WRN | (n << 16);
+ // enable interrupts
+ I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_RXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE;
+ // start RX
+ I2Cx->CR2 |= I2C_CR2_START;
} else {
- // done
- i2c_stop(I2C_OK);
+ // done
+ i2c_stop(I2C_OK);
}
}
void i2c_irq (void) {
unsigned int isr = I2Cx->ISR;
if (isr & I2C_ISR_NACKF) {
- // NACK detected, transfer failed!
- i2c_stop(I2C_NAK);
+ // NACK detected, transfer failed!
+ i2c_stop(I2C_NAK);
} else if (isr & I2C_ISR_TC) {
- // transfer complete, move on
- i2c_cont();
+ // transfer complete, move on
+ i2c_cont();
} else if (isr & I2C_ISR_TXIS) {
- // write next byte
- I2Cx->TXDR = *xfr.wptr++;
+ // write next byte
+ I2Cx->TXDR = *xfr.wptr++;
} else if (isr & I2C_ISR_RXNE) {
- // next byte received
- *xfr.rptr++ = I2Cx->RXDR;
+ // next byte received
+ *xfr.rptr++ = I2Cx->RXDR;
} else {
- hal_failed(); // XXX
+ hal_failed(); // XXX
}
}
@@ -124,7 +124,7 @@ static void i2c_timeout (osjob_t* job) {
}
void i2c_xfer_ex (unsigned int addr, unsigned char* buf, unsigned int wlen, unsigned int rlen, ostime_t timeout,
- osjob_t* job, osjobcb_t cb, int* pstatus) {
+ osjob_t* job, osjobcb_t cb, int* pstatus) {
// setup xfr structure
xfr.wlen = wlen;
xfr.rlen = rlen;
@@ -135,7 +135,7 @@ void i2c_xfer_ex (unsigned int addr, unsigned char* buf, unsigned int wlen, unsi
*xfr.pstatus = I2C_BUSY;
// set timeout
if (timeout) {
- os_setTimedCallback(job, os_getTime() + timeout, i2c_timeout);
+ os_setTimedCallback(job, os_getTime() + timeout, i2c_timeout);
}
// prepare peripheral
i2c_start(addr);
diff --git a/stm32/leds.c b/stm32/leds.c
index 2c9b43e..b3f8e6d 100644
--- a/stm32/leds.c
+++ b/stm32/leds.c
@@ -8,10 +8,10 @@
#if defined(BRD_LED_TIM)
#if BRD_LED_TIM == 2
-#define TIMx TIM2
-#define TIMx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } while (0)
-#define TIMx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; } while (0)
-#define TIMx_IRQn TIM2_IRQn
+#define TIMx TIM2
+#define TIMx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } while (0)
+#define TIMx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; } while (0)
+#define TIMx_IRQn TIM2_IRQn
#else
#error "Unsupported timer"
#endif
@@ -19,11 +19,11 @@
static struct {
unsigned int state;
struct {
- int step;
- unsigned int n;
- unsigned int delay;
- unsigned int min;
- unsigned int max;
+ int step;
+ unsigned int n;
+ unsigned int delay;
+ unsigned int min;
+ unsigned int max;
} pulse[4];
} pwm;
@@ -35,11 +35,11 @@ void leds_init (void) {
TIMx->PSC = 4;
TIMx->ARR = 0xffff;
TIMx->CCMR1 =
- TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE |
- TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
+ TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE |
+ TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
TIMx->CCMR2 =
- TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE |
- TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
+ TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE |
+ TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
TIMx_disable();
#endif
}
@@ -53,56 +53,56 @@ static void pwm_set_gpio (unsigned int gpio, bool enable, bool pulse, unsigned i
unsigned int state1 = state0;
if (enable) {
- state1 |= (0x01 << ch);
- if (pulse) {
- state1 |= (0x10 << ch);
- }
+ state1 |= (0x01 << ch);
+ if (pulse) {
+ state1 |= (0x10 << ch);
+ }
} else {
- state1 &= ~(0x11 << ch);
+ state1 &= ~(0x11 << ch);
}
if (state0 == state1) {
- return;
+ return;
}
hal_disableIRQs();
if (state1) {
- if (state0 == 0) {
- TIMx_enable(); // enable peripheral clock
+ if (state0 == 0) {
+ TIMx_enable(); // enable peripheral clock
hal_setMaxSleep(HAL_SLEEP_S0); // disable sleep (keep clock at full speed)
- TIMx->CR1 |= TIM_CR1_CEN; // enable timer peripheral
- TIMx->EGR |= TIM_EGR_UG; // start pwm
- }
- if (state1 & 0xf0) {
- if ((state0 & 0xf0) == 0) {
- TIMx->DIER |= TIM_DIER_UIE; // enable update interrupt
- NVIC_EnableIRQ(TIMx_IRQn); // enable interrupt in NVIC
- }
- } else {
- if ((state0 & 0xf0) == 0) {
- TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt
- NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC
- }
- }
+ TIMx->CR1 |= TIM_CR1_CEN; // enable timer peripheral
+ TIMx->EGR |= TIM_EGR_UG; // start pwm
+ }
+ if (state1 & 0xf0) {
+ if ((state0 & 0xf0) == 0) {
+ TIMx->DIER |= TIM_DIER_UIE; // enable update interrupt
+ NVIC_EnableIRQ(TIMx_IRQn); // enable interrupt in NVIC
+ }
+ } else {
+ if ((state0 & 0xf0) == 0) {
+ TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt
+ NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC
+ }
+ }
} else if (state0) {
- TIMx->CR1 &= ~TIM_CR1_CEN; // disable timer
- TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt
- TIMx_disable(); // disable peripheral clock
+ TIMx->CR1 &= ~TIM_CR1_CEN; // disable timer
+ TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt
+ TIMx_disable(); // disable peripheral clock
hal_clearMaxSleep(HAL_SLEEP_S0); // re-enable sleep
- NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC
+ NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC
}
if (enable) {
- *((&(TIMx->CCR1)) + ch) = ccr; // set initial CCR value
- if ((state0 & (1 << ch)) == 0) {
- unsigned int ccer = TIM_CCER_CC1E;
- if (gpio & BRD_GPIO_ACTIVE_LOW) {
- ccer |= TIM_CCER_CC1P;
- }
- TIMx->CCER |= (ccer << (4 * ch)); // enable channel
- CFG_PIN_AF(gpio, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE);
- }
+ *((&(TIMx->CCR1)) + ch) = ccr; // set initial CCR value
+ if ((state0 & (1 << ch)) == 0) {
+ unsigned int ccer = TIM_CCER_CC1E;
+ if (gpio & BRD_GPIO_ACTIVE_LOW) {
+ ccer |= TIM_CCER_CC1P;
+ }
+ TIMx->CCER |= (ccer << (4 * ch)); // enable channel
+ CFG_PIN_AF(gpio, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE);
+ }
}
pwm.state = state1;
@@ -131,14 +131,14 @@ void leds_pulse (unsigned int gpio, unsigned int min, unsigned int max, int step
void leds_set (unsigned int gpio, int state) {
if (state) {
- if (gpio & BRD_GPIO_ACTIVE_LOW) {
- SET_PIN(gpio, 0);
- } else {
- SET_PIN(gpio, 1);
- }
- CFG_PIN(gpio, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE);
+ if (gpio & BRD_GPIO_ACTIVE_LOW) {
+ SET_PIN(gpio, 0);
+ } else {
+ SET_PIN(gpio, 1);
+ }
+ CFG_PIN(gpio, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE);
} else {
- CFG_PIN(gpio, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE);
+ CFG_PIN(gpio, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE);
}
#if defined(BRD_LED_TIM)
pwm_set_gpio(gpio, false, false, 0);
@@ -148,29 +148,29 @@ void leds_set (unsigned int gpio, int state) {
#if defined(BRD_LED_TIM)
void leds_pwm_irq (void) {
if (TIMx->SR & TIM_SR_UIF) { // update event
- TIMx->SR = ~TIM_SR_UIF; // clear flag
- unsigned int ps = pwm.state & 0x0f;
- while (ps) {
- unsigned int ch = __builtin_ctz(ps);
- if (pwm.pulse[ch].step) {
- if (pwm.pulse[ch].n < pwm.pulse[ch].delay) {
- pwm.pulse[ch].n += 1;
- } else {
- pwm.pulse[ch].n = 0;
- int ccr = *((&(TIMx->CCR1)) + ch);
- ccr += pwm.pulse[ch].step;
- if (ccr <= pwm.pulse[ch].min) {
- ccr = pwm.pulse[ch].min;
- pwm.pulse[ch].step = -pwm.pulse[ch].step;
- } else if (ccr >= pwm.pulse[ch].max) {
- ccr = pwm.pulse[ch].max;
- pwm.pulse[ch].step = -pwm.pulse[ch].step;
- }
- *((&(TIMx->CCR1)) + ch) = ccr;
- }
- }
- ps &= ~(1 << ch);
- }
+ TIMx->SR = ~TIM_SR_UIF; // clear flag
+ unsigned int ps = pwm.state & 0x0f;
+ while (ps) {
+ unsigned int ch = __builtin_ctz(ps);
+ if (pwm.pulse[ch].step) {
+ if (pwm.pulse[ch].n < pwm.pulse[ch].delay) {
+ pwm.pulse[ch].n += 1;
+ } else {
+ pwm.pulse[ch].n = 0;
+ int ccr = *((&(TIMx->CCR1)) + ch);
+ ccr += pwm.pulse[ch].step;
+ if (ccr <= pwm.pulse[ch].min) {
+ ccr = pwm.pulse[ch].min;
+ pwm.pulse[ch].step = -pwm.pulse[ch].step;
+ } else if (ccr >= pwm.pulse[ch].max) {
+ ccr = pwm.pulse[ch].max;
+ pwm.pulse[ch].step = -pwm.pulse[ch].step;
+ }
+ *((&(TIMx->CCR1)) + ch) = ccr;
+ }
+ }
+ ps &= ~(1 << ch);
+ }
}
}
#endif
diff --git a/stm32/startup.c b/stm32/startup.c
index f54a51c..49b777f 100644
--- a/stm32/startup.c
+++ b/stm32/startup.c
@@ -25,20 +25,20 @@ void _start (boot_boottab* boottab) {
uint32_t* src = &_sidata;
uint32_t* dst = &_sdata;
while( dst < &_edata ) {
- *dst++ = *src++;
+ *dst++ = *src++;
}
// initialize bss
dst = &_sbss;
while( dst < &_ebss ) {
- *dst++ = 0;
+ *dst++ = 0;
}
// copy current Cortex M IRQ + NVIC vector to RAM
src = (uint32_t*) 0;
dst = irqvector;
for( int i = 0; i < (16 + MAX_IRQn); i++ ) {
- *dst++ = *src++;
+ *dst++ = *src++;
}
// fix-up NVIC vector with handlers from firmware
for( const irqdef* id = HAL_irqdefs; id->handler; id++ ) {
diff --git a/target/arduino/basicmac.h b/target/arduino/basicmac.h
new file mode 100644
index 0000000..9dbf0dc
--- /dev/null
+++ b/target/arduino/basicmac.h
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *******************************************************************************/
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "lmic/lmic.h"
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/target/arduino/board.h b/target/arduino/board.h
new file mode 100644
index 0000000..2a7505e
--- /dev/null
+++ b/target/arduino/board.h
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *******************************************************************************/
+
+#include "../hal/target-config.h"
diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino
new file mode 100644
index 0000000..5fa2e87
--- /dev/null
+++ b/target/arduino/examples-common-files/standard-pinmaps.ino
@@ -0,0 +1,151 @@
+// This file defines pinmaps, which tell basicmac what pins to use to
+// talk to the radio.
+//
+// Below, a lot of different pinmaps for various standard boards are
+// defined. These are used when USE_STANDARD_PINMAP is defined,
+// otherwise the main sketch file should define its own pinmap.
+//
+// These pinmaps live in their own file, to make it easier to share them
+// between example sketches.
+
+#if defined(USE_STANDARD_PINMAP)
+
+#if defined(BASICMAC_DUMMY_PINMAP)
+// Dummy minimal pinmap, just used for CI compile-testing.
+const lmic_pinmap lmic_pins = {
+ .nss = 10,
+ // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ // RST is hardwired to MCU reset
+ .rst = LMIC_UNUSED_PIN,
+ .dio = {1, 2, 3},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(ARDUINO_AVR_MINI)
+#if !defined(BRD_sx1276_radio)
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+// Assume this is a Nexus board
+// https://github.com/Ideetron/RFM95W_Nexus/blob/master/RFM95W_NEXUS_02.pdf
+// Note: BasicMAC is currently too big to fit into the 328p on this board.
+const lmic_pinmap lmic_pins = {
+ .nss = 10,
+ // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ // RST is hardwarid to MCU reset
+ .rst = LMIC_UNUSED_PIN,
+ .dio = {4, 5, 7},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(ARDUINO_MJS_V1)
+#if !defined(BRD_sx1276_radio)
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+// https://github.com/meetjestad/mjs_pcb
+// Note: BasicMAC is currently too big to fit into the 328p on this board.
+const lmic_pinmap lmic_pins = {
+ .nss = 10,
+ // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ .rst = 9,
+ .dio = {2, 3, 4},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(ADAFRUIT_FEATHER_M0)
+#if !defined(BRD_sx1276_radio)
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+// Assume this a Feather M0 LoRa
+// https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/pinouts
+const lmic_pinmap lmic_pins = {
+ .nss = 8,
+ // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ .rst = 4,
+ // This needs additional external connections: DIO1 (labeled IO1 in
+ // docs and board) to 5 and DIO2 (labeled D2 on the board and IO1 in
+ // the docs) to 6. DIO0 is internally connected.
+ .dio = {3, 5, 6},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(ARDUINO_STM32L4_LS200)
+#if !defined(BRD_sx1262_radio)
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+// LacunaSpace LS200 development board
+// Uses SPI bus at PC10/11/12
+// This uses E22_* constants from the board variant file
+const lmic_pinmap lmic_pins = {
+ .nss = E22_NSS, // PD2
+ // DIO2 connected to TXEN on LS200 board
+ .tx = LMIC_CONTROLLED_BY_DIO2,
+ .rx = E22_RXEN, // PC4
+ .rst = E22_NRST, // PA4
+ .dio = {LMIC_UNUSED_PIN, E22_DIO1 /* PC7 */, LMIC_UNUSED_PIN},
+ .busy = E22_BUSY, // PB12
+ // DIO3 connected to TCXO on E22 board
+ .tcxo = LMIC_CONTROLLED_BY_DIO3,
+};
+#elif defined(ARDUINO_TTGO_LoRa32_V1)
+#if !defined(BRD_sx1276_radio)
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+// https://github.com/LilyGO/TTGO-LORA32/tree/LilyGO-868-V1.0
+// Pinout: https://github.com/LilyGO/TTGO-LORA32/blob/LilyGO-868-V1.0/images/image1.jpg
+const lmic_pinmap lmic_pins = {
+ .nss = 18,
+ // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ .rst = 14,
+ .dio = {26, 33, 32},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(ARDUINO_TBeam)
+// TTGO T-Beam development board
+// https://github.com/LilyGO/TTGO-T-Beam
+// This board is available with an SX1276 or SX1262 soldered on. The board is
+// otherwise identical and does not have a separate board entry in the
+// Arduino IDE, so decide based on the radio constant which one to use.
+//
+// Uses LoRa SPI bus at 5/19/27
+// This uses LORA_* constants from the board variant file
+#if defined(BRD_sx1276_radio)
+const lmic_pinmap lmic_pins = {
+ .nss = LORA_CS, // 18
+ // RX/TX is controlled through RXTX by the SX1276/8 directly on the HPD13/4A
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ .rst = LORA_RST, // 23
+ .dio = {LORA_IO0 /* 26 */ , LORA_IO1 /* 33 */, LORA_IO2 /* 32 */},
+ .busy = LMIC_UNUSED_PIN,
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#elif defined(BRD_sx1262_radio)
+const lmic_pinmap lmic_pins = {
+ .nss = LORA_CS, // 18
+ // TXEN is controlled through DIO2 by the SX1262 (HPD16A) directly
+ .tx = LMIC_CONTROLLED_BY_DIO2,
+ .rx = LMIC_UNUSED_PIN,
+ .rst = LORA_RST, // 23
+ .dio = {LMIC_UNUSED_PIN, LORA_IO1 /* 33 */, LMIC_UNUSED_PIN},
+ .busy = LORA_IO2, // 32
+ // TCXO is controlled through DIO3 by the SX1262 directly
+ .tcxo = LMIC_CONTROLLED_BY_DIO3,
+};
+#else
+#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)"
+#endif
+#else
+#error "Unknown board, no standard pimap available. Define your own in the main sketch file."
+#endif
+#endif // defined(USE_STANDARD_PINMAP)
diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino
new file mode 100644
index 0000000..1d50e52
--- /dev/null
+++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
+ *
+ * Permission is hereby granted, free of charge, to anyone
+ * obtaining a copy of this document and accompanying files,
+ * to do whatever they want with them without any restriction,
+ * including, but not limited to, copying, modification and redistribution.
+ * NO WARRANTY OF ANY KIND IS PROVIDED.
+ *
+ * This example sends a valid LoRaWAN packet with payload "Hello,
+ * world!", using frequency and encryption settings matching those of
+ * the The Things Network.
+ *
+ * This uses ABP (Activation-by-personalisation), where a DevAddr and
+ * Session keys are preconfigured (unlike OTAA, where a DevEUI and
+ * application key is configured, while the DevAddr and session keys are
+ * assigned/generated in the over-the-air-activation procedure).
+ *
+ * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
+ * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
+ * violated by this sketch when left running for longer)!
+ *
+ * To use this sketch, first register your application and device with
+ * the things network, to set or generate a DevAddr, NwkSKey and
+ * AppSKey. Each device should have their own unique values for these
+ * fields.
+ *
+ * Do not forget to define the radio type correctly in config.h.
+ *
+ *******************************************************************************/
+
+#include
+#include
+#include
+
+// LoRaWAN NwkSKey, network session key
+// This is the default Semtech key, which is used by the early prototype TTN
+// network.
+static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
+
+// LoRaWAN AppSKey, application session key
+// This is the default Semtech key, which is used by the early prototype TTN
+// network.
+static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
+
+// LoRaWAN end-device address (DevAddr)
+static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node!
+
+// These callbacks are only used in over-the-air activation, so they are
+// left empty here (we cannot leave them out completely unless
+// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
+void os_getJoinEui (u1_t* /* buf */) { }
+void os_getDevEui (u1_t* /* buf */) { }
+void os_getNwkKey (u1_t* /* buf */) { }
+
+// The region to use, this just uses the first one (can be changed if
+// multiple regions are enabled).
+u1_t os_getRegion (void) { return LMIC_regionCode(0); }
+
+// Schedule TX every this many milliseconds (might become longer due to duty
+// cycle limitations).
+const unsigned TX_INTERVAL = 60000;
+
+// When this is defined, a standard pinmap from standard-pinmaps.ino
+// will be used. If you need to use a custom pinmap, comment this line
+// and enter the pin numbers in the lmic_pins variable below.
+#define USE_STANDARD_PINMAP
+
+#if !defined(USE_STANDARD_PINMAP)
+// All pin assignments use Arduino pin numbers (e.g. what you would pass
+// to digitalWrite), or LMIC_UNUSED_PIN when a pin is not connected.
+const lmic_pinmap lmic_pins = {
+ // NSS input pin for SPI communication (required)
+ .nss = 0,
+ // If needed, these pins control the RX/TX antenna switch (active
+ // high outputs). When you have both, the antenna switch can
+ // powerdown when unused. If you just have a RXTX pin it should
+ // usually be assigned to .tx, reverting to RX mode when idle).
+ //
+ // The SX127x has an RXTX pin that can automatically control the
+ // antenna switch (if internally connected on the transceiver
+ // board). This pin is always active, so no configuration is needed
+ // for that here.
+ // On SX126x, the DIO2 can be used for the same thing, but this is
+ // disabled by default. To enable this, set .tx to
+ // LMIC_CONTROLLED_BY_DIO2 below (this seems to be common and
+ // enabling it when not needed is probably harmless, unless DIO2 is
+ // connected to GND or VCC directly inside the transceiver board).
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ // Radio reset output pin (active high for SX1276, active low for
+ // others). When omitted, reset is skipped which might cause problems.
+ .rst = 1,
+ // DIO input pins.
+ // For SX127x, LoRa needs DIO0 and DIO1, FSK needs DIO0, DIO1 and DIO2
+ // For SX126x, Only DIO1 is needed (so leave DIO0 and DIO2 as LMIC_UNUSED_PIN)
+ .dio = {/* DIO0 */ 2, /* DIO1 */ 3, /* DIO2 */ 4},
+ // Busy input pin (SX126x only). When omitted, a delay is used which might
+ // cause problems.
+ .busy = LMIC_UNUSED_PIN,
+ // TCXO oscillator enable output pin (active high).
+ //
+ // For SX127x this should be an I/O pin that controls the TCXO, or
+ // LMIC_UNUSED_PIN when a crystal is used instead of a TCXO.
+ //
+ // For SX126x this should be LMIC_CONTROLLED_BY_DIO3 when a TCXO is
+ // directly connected to the transceiver DIO3 to let the transceiver
+ // start and stop the TCXO, or LMIC_UNUSED_PIN when a crystal is
+ // used instead of a TCXO. Controlling the TCXO from the MCU is not
+ // supported.
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#endif // !defined(USE_STANDARD_PINMAP)
+
+void onLmicEvent (ev_t ev) {
+ Serial.print(os_getTime());
+ Serial.print(": ");
+ switch(ev) {
+ case EV_SCAN_TIMEOUT:
+ Serial.println(F("EV_SCAN_TIMEOUT"));
+ break;
+ case EV_BEACON_FOUND:
+ Serial.println(F("EV_BEACON_FOUND"));
+ break;
+ case EV_BEACON_MISSED:
+ Serial.println(F("EV_BEACON_MISSED"));
+ break;
+ case EV_BEACON_TRACKED:
+ Serial.println(F("EV_BEACON_TRACKED"));
+ break;
+ case EV_JOINING:
+ Serial.println(F("EV_JOINING"));
+ break;
+ case EV_JOINED:
+ Serial.println(F("EV_JOINED"));
+ break;
+ case EV_RFU1:
+ Serial.println(F("EV_RFU1"));
+ break;
+ case EV_JOIN_FAILED:
+ Serial.println(F("EV_JOIN_FAILED"));
+ break;
+ case EV_REJOIN_FAILED:
+ Serial.println(F("EV_REJOIN_FAILED"));
+ break;
+ case EV_TXCOMPLETE:
+ Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
+ if (LMIC.txrxFlags & TXRX_ACK)
+ Serial.println(F("Received ack"));
+ if (LMIC.dataLen) {
+ Serial.print(F("Received "));
+ Serial.print(LMIC.dataLen);
+ Serial.println(F(" bytes of payload"));
+ }
+ break;
+ case EV_LOST_TSYNC:
+ Serial.println(F("EV_LOST_TSYNC"));
+ break;
+ case EV_RESET:
+ Serial.println(F("EV_RESET"));
+ break;
+ case EV_RXCOMPLETE:
+ // data received in ping slot
+ Serial.println(F("EV_RXCOMPLETE"));
+ break;
+ case EV_LINK_DEAD:
+ Serial.println(F("EV_LINK_DEAD"));
+ break;
+ case EV_LINK_ALIVE:
+ Serial.println(F("EV_LINK_ALIVE"));
+ break;
+ case EV_SCAN_FOUND:
+ Serial.println(F("EV_SCAN_FOUND"));
+ break;
+ case EV_TXSTART:
+ Serial.println(F("EV_TXSTART"));
+ break;
+ case EV_TXDONE:
+ Serial.println(F("EV_TXDONE"));
+ break;
+ case EV_DATARATE:
+ Serial.println(F("EV_DATARATE"));
+ break;
+ case EV_START_SCAN:
+ Serial.println(F("EV_START_SCAN"));
+ break;
+ case EV_ADR_BACKOFF:
+ Serial.println(F("EV_ADR_BACKOFF"));
+ break;
+ default:
+ Serial.print(F("Unknown event: "));
+ Serial.println(ev);
+ break;
+ }
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ // Wait up to 5 seconds for serial to be opened, to allow catching
+ // startup messages on native USB boards (that do not reset when
+ // serial is opened).
+ unsigned long start = millis();
+ while (millis() - start < 5000 && !Serial);
+
+ Serial.println();
+ Serial.println();
+ Serial.println(F("Starting"));
+ Serial.println();
+
+ // LMIC init
+ os_init(nullptr);
+ // Reset the MAC state. Session and pending data transfers will be discarded.
+ LMIC_reset();
+
+ // Set static session parameters. Instead of dynamically establishing a session
+ // by joining the network, precomputed session parameters are be provided.
+ #ifdef PROGMEM
+ // On AVR, these values are stored in flash and only copied to RAM
+ // once. Copy them to a temporary buffer here, LMIC_setSession will
+ // copy them into a buffer of its own again.
+ uint8_t appskey[sizeof(APPSKEY)];
+ uint8_t nwkskey[sizeof(NWKSKEY)];
+ memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
+ memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
+ LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
+ #else
+ // If not running an AVR with PROGMEM, just use the arrays directly
+ LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
+ #endif
+
+ #if defined(CFG_eu868)
+ // These are defined by the LoRaWAN specification
+ enum {
+ EU_DR_SF12 = 0,
+ EU_DR_SF11 = 1,
+ EU_DR_SF10 = 2,
+ EU_DR_SF9 = 3,
+ EU_DR_SF8 = 4,
+ EU_DR_SF7 = 5,
+ EU_DR_SF7_BW250 = 6,
+ EU_DR_FSK = 7,
+ };
+
+ // Set up the channels used by the Things Network, which corresponds
+ // to the defaults of most gateways. Without this, only three base
+ // channels from the LoRaWAN specification are used, which certainly
+ // works, so it is good for debugging, but can overload those
+ // frequencies, so be sure to configure the full frequency range of
+ // your network here (unless your network autoconfigures them).
+ // Setting up channels should happen after LMIC_setSession, as that
+ // configures the minimal channel set.
+ LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7_BW250)); // g-band
+ LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band
+ LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(EU_DR_FSK, EU_DR_FSK)); // g2-band
+
+ // TTN uses SF9 at 869.525Mhz for its RX2 window (frequency is
+ // default LoRaWAN, SF is different, but set them both to be
+ // explicit).
+ LMIC.dn2Freq = 869525000;
+ LMIC.dn2Dr = EU_DR_SF9;
+
+ // Set data rate for uplink
+ LMIC_setDrTxpow(EU_DR_SF7, KEEP_TXPOWADJ);
+ #elif defined(CFG_us915)
+ // NA-US channels 0-71 are configured automatically
+ // but only one group of 8 should (a subband) should be active
+ // TTN recommends the second sub band, 1 in a zero based count.
+ // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
+ // TODO: How to configure these channels? LMIC had LMIC_selectSubBand,
+ // but it seems BasicMac only has LMIC_disableChannel.
+ #endif
+
+ // Disable link check validation
+ LMIC_setLinkCheckMode(0);
+
+ // Enable this to increase the receive window size, to compensate
+ // for an inaccurate clock. // This compensate for +/- 10% clock
+ // error, a lower value will likely be more appropriate.
+ //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
+
+ // Queue first packet
+ send_packet();
+}
+
+uint32_t last_packet = 0;
+
+void loop() {
+ // Let LMIC handle background tasks
+ os_runstep();
+
+ // If TX_INTERVAL passed, *and* our previous packet is not still
+ // pending (which can happen due to duty cycle limitations), send
+ // the next packet.
+ if (millis() - last_packet > TX_INTERVAL && !(LMIC.opmode & (OP_JOINING|OP_TXRXPEND)))
+ send_packet();
+}
+
+void send_packet(){
+ // Prepare upstream data transmission at the next possible time.
+ uint8_t mydata[] = "Hello, world!";
+ LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
+ Serial.println(F("Packet queued"));
+
+ last_packet = millis();
+}
+
diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino
new file mode 100644
index 0000000..4d1e69e
--- /dev/null
+++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
+ *
+ * Permission is hereby granted, free of charge, to anyone
+ * obtaining a copy of this document and accompanying files,
+ * to do whatever they want with them without any restriction,
+ * including, but not limited to, copying, modification and redistribution.
+ * NO WARRANTY OF ANY KIND IS PROVIDED.
+ *
+ * This example sends a valid LoRaWAN packet with payload "Hello,
+ * world!", using frequency and encryption settings matching those of
+ * the The Things Network.
+ *
+ * This uses OTAA (Over-the-air activation), where where a DevEUI and
+ * application key is configured, which are used in an over-the-air
+ * activation procedure where a DevAddr and session keys are
+ * assigned/generated for use with all further communication.
+ *
+ * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
+ * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
+ * violated by this sketch when left running for longer)!
+
+ * To use this sketch, first register your application and device with
+ * the things network, to set or generate an AppEUI, DevEUI and AppKey.
+ * Multiple devices can use the same AppEUI, but each device has its own
+ * DevEUI and AppKey.
+ *
+ * Do not forget to define the radio type correctly in config.h.
+ *
+ *******************************************************************************/
+
+#include
+#include
+#include
+
+// This EUI must be in little-endian format, so least-significant-byte
+// first. When copying an EUI from ttnctl output, this means to reverse
+// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
+// 0x70.
+static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+void os_getJoinEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
+
+// This should also be in little endian format, see above.
+static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
+
+// This key should be in big endian format (or, since it is not really a
+// number but a block of memory, endianness does not really apply). In
+// practice, a key taken from ttnctl can be copied as-is.
+// The key shown here is the semtech default key.
+static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
+void os_getNwkKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
+
+// The region to use, this just uses the first one (can be changed if
+// multiple regions are enabled).
+u1_t os_getRegion (void) { return LMIC_regionCode(0); }
+
+// Schedule TX every this many milliseconds (might become longer due to duty
+// cycle limitations).
+const unsigned TX_INTERVAL = 60000;
+
+// Timestamp of last packet sent
+uint32_t last_packet = 0;
+
+// When this is defined, a standard pinmap from standard-pinmaps.ino
+// will be used. If you need to use a custom pinmap, comment this line
+// and enter the pin numbers in the lmic_pins variable below.
+#define USE_STANDARD_PINMAP
+
+#if !defined(USE_STANDARD_PINMAP)
+// All pin assignments use Arduino pin numbers (e.g. what you would pass
+// to digitalWrite), or LMIC_UNUSED_PIN when a pin is not connected.
+const lmic_pinmap lmic_pins = {
+ // NSS input pin for SPI communication (required)
+ .nss = 0,
+ // If needed, these pins control the RX/TX antenna switch (active
+ // high outputs). When you have both, the antenna switch can
+ // powerdown when unused. If you just have a RXTX pin it should
+ // usually be assigned to .tx, reverting to RX mode when idle).
+ //
+ // The SX127x has an RXTX pin that can automatically control the
+ // antenna switch (if internally connected on the transceiver
+ // board). This pin is always active, so no configuration is needed
+ // for that here.
+ // On SX126x, the DIO2 can be used for the same thing, but this is
+ // disabled by default. To enable this, set .tx to
+ // LMIC_CONTROLLED_BY_DIO2 below (this seems to be common and
+ // enabling it when not needed is probably harmless, unless DIO2 is
+ // connected to GND or VCC directly inside the transceiver board).
+ .tx = LMIC_UNUSED_PIN,
+ .rx = LMIC_UNUSED_PIN,
+ // Radio reset output pin (active high for SX1276, active low for
+ // others). When omitted, reset is skipped which might cause problems.
+ .rst = 1,
+ // DIO input pins.
+ // For SX127x, LoRa needs DIO0 and DIO1, FSK needs DIO0, DIO1 and DIO2
+ // For SX126x, Only DIO1 is needed (so leave DIO0 and DIO2 as LMIC_UNUSED_PIN)
+ .dio = {/* DIO0 */ 2, /* DIO1 */ 3, /* DIO2 */ 4},
+ // Busy input pin (SX126x only). When omitted, a delay is used which might
+ // cause problems.
+ .busy = LMIC_UNUSED_PIN,
+ // TCXO oscillator enable output pin (active high).
+ //
+ // For SX127x this should be an I/O pin that controls the TCXO, or
+ // LMIC_UNUSED_PIN when a crystal is used instead of a TCXO.
+ //
+ // For SX126x this should be LMIC_CONTROLLED_BY_DIO3 when a TCXO is
+ // directly connected to the transceiver DIO3 to let the transceiver
+ // start and stop the TCXO, or LMIC_UNUSED_PIN when a crystal is
+ // used instead of a TCXO. Controlling the TCXO from the MCU is not
+ // supported.
+ .tcxo = LMIC_UNUSED_PIN,
+};
+#endif // !defined(USE_STANDARD_PINMAP)
+
+void onLmicEvent (ev_t ev) {
+ Serial.print(os_getTime());
+ Serial.print(": ");
+ switch(ev) {
+ case EV_SCAN_TIMEOUT:
+ Serial.println(F("EV_SCAN_TIMEOUT"));
+ break;
+ case EV_BEACON_FOUND:
+ Serial.println(F("EV_BEACON_FOUND"));
+ break;
+ case EV_BEACON_MISSED:
+ Serial.println(F("EV_BEACON_MISSED"));
+ break;
+ case EV_BEACON_TRACKED:
+ Serial.println(F("EV_BEACON_TRACKED"));
+ break;
+ case EV_JOINING:
+ Serial.println(F("EV_JOINING"));
+ break;
+ case EV_JOINED:
+ Serial.println(F("EV_JOINED"));
+
+ // Disable link check validation (automatically enabled
+ // during join, but not supported by TTN at this time).
+ LMIC_setLinkCheckMode(0);
+ break;
+ case EV_RFU1:
+ Serial.println(F("EV_RFU1"));
+ break;
+ case EV_JOIN_FAILED:
+ Serial.println(F("EV_JOIN_FAILED"));
+ break;
+ case EV_REJOIN_FAILED:
+ Serial.println(F("EV_REJOIN_FAILED"));
+ break;
+ break;
+ case EV_TXCOMPLETE:
+ Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
+ if (LMIC.txrxFlags & TXRX_ACK)
+ Serial.println(F("Received ack"));
+ if (LMIC.dataLen) {
+ Serial.print(F("Received "));
+ Serial.print(LMIC.dataLen);
+ Serial.println(F(" bytes of payload"));
+ }
+ break;
+ case EV_LOST_TSYNC:
+ Serial.println(F("EV_LOST_TSYNC"));
+ break;
+ case EV_RESET:
+ Serial.println(F("EV_RESET"));
+ break;
+ case EV_RXCOMPLETE:
+ // data received in ping slot
+ Serial.println(F("EV_RXCOMPLETE"));
+ break;
+ case EV_LINK_DEAD:
+ Serial.println(F("EV_LINK_DEAD"));
+ break;
+ case EV_LINK_ALIVE:
+ Serial.println(F("EV_LINK_ALIVE"));
+ break;
+ case EV_SCAN_FOUND:
+ Serial.println(F("EV_SCAN_FOUND"));
+ break;
+ case EV_TXSTART:
+ Serial.println(F("EV_TXSTART"));
+ break;
+ case EV_TXDONE:
+ Serial.println(F("EV_TXDONE"));
+ break;
+ case EV_DATARATE:
+ Serial.println(F("EV_DATARATE"));
+ break;
+ case EV_START_SCAN:
+ Serial.println(F("EV_START_SCAN"));
+ break;
+ case EV_ADR_BACKOFF:
+ Serial.println(F("EV_ADR_BACKOFF"));
+ break;
+
+ default:
+ Serial.print(F("Unknown event: "));
+ Serial.println(ev);
+ break;
+ }
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ // Wait up to 5 seconds for serial to be opened, to allow catching
+ // startup messages on native USB boards (that do not reset when
+ // serial is opened).
+ unsigned long start = millis();
+ while (millis() - start < 5000 && !Serial);
+
+ Serial.println();
+ Serial.println();
+ Serial.println(F("Starting"));
+ Serial.println();
+
+ // LMIC init
+ os_init(nullptr);
+ LMIC_reset();
+
+ // Enable this to increase the receive window size, to compensate
+ // for an inaccurate clock. // This compensate for +/- 10% clock
+ // error, a lower value will likely be more appropriate.
+ //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
+
+ // Start join
+ LMIC_startJoining();
+
+ // Make sure the first packet is scheduled ASAP after join completes
+ last_packet = millis() - TX_INTERVAL;
+
+ // Optionally wait for join to complete (uncomment this is you want
+ // to run the loop while joining).
+ while ((LMIC.opmode & (OP_JOINING)))
+ os_runstep();
+}
+
+void loop() {
+ // Let LMIC handle background tasks
+ os_runstep();
+
+ // If TX_INTERVAL passed, *and* our previous packet is not still
+ // pending (which can happen due to duty cycle limitations), send
+ // the next packet.
+ if (millis() - last_packet > TX_INTERVAL && !(LMIC.opmode & (OP_JOINING|OP_TXRXPEND)))
+ send_packet();
+}
+
+void send_packet(){
+ // Prepare upstream data transmission at the next possible time.
+ uint8_t mydata[] = "Hello, world!";
+ LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
+ Serial.println(F("Packet queued"));
+
+ last_packet = millis();
+}
diff --git a/target/arduino/export.sh b/target/arduino/export.sh
new file mode 100755
index 0000000..bbed1ad
--- /dev/null
+++ b/target/arduino/export.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+#
+# Copyright (C) 2016, Matthijs Kooijman
+#
+# --- Revised 3-Clause BSD License ---
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the copyright holder nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# Export script for unix operating systems (e.g. Linux and OSX).
+#
+
+SRC=$(cd "$(dirname "$0")"; pwd)
+
+usage() {
+ echo "$0 [--link] TARGET_DIR"
+ echo "Create an Arduino-compatible library in the given directory, overwriting existing files. If --link is given, creates symbolic links for easy testing."
+}
+
+
+# This recursively copies $1 (file or directory) into $2 (directory)
+# This is essentially cp --symbolic-link, but POSIX-compatible.
+create_links() {
+ local TARGET=$(cd "$2" && pwd)
+ local SRCDIR=$(cd "$(dirname "$1")" && pwd)
+ local SRCNAME=$(basename "$1")
+ (cd "$SRCDIR" && find "$SRCNAME" -type d -exec mkdir -p "$TARGET/{}" \; -o -exec ln -s -v "$SRCDIR/{}" "$TARGET/{}" \; )
+}
+
+
+CMD="cp -r -v"
+case "$1" in
+ --help)
+ usage
+ exit 0
+ ;;
+ --link)
+ CMD="create_links"
+ shift;
+ ;;
+ --*)
+ echo "Unknown option: $1" 1>&2
+ exit 1
+ ;;
+esac
+
+TARGET=$1
+
+if [ -z "$TARGET" ]; then
+ usage
+ exit 1
+fi
+
+if ! [ -d "$(dirname "$TARGET")" ]; then
+ echo "Parent of $TARGET should exist" 1>&2
+ exit 1
+fi
+
+
+if [ -e "$TARGET" ]; then
+ echo -n "$TARGET exists, remove before export? [yN]"
+ read answer
+ if [ "$answer" = "y" ]; then
+ rm -rf "$TARGET"
+ fi
+fi
+
+mkdir -p "$TARGET"/src
+
+# This copies or links the relevant directories. For the hal and lmic
+# directories, the contained files are copied or linked, so that when
+# linking relative includes still work as expected
+$CMD "$SRC"/library.properties "$TARGET"
+$CMD "$SRC"/basicmac.h "$TARGET"/src
+$CMD "$SRC"/../../lmic "$TARGET"/src
+$CMD "$SRC"/hal "$TARGET"/src
+$CMD "$SRC"/../../aes "$TARGET"/src
+$CMD "$SRC"/examples "$TARGET"
+$CMD "$SRC"/board.h "$TARGET"/src/lmic
+$CMD "$SRC"/hw.h "$TARGET"/src/lmic
+
+# Then copy/link the common files (e.g. pinmap) into each example
+# directory
+for E in "$TARGET"/examples/*; do
+ $CMD "$SRC"/examples-common-files/* "$E"
+done
diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp
new file mode 100644
index 0000000..b275b7f
--- /dev/null
+++ b/target/arduino/hal/hal.cpp
@@ -0,0 +1,495 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This the HAL to run LMIC on top of the Arduino environment.
+ *******************************************************************************/
+
+#define _GNU_SOURCE 1 // For fopencookie
+// Must be first, otherwise it might have already been included without _GNU_SOURCE
+#include
+#undef _GNU_SOURCE
+// Prevent warning on samd where samd21g18a.h from CMSIS defines this
+#undef LITTLE_ENDIAN
+#include
+#include
+#include "../basicmac.h"
+#include "hal.h"
+
+// Datasheet defins typical times until busy goes low. Most are < 200us,
+// except when waking up from sleep, which typically takes 3500us. Since
+// we cannot know here if we are in sleep, we'll have to assume we are.
+// Since 3500 is typical, not maximum, wait a bit more than that.
+static unsigned long MAX_BUSY_TIME = 5000;
+
+// -----------------------------------------------------------------------------
+// I/O
+
+static void hal_io_init () {
+ uint8_t i;
+ // Checks below assume that all special pin values are >= LMIC_UNUSED_PIN, so check that.
+ #if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+ LMIC_STATIC_ASSERT(LMIC_UNUSED_PIN < LMIC_CONTROLLED_BY_DIO3, "Unexpected constant values");
+ LMIC_STATIC_ASSERT(LMIC_UNUSED_PIN < LMIC_CONTROLLED_BY_DIO2, "Unexpected constant values");
+ #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+
+ ASSERT(lmic_pins.nss < LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.rst <= LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.rx <= LMIC_UNUSED_PIN);
+
+#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
+ //DIO0 is required, DIO1 is required for LoRa, DIO2 for FSK
+ ASSERT(lmic_pins.dio[0] < LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.dio[1] < LMIC_UNUSED_PIN || lmic_pins.dio[2] < LMIC_UNUSED_PIN);
+
+ ASSERT(lmic_pins.busy == LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN);
+#elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+ // Only DIO1 should be specified
+ ASSERT(lmic_pins.dio[0] == LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.dio[1] < LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.dio[2] == LMIC_UNUSED_PIN);
+
+ ASSERT(lmic_pins.busy <= LMIC_UNUSED_PIN);
+ ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN || lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3);
+ ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN || lmic_pins.tx == LMIC_CONTROLLED_BY_DIO2);
+#else
+ #error "Unknown radio type?"
+#endif
+
+ // Write HIGH to deselect (NSS is active low). Do this before
+ // setting output, to prevent a moment of OUTPUT LOW on e.g. AVR.
+ digitalWrite(lmic_pins.nss, HIGH);
+ pinMode(lmic_pins.nss, OUTPUT);
+ // Write HIGH again after setting output, for architectures that
+ // reset to LOW when setting OUTPUT (e.g. arduino-STM32L4).
+ digitalWrite(lmic_pins.nss, HIGH);
+
+ if (lmic_pins.tx < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.tx, OUTPUT);
+ if (lmic_pins.rx < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.rx, OUTPUT);
+ if (lmic_pins.rst < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.rst, OUTPUT);
+ if (lmic_pins.busy < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.busy, INPUT);
+ if (lmic_pins.tcxo < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.tcxo, OUTPUT);
+
+ for (i = 0; i < NUM_DIO; ++i) {
+ if (lmic_pins.dio[i] < LMIC_UNUSED_PIN)
+ pinMode(lmic_pins.dio[i], INPUT);
+ }
+}
+
+// rx = 0, tx = 1, off = -1
+void hal_ant_switch (u1_t val) {
+ // TODO: Support separate pin for TX2 (PA_BOOST output)
+ if (lmic_pins.tx < LMIC_UNUSED_PIN)
+ digitalWrite(lmic_pins.tx, val == HAL_ANTSW_TX || val == HAL_ANTSW_TX2);
+ if (lmic_pins.rx < LMIC_UNUSED_PIN)
+ digitalWrite(lmic_pins.rx, val == HAL_ANTSW_RX);
+}
+
+// set radio RST pin to given value (or keep floating!)
+bool hal_pin_rst (u1_t val) {
+ if (lmic_pins.rst >= LMIC_UNUSED_PIN)
+ return false;
+
+ if(val == 0 || val == 1) { // drive pin
+ pinMode(lmic_pins.rst, OUTPUT);
+ digitalWrite(lmic_pins.rst, val);
+ } else { // keep pin floating
+ pinMode(lmic_pins.rst, INPUT);
+ }
+ return true;
+}
+
+void hal_irqmask_set (int /* mask */) {
+ // Not implemented
+}
+
+static bool dio_states[NUM_DIO] = {0};
+
+static void hal_io_check() {
+ uint8_t i;
+ for (i = 0; i < NUM_DIO; ++i) {
+ if (lmic_pins.dio[i] >= LMIC_UNUSED_PIN)
+ continue;
+
+ if (dio_states[i] != digitalRead(lmic_pins.dio[i])) {
+ dio_states[i] = !dio_states[i];
+ if (dio_states[i])
+ radio_irq_handler(i, hal_ticks());
+ }
+ }
+}
+
+#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
+bool hal_pin_tcxo (u1_t val) {
+ if (lmic_pins.tcxo >= LMIC_UNUSED_PIN)
+ return false;
+
+ digitalWrite(lmic_pins.tcxo, val);
+ return true;
+}
+#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio)
+
+void hal_pin_busy_wait (void) {
+ if (lmic_pins.busy >= LMIC_UNUSED_PIN) {
+ // TODO: We could probably keep some state so we know the chip
+ // is in sleep, since otherwise the delay can be much shorter.
+ // Also, all delays after commands (rather than waking up from
+ // sleep) are measured from the *end* of the previous SPI
+ // transaction, so we could wait shorter if we remember when
+ // that was.
+ delayMicroseconds(MAX_BUSY_TIME);
+ } else {
+ unsigned long start = micros();
+
+ while((micros() - start) < MAX_BUSY_TIME && digitalRead(lmic_pins.busy)) /* wait */;
+ }
+}
+
+#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+bool hal_dio3_controls_tcxo (void) {
+ return lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3;
+}
+bool hal_dio2_controls_rxtx (void) {
+ return lmic_pins.tx == LMIC_CONTROLLED_BY_DIO2;
+}
+#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+
+// -----------------------------------------------------------------------------
+// SPI
+
+static const SPISettings settings(10E6, MSBFIRST, SPI_MODE0);
+
+static void hal_spi_init () {
+ SPI.begin();
+}
+
+void hal_spi_select (int on) {
+ if (on)
+ SPI.beginTransaction(settings);
+ else
+ SPI.endTransaction();
+
+ //Serial.println(val?">>":"<<");
+ digitalWrite(lmic_pins.nss, !on);
+}
+
+// perform SPI transaction with radio
+u1_t hal_spi (u1_t out) {
+ u1_t res = SPI.transfer(out);
+/*
+ Serial.print(">");
+ Serial.print(out, HEX);
+ Serial.print("<");
+ Serial.println(res, HEX);
+ */
+ return res;
+}
+
+// -----------------------------------------------------------------------------
+// TIME
+
+static void hal_time_init () {
+ // Nothing to do
+}
+
+u4_t hal_ticks () {
+ // Because micros() is scaled down in this function, micros() will
+ // overflow before the tick timer should, causing the tick timer to
+ // miss a significant part of its values if not corrected. To fix
+ // this, the "overflow" serves as an overflow area for the micros()
+ // counter. It consists of three parts:
+ // - The US_PER_OSTICK upper bits are effectively an extension for
+ // the micros() counter and are added to the result of this
+ // function.
+ // - The next bit overlaps with the most significant bit of
+ // micros(). This is used to detect micros() overflows.
+ // - The remaining bits are always zero.
+ //
+ // By comparing the overlapping bit with the corresponding bit in
+ // the micros() return value, overflows can be detected and the
+ // upper bits are incremented. This is done using some clever
+ // bitwise operations, to remove the need for comparisons and a
+ // jumps, which should result in efficient code. By avoiding shifts
+ // other than by multiples of 8 as much as possible, this is also
+ // efficient on AVR (which only has 1-bit shifts).
+ static uint8_t overflow = 0;
+
+ // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0,
+ // the others will be the lower bits of our return value.
+ uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT;
+ // Most significant byte of scaled
+ uint8_t msb = scaled >> 24;
+ // Mask pointing to the overlapping bit in msb and overflow.
+ const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT));
+ // Update overflow. If the overlapping bit is different
+ // between overflow and msb, it is added to the stored value,
+ // so the overlapping bit becomes equal again and, if it changed
+ // from 1 to 0, the upper bits are incremented.
+ overflow += (msb ^ overflow) & mask;
+
+ // Return the scaled value with the upper bits of stored added. The
+ // overlapping bit will be equal and the lower bits will be 0, so
+ // bitwise or is a no-op for them.
+ return scaled | ((uint32_t)overflow << 24);
+
+ // 0 leads to correct, but overly complex code (it could just return
+ // micros() unmodified), 8 leaves no room for the overlapping bit.
+ static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value");
+}
+
+u8_t hal_xticks (void) {
+ // TODO
+ return hal_ticks();
+}
+/* Not actually used now
+s2_t hal_subticks (void) {
+ // TODO
+ return 0;
+}
+*/
+
+// Returns the number of ticks until time. Negative values indicate that
+// time has already passed.
+static s4_t delta_time(u4_t time) {
+ return (s4_t)(time - hal_ticks());
+}
+
+void hal_waitUntil (u4_t time) {
+ s4_t delta = delta_time(time);
+ // From delayMicroseconds docs: Currently, the largest value that
+ // will produce an accurate delay is 16383.
+ while (delta > (16000 / US_PER_OSTICK)) {
+ delay(16);
+ delta -= (16000 / US_PER_OSTICK);
+ }
+ if (delta > 0)
+ delayMicroseconds(delta * US_PER_OSTICK);
+}
+
+// check and rewind for target time
+u1_t hal_checkTimer (u4_t time) {
+ // No need to schedule wakeup, since we're not sleeping
+ return delta_time(time) <= 0;
+}
+
+static uint8_t irqlevel = 0;
+
+void hal_disableIRQs () {
+ noInterrupts();
+ irqlevel++;
+}
+
+void hal_enableIRQs () {
+ if(--irqlevel == 0) {
+ interrupts();
+
+ // Instead of using proper interrupts (which are a bit tricky
+ // and/or not available on all pins on AVR), just poll the pin
+ // values. Since os_runloop disables and re-enables interrupts,
+ // putting this here makes sure we check at least once every
+ // loop.
+ //
+ // As an additional bonus, this prevents the can of worms that
+ // we would otherwise get for running SPI transfers inside ISRs
+ hal_io_check();
+ }
+}
+
+u1_t hal_sleep (u1_t type, u4_t targettime) {
+ // Actual sleeping not implemented, but jobs are only run when this
+ // function returns 0, so make sure we only do that when the
+ // targettime is close. When asked to sleep forever (until woken up
+ // by an interrupt), just return immediately to keep polling.
+ if (type == HAL_SLEEP_FOREVER)
+ return 0;
+
+ // TODO: What value should we use for "close"?
+ return delta_time(targettime) < 10 ? 0 : 1;
+}
+
+void hal_watchcount (int /* cnt */) {
+ // Not implemented
+}
+
+// -----------------------------------------------------------------------------
+// DEBUG
+
+#ifdef CFG_DEBUG
+static void hal_debug_init() {
+ #ifdef LED_BUILTIN
+ pinMode(LED_BUILTIN, OUTPUT);
+ #endif
+}
+
+#if !defined(CFG_DEBUG_STREAM)
+#error "CFG_DEBUG needs CFG_DEBUG_STREAM defined in target-config.h"
+#endif
+
+void hal_debug_str (const char* str) {
+ CFG_DEBUG_STREAM.print(str);
+}
+
+void hal_debug_led (int val) {
+ #ifdef LED_BUILTIN
+ digitalWrite(LED_BUILTIN, val);
+ #endif
+}
+#endif // CFG_DEBUG
+
+// -----------------------------------------------------------------------------
+
+#if defined(LMIC_PRINTF_TO)
+#if defined(__AVR__)
+// On AVR, use the AVR-specific fdev_setup_stream to redirect stdout
+static int uart_putchar (char c, FILE *)
+{
+ LMIC_PRINTF_TO.write(c) ;
+ return 0 ;
+}
+
+void hal_printf_init() {
+ // create a FILE structure to reference our UART output function
+ static FILE uartout;
+ memset(&uartout, 0, sizeof(uartout));
+
+ // fill in the UART file descriptor with pointer to writer.
+ fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
+
+ // The uart is the standard output device STDOUT.
+ stdout = &uartout ;
+}
+#else
+// On non-AVR platforms, use the somewhat more complex "cookie"-based
+// approach to custom streams. This is a GNU-specific extension to libc.
+static ssize_t uart_putchar (void *, const char *buf, size_t len) {
+ auto res = LMIC_PRINTF_TO.write(buf, len);
+ // Since the C interface has no meaningful way to flush (fflush() is a
+ // no-op on AVR since stdio does not introduce any buffering), just flush
+ // every byte.
+ LMIC_PRINTF_TO.flush();
+ return res;
+}
+
+static cookie_io_functions_t functions = {
+ .read = NULL,
+ .write = uart_putchar,
+ .seek = NULL,
+ .close = NULL
+};
+
+void hal_printf_init() {
+ stdout = fopencookie(NULL, "w", functions);
+ // Disable buffering, so the callbacks get called right away
+ setbuf(stdout, nullptr);
+}
+#endif // !defined(__AVR__)
+#endif // defined(LMIC_PRINTF_TO)
+
+void hal_init (void * /* bootarg */) {
+ // configure radio I/O and interrupt handler
+ hal_io_init();
+ // configure radio SPI
+ hal_spi_init();
+ // configure timer and interrupt handler
+ hal_time_init();
+#if defined(LMIC_PRINTF_TO)
+ // printf support
+ hal_printf_init();
+#endif
+#ifdef CFG_DEBUG
+ hal_debug_init();
+#endif
+}
+
+void hal_failed () {
+#ifdef CFG_DEBUG
+ CFG_DEBUG_STREAM.flush();
+#endif
+ // keep IRQs enabled, to allow e.g. USB to continue to run and allow
+ // firmware uploads on boards with native USB.
+ while(1);
+}
+
+void hal_reboot (void) {
+ // TODO
+ hal_failed();
+}
+
+u1_t hal_getBattLevel (void) {
+ // Not implemented
+ return 0;
+}
+
+void hal_setBattLevel (u1_t /* level */) {
+ // Not implemented
+}
+
+void hal_fwinfo (hal_fwi* /* fwi */) {
+ // Not implemented
+}
+
+u1_t* hal_joineui (void) {
+ return nullptr;
+}
+
+u1_t* hal_deveui (void) {
+ return nullptr;
+}
+
+u1_t* hal_nwkkey (void) {
+ return nullptr;
+}
+
+u1_t* hal_appkey (void) {
+ return nullptr;
+}
+
+u1_t* hal_serial (void) {
+ return nullptr;
+}
+
+u4_t hal_region (void) {
+ return 0;
+}
+
+u4_t hal_hwid (void) {
+ return 0;
+}
+
+u4_t hal_unique (void) {
+ return 0;
+}
+
+u4_t hal_dnonce_next (void) {
+ return os_getRndU2();
+}
diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h
new file mode 100644
index 0000000..4f8fb75
--- /dev/null
+++ b/target/arduino/hal/hal.h
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This the HAL to run LMIC on top of the Arduino environment.
+ *******************************************************************************/
+#ifndef _hal_hal_h_
+#define _hal_hal_h_
+
+static const int NUM_DIO = 3;
+
+struct lmic_pinmap {
+ u1_t nss;
+ // Written HIGH in TX mode, LOW otherwise.
+ // Typically used with a single RXTX switch pin.
+ u1_t tx;
+ // Written HIGH in RX mode, LOW otherwise.
+ // Typicaly used with separate RX/TX pins, to allow switching off
+ // the antenna switch completely.
+ u1_t rx;
+ u1_t rst;
+ u1_t dio[NUM_DIO];
+ u1_t busy;
+ u1_t tcxo;
+};
+
+// Use this for any unused pins.
+const u1_t LMIC_UNUSED_PIN = 0xfd;
+
+#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+// Used for lmic_pinmap.tcxo only
+const u1_t LMIC_CONTROLLED_BY_DIO3 = 0xff;
+const u1_t LMIC_CONTROLLED_BY_DIO2 = 0xfe;
+#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio)
+
+// Declared here, to be defined an initialized by the application
+extern const lmic_pinmap lmic_pins;
+
+#endif // _hal_hal_h_
diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h
new file mode 100644
index 0000000..a124d27
--- /dev/null
+++ b/target/arduino/hal/target-config.h
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file defines the configuration of the library and HAL.
+ *******************************************************************************/
+#ifndef _lmic_arduino_hal_config_h_
+#define _lmic_arduino_hal_config_h_
+
+// This defines the region(s) to use. You can enable more than one and
+// then select the right region at runtime using os_getRegion() and/or
+// LMIC_reset_ex().
+#if !defined(CFG_eu868) && !defined(CFG_us915)
+#define CFG_eu868 1
+//#define CFG_us915 1
+//#define CFG_as923 1
+//#define CFG_il915 1
+//#define CFG_kr920 1
+//#define CFG_au915 1
+#endif // !defined(CFG_eu868) && !defined(CFG_us915)
+
+#define CFG_autojoin
+
+#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1261_radio) && !defined(BRD_sx1262_radio)
+// This is the SX1272/SX1273 radio, which is also used on the HopeRF
+// RFM92 boards.
+//#define BRD_sx1272_radio 1
+// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on
+// the HopeRF RFM95 boards.
+//#define BRD_sx1276_radio 1
+// This is the newer SX1261 radio (up to +15dBM).
+//#define BRD_sx1261_radio 1
+// This is the newer SX1262 radio (up to +22dBM).
+#define BRD_sx1262_radio 1
+#endif // !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio)
+
+// 16 μs per tick
+// LMIC requires ticks to be 15.5μs - 100 μs long
+#define US_PER_OSTICK_EXPONENT 4
+#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT)
+#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
+
+// When this is defined, some debug output will be printed and
+// debug_printf(...) is available (which is a slightly non-standard
+// printf implementation).
+// Without this, assertion failures are *not* printed!
+#define CFG_DEBUG
+// When this is defined, additional debug output is printed.
+//#define CFG_DEBUG_VERBOSE
+// Debug output (and assertion failures) are printed to this Stream
+#define CFG_DEBUG_STREAM Serial
+// Define these to add some TX or RX specific debug output (needs
+// CFG_DEBUG)
+#define DEBUG_TX
+#define DEBUG_RX
+// Define these to add some job scheduling specific debug output (needs
+// CFG_DEBUG_VERBOSE)
+//#define DEBUG_JOBS
+// Uncomment to display timestamps in ticks rather than milliseconds
+//#define CFG_DEBUG_RAW_TIMESTAMPS
+
+// When this is defined, the standard libc printf function will print to
+// this Stream. You should probably use CFG_DEBUG and debug_printf()
+// instead, though.
+//#define LMIC_PRINTF_TO Serial
+
+// Remove/comment this to enable code related to beacon tracking.
+#define DISABLE_CLASSB
+
+// This allows choosing between multiple included AES implementations.
+// Make sure exactly one of these is uncommented.
+//
+// This selects the original AES implementation included LMIC. This
+// implementation is optimized for speed on 32-bit processors using
+// fairly big lookup tables, but it takes up big amounts of flash on the
+// AVR architecture.
+// #define USE_ORIGINAL_AES
+//
+// This selects the AES implementation written by Ideetroon for their
+// own LoRaWAN library. It also uses lookup tables, but smaller
+// byte-oriented ones, making it use a lot less flash space (but it is
+// also about twice as slow as the original).
+#define USE_IDEETRON_AES
+
+#endif // _lmic_arduino_hal_config_h_
diff --git a/target/arduino/hw.h b/target/arduino/hw.h
new file mode 100644
index 0000000..a4a149e
--- /dev/null
+++ b/target/arduino/hw.h
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Matthijs Kooijman
+ *
+ * --- Revised 3-Clause BSD License ---
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *******************************************************************************/
+
+#ifndef _hw_h_
+#define _hw_h_
+
+// Dummy file, not used by this HAL (but must be present, since other
+// files include it).
+
+#endif
diff --git a/target/arduino/library.properties b/target/arduino/library.properties
new file mode 100644
index 0000000..822b6e0
--- /dev/null
+++ b/target/arduino/library.properties
@@ -0,0 +1,9 @@
+name=Basicmac LoRaWAN stack
+version=2.2.1
+author=Various
+maintainer=Matthijs Kooijman
+sentence=LoRaWAN framework originally released by Semtech and based on the LMIC library by IBM.
+paragraph=Supports SX1272/SX1276/SX1261/SX1262 LoRa tranceivers
+category=Communication
+url=https://github.com/lacunaspace/basicmac
+architectures=*
diff --git a/unicorn/startup.c b/unicorn/startup.c
index b463837..691761a 100644
--- a/unicorn/startup.c
+++ b/unicorn/startup.c
@@ -15,13 +15,13 @@ void _start (boot_boottab* boottab) {
uint32_t* src = &_sidata;
uint32_t* dst = &_sdata;
while( dst < &_edata ) {
- *dst++ = *src++;
+ *dst++ = *src++;
}
// initialize bss
dst = &_sbss;
while( dst < &_ebss ) {
- *dst++ = 0;
+ *dst++ = 0;
}
// call main function