Unverified Commit 8578e1ea authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

Import partially adapted MCCI LoRaWAN LMIC library. Needs further work.

parent 30a29dcd
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -7,3 +7,18 @@
ifdef app
	loop = 1
endif

COMMON_FLAGS += -Isrc/lib/MCCI_LoRaWAN_LMIC_library/src

C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_eu868.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_util.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_eu_like.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_channelshuffle.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/oslmic.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/radio.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/aes/lmic.c
C_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/aes/other.c

CXX_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/hal/hal.cc
CXX_TARGETS += src/lib/MCCI_LoRaWAN_LMIC_library/src/aes/ideetron/AES-128_V10.cc
+146 −88
Original line number Diff line number Diff line
@@ -8,16 +8,33 @@
#include "driver/stdout.h"
#include "driver/uptime.h"
#include "driver/adc.h"
#include "driver/spi.h"

/*
 * Work in progress! Joining is sometimes successful, but that's about it.
 * TX with user-defined payloads after joining doesn't appear to work yet.
 */

#define CFG_eu868 1
#define CFG_sx1276_radio 1
#define DISABLE_LMIC_FAILURE_TO

#include <lmic.h>
#include <hal/hal.h>

static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

static const u1_t PROGMEM DEVEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

#include "radio.h"
#include "oslmic.h"
static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

unsigned char txbuf[4];
unsigned char rxbuf[4];
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 10;

bool joined = false;

// DIYMall BSFrance LoRa32u4 II v1.3
// See also: arduino packages/adafruit/hardware/avr/1.4.13/variants/feather32u4/pins_arduino.h
@@ -25,95 +42,128 @@ unsigned char rxbuf[4];
// PB0 is also the !SS pin, so it must be configured as output in order to use SPI.
// PB5 "D9" <- Vbat/2 via voltage divider. Appears to also have a connection to the user LED
// PC7 "D13" -> User LED
const GPIO::Pin rstpin = GPIO::pd4; // "D4" -> RST
const GPIO::Pin  cspin = GPIO::pb4; // "D8" -> NSS
const GPIO::Pin dio0pin = GPIO::pe6; // "D7" <- DIO0 / IRQ
const GPIO::Pin dio1pin = GPIO::pc6; // "D5" <- DIO1

static void writeReg (u1_t addr, u1_t data) {
	txbuf[0] = addr | 0x80;
	txbuf[1] = data;
	gpio.write(cspin, 0);
	spi.xmit(2, txbuf, 0, rxbuf);
	gpio.write(cspin, 1);
}

static u1_t readReg (u1_t addr) {
	txbuf[0] = addr & 0x7f;
	gpio.write(cspin, 0);
	spi.xmit(1, txbuf, 2, rxbuf);
	gpio.write(cspin, 1);
	return rxbuf[1];
const lmic_pinmap lmic_pins = {
	.nss = GPIO::pb4,
	.rxtx = LMIC_UNUSED_PIN,
	.rst = GPIO::pd4,
	.dio = {GPIO::pe6, GPIO::pc6, LMIC_UNUSED_PIN},
};

static osjob_t sendjob;

void do_send(osjob_t* j){
	uint16_t bat = adc.getVBat_mV(false);

	// Check if there is not a current TX/RX job running
	if (LMIC.opmode & OP_TXRXPEND) {
		kout.pprint(PSTR("OP_TXRXPEND\n"));
	} else {
		// Prepare upstream data transmission at the next possible time.
		LMIC_setTxData2(1, (uint8_t *)&bat, sizeof(bat), 0);
		kout.pprint(PSTR("Packet queued\n"));
	}
/*
static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
	txbuf[0] = addr | 0x80;
	for (uint8_t i = 0; i < len; i++) {
		txbuf[i+1] = buf[i];
	}
	spi.xmit(len+1, txbuf, 0, rxbuf);
	// Next TX is scheduled after TX_COMPLETE event.
}

static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) {
	txbuf[0] = addr & 0x7f;
	spi.xmit(1, txbuf, len, buf);
}
*/
void do_sleep(){
    kout.pprint(PSTR("naptime\n"));

static void writeOpmode(u1_t mode) {
	u1_t const maskedMode = mode & OPMODE_MASK;
	writeReg(RegOpMode, mode);
    for(int i=0; i<75; i++){
         arch.idle();
    }

static void opmode (u1_t mode) {
	writeOpmode((readReg(RegOpMode) & ~OPMODE_MASK) | mode);
}



bool radio_init()
void onEvent (ev_t ev) {
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            kout.pprint(PSTR("EV_SCAN_TIMEOUT\n"));
            break;
        case EV_JOINING:
            kout.pprint(PSTR("EV_JOINING\n"));
            break;
        case EV_JOINED:
            kout.pprint(PSTR("EV_JOINED\n"));
            #if 1
            {
	// requestModuleActive(1); not required for sx yadayada
	gpio.output(cspin, 1);
#ifdef CFG_sx1276_radio
	gpio.output(rstpin, 0);
#else
	gpio.output(rstpin, 1);
#endif
	arch.delay_ms(1);
	gpio.input(rstpin);
	gpio.write(rstpin, 0); // disable pull-up
	arch.delay_ms(5);
	opmode(OPMODE_SLEEP);

	u1_t v = readReg(RegVersion);
#ifdef CFG_sx1276_radio
	if(v != 0x12 ) {
		kout << "Radio version mismatch: expected " << hex << 0x12 << ", got " << v << endl;
		return false;
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              kout.pprint(PSTR("netid: "));
              kout << dec << netid << endl;
              kout.pprint(PSTR("devaddr: "));
              kout << hex << devaddr << endl;
              //Serial.print(PSTR("AppSKey: "));
              for (size_t i=0; i<sizeof(artKey); ++i) {
                if (i != 0) {
                  //Serial.print(PSTR("-"));
                }
                //printHex2(artKey[i]);
              }
#elif CFG_sx1272_radio
	if(v != 0x22) {
		kout << "Radio version mismatch: expected " << hex << 0x22 << ", got " << v << endl;
		return false;
              kout.pprint(PSTR(""));
              //Serial.print(PSTR("NwkSKey: "));
              for (size_t i=0; i<sizeof(nwkKey); ++i) {
                      if (i != 0) {
                              //Serial.print("-");
                      }
                      //printHex2(nwkKey[i]);
              }
              kout << endl;
            }
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
            #endif
	return true;
            joined = true;
            break;
        case EV_JOIN_FAILED:
            kout.pprint(PSTR("EV_JOIN_FAILED\n"));
            break;
        case EV_TXCOMPLETE:
            kout.pprint(PSTR("EV_TXCOMPLETE (includes waiting for RX windows)\n"));
            // Schedule next transmission
            if (joined) {
                do_sleep();
                do_send(&sendjob);
            } else {
                os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            }
            break;
        case EV_RESET:
            kout.pprint(PSTR("EV_RESET\n"));
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            kout.pprint(PSTR("EV_RXCOMPLETE\n"));
            break;
        case EV_LINK_DEAD:
            kout.pprint(PSTR("EV_LINK_DEAD\n"));
            break;
        case EV_LINK_ALIVE:
            kout.pprint(PSTR("EV_LINK_ALIVE\n"));
            break;
        case EV_TXSTART:
            kout.pprint(PSTR("EV_TXSTART\n"));
            break;
        case EV_TXCANCELED:
            kout.pprint(PSTR("EV_TXCANCELED\n"));
            break;
        case EV_RXSTART:
            /* do not print anything -- it wrecks timing */
            break;
        case EV_JOIN_TXCOMPLETE:
            kout.pprint(PSTR("EV_JOIN_TXCOMPLETE: no JoinAccept\n"));
            break;

        default:
            kout.pprint(PSTR("Unknown event: "));
            kout << (unsigned) ev;
            break;
    }
}

void loop(void)
{
	//gpio.led_toggle(1);
#ifdef TIMER_S
	kout << dec << uptime.get_s() << endl;
#else
	kout << "beep boop" << endl;
#endif
	kout << "VCC  = " << adc.getVCC_mV() << " mV" << endl;
	kout << "Vbat = " << adc.getVBat_mV(false) << " mV" << endl;
	gpio.led_toggle(0);
	os_runloop_once();
	gpio.led_toggle();
}

int main(void)
@@ -121,14 +171,22 @@ int main(void)
	arch.setup();
	gpio.setup();
	kout.setup();
	spi.setup();
	gpio.input(GPIO::pb5);
	gpio.led_on();

	radio_init();
	kout.pprint(PSTR("Hello, World!\n"));
	kout.pprint(PSTR("Test, World!\n"));

	kout << "Hello, World!" << endl;
	kout << "Test, World!" << endl;
	os_init();
	LMIC_reset();
	LMIC_setClockError(MAX_CLOCK_ERROR * 20 / 100);
	LMIC_setAdrMode(0);
	do_send(&sendjob);

	while (1) {
		os_runloop_once();
		gpio.led_toggle();
	}
	arch.idle_loop();

	return 0;
+135 −0
Original line number Diff line number Diff line
# Adding a new region to Arduino LMIC

This variant of the Arduino LMIC code supports adding additional regions beyond the eu868 and us915 bands supported by the original IBM LMIC 1.6 code.

This document outlines how to add a new region.

<!--
  This TOC uses the VS Code markdown TOC extension AlanWalk.markdown-toc.
  We strongly recommend updating using VS Code, the markdown-toc extension and the
  bierner.markdown-preview-github-styles extension. Note that if you are using
  VS Code 1.29 and Markdown TOC 1.5.6, https://github.com/AlanWalk/markdown-toc/issues/65
  applies -- you must change your line-ending to some non-auto value in Settings>
  Text Editor>Files.  `\n` works for me.
-->
<!-- markdownlint-disable MD033 MD004 -->
<!-- markdownlint-capture -->
<!-- markdownlint-disable -->
<!-- TOC depthFrom:2 updateOnSave:true -->

- [Planning](#planning)
	- [Determine the region/region category](#determine-the-regionregion-category)
	- [Check whether the region is already listed in `lmic_config_preconditions.h`](#check-whether-the-region-is-already-listed-in-lmic_config_preconditionsh)
- [Make the appropriate changes in `lmic_config_preconditions.h`](#make-the-appropriate-changes-in-lmic_config_preconditionsh)
- [Document your region in `README.md`](#document-your-region-in-readmemd)
- [Add the definitions for your region in `lorabase.h`](#add-the-definitions-for-your-region-in-lorabaseh)
- [Edit `lmic_bandplan.h`](#edit-lmic_bandplanh)
- [Create <code>lmic_<em>newregion</em>.c</code>](#create-codelmic_emnewregionemccode)
- [General Discussion](#general-discussion)
- [Adding the region to the Arduino_LoRaWAN library](#adding-the-region-to-the-arduino_lorawan-library)

<!-- /TOC -->
<!-- markdownlint-restore -->
<!-- Due to a bug in Markdown TOC, the table is formatted incorrectly if tab indentation is set other than 4. Due to another bug, this comment must be *after* the TOC entry. -->

## Planning

### Determine the region/region category

Compare the target region (in the LoRaWAN regional specification) to the EU868 and US915 regions. There are three possibilities.

1. The region is like the EU region. There are a limited number of channels (up to 8), and only a small number of channels are used for OTAA join operations. The response masks refer to individual channels, and the JOIN-response can send frequencies of specific channels to be added.

2. The region is like the US region. There are many channels (the US has 64) with fixed frequencies, and the channel masks refer to subsets of the fixed channels.

3. The region is not really like either the EU or US. At the moment, it seems that CN470-510MHz (section 2.6 of LoRaWAN Regional Parameters spec V1.0.2rB) falls into this category.

Band plans in categories (1) and (2) are easily supported. Band plans in category (3) are not supported by the current code.

### Check whether the region is already listed in `lmic_config_preconditions.h`

Check `src/lmic/lmic_config_preconditions.h` and scan the `LMIC_REGION_...` definitions. The numeric values are assigned based on the subchapter in section 2 of the LoRaWAN 1.0.2 Regional Parameters document. If your symbol is already there, then the first part of adaptation has already been done. There will already be a corresponding `CFG_...` symbol. But if your region isn't supported, you'll need to add it here.

- `LMIC_REGION_myregion` must be a distinct integer, and must be less than 32 (so as to fit into a bitmask)

## Make the appropriate changes in `lmic_config_preconditions.h`

- `LMIC_REGION_SUPPORTED` is a bit mask of all regions supported by the code. Your new region must appear in this list.
- `CFG_LMIC_REGION_MASK` is a bit mask that, when expanded, returns a bitmask for each defined `CFG_...` variable. You must add your `CFG_myregion` symbol to this list.
- `CFG_region` evaluates to the `LMIC_REGION_...` value for the selected region (as long as only one region is selected). The header files check for this, so you don't have to.
- `CFG_LMIC_EU_like_MASK` is a bitmask of regions that are EU-like, and `CFG_LMIC_US_like_MASK` is a bitmask of regions that are US-like. Add your region to the appropriate one of these two variables.

## Document your region in `README.md`

You'll see where the regions are listed. Add yours.

## Add the definitions for your region in `lorabase.h`

- If your region is EU like, copy the EU block. Document any duty-cycle limitations.
- if your region is US like, copy the US block.
- As appropriate, copy `lorabase_eu868.h` or `lorabase_us915.h` to make your own <code>lorabase_<em>newregion</em>.h</code>.  Fill in the symbols.

At time of writing, you need to duplicate some code to copy some settings from `lorabase_eu868.h` or `lorabase_us915.h` to the new file; and you need to put some region-specific knowledge into the `lorabase.h` header file. The long-term direction is to put all the regional knowledge into the region-specific header, and then the central code will just copy. The architectural impulse is that we'll want to be able to reuse the regional header files in other contexts. On the other hand, because it's error prone, we don't want to `#include` files that aren't being used; otherwise you could accidentally use EU parameters in US code, etc.

- Now's a good time to test-compile and clean out errors introduced. Make sure you set the region to your new target region. You'll have problems compiling, but they should look like this:

    ```console
    lmic.c:29: In file included from

    lmic_bandplan.h: 52:3: error: #error "LMICbandplan_maxFrameLen() not defined by bandplan"
       # error "LMICbandplan_maxFrameLen() not defined by bandplan"

    lmic_bandplan.h: 56:3: error: #error "pow2dBm() not defined by bandplan"
       # error "pow2dBm() not defined by bandplan"
    ```

- If using an MCCI BSP, you might want to edit your local copy of `boards.txt` to add the new region.

- If using an MCCI BSP, you should definitely edit the template files to add the new region to the list.

- Modify the `.travis.yml` file to test the new region.

## Edit `lmic_bandplan.h`

The next step is to add the region-specific interfaces for your region.

Do this by editing `lmic_bandplan.h` and adding the appropriate call to a (new) region-specific file `lmic_bandplan_myregion.h`, where "myregion" is the abbreviation for your region.

Then, if your region is eu868-like, copy `lmic_bandplan_eu868.h` to create your new region-specific header file; otherwise copy `lmic_bandplan_us915.h`.

Edit the file.

Try to compile again; you should now get link errors related to your new band-plan, like this:

```console
c:\tmp\buildfolder\libraries\arduino-lmic\lmic\lmic.c.o: In function `lowerDR':

C:\Users\tmm\Documents\Arduino\libraries\arduino-lmic\src\lmic/lorabase.h:667: undefined reference to `constant_table__DR2RPS_CRC'
```

## Create <code>lmic_<em>newregion</em>.c</code>

Once again, you will start by copying either `lmic_eu868.c` or `lmic_us915.c` to create your new file. Then touch it up as necessary.

## General Discussion

- You'll find it easier to do the test compiles using the example scripts in this directory, rather than trying to get all the Catena framework going too. On the other hand, working with the Catena framework will expose more problems.

- Don't forget to check and update the examples.

- You will also need to update the `boards.template` file for MCCI BSPs, in order to get the region to show up in the Arduino IDE menu.

- You will need to update the [`arduino-lorawan`](https://github.com/mcci-catena/arduino-lorawan) library to include support for the new region. (See [below](#adding-the-region-to-the-arduino_lorawan-library) for instructions.)

- Please increase the version of `arduino-lmic` (symbol `ARDUINO_LMIC_VERSION` in `src/lmic/lmic.h`), and change `arduino-lorawan`'s `Arduino_LoRaWAN_lmic.h` to check for at least that newer version.

- Please also increase the version of the `arduino-lorawan` library (symbol `ARDUINO_LORAWAN_VERSION`).

## Adding the region to the Arduino_LoRaWAN library

In `Arduino_LoRaWAN_ttn.h`:

- Add a new class with name `Arduino_LoRaWAN_ttn_myregion`, copied either from the `Arduino_LoRaWAN_ttn_eu868` class or the `Arduino_LoRaWAN_ttn_us915` class.
- Extend the list of `#if defined(CFG_eu868)` etc. to define `Arduino_LoRaWAN_REGION_TAG` to the suffix of your new class if `CFG_myregion` is defined.

Then copy and edit either `ttn_eu868_netbegin.cpp`/`ttn_eu868_netjoin.cpp` or `ttn_us915_netbegin.cpp`/`ttn_us915_netjoin.cpp` to make your own file(s) for the key functions.
+23 −0
Original line number Diff line number Diff line
MIT License

Copyright (C) 2014-2016 IBM Corporation
Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
Copyright (c) 2016-2021 MCCI Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1375 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading