Arduino LMIC library updated

(Matthijs Kooijman) #1

Quite some time ago, I started out an Arduino port of the LMIC library. Initially, it was just a proof of concept for node->node communication, but a few months ago @Thomas and @popcorn helped to get proper LoRaWAN support working as well. @Thomas has been providing a working version for a while, but in the past few weeks I've updated my version of the library, incorporating some of his changes, as well as a lot of other fixes, improvements and more complete documentation.

The library should, in theory, work on AVR-based arduinos as well as other architectures, though I have not tested with any non-AVR hardware with this particular version of the library yet. Feedback about this is welcome (also positive feedback if the code is working on your hardware)!

The library can be found here:

Test module lora sx1272
(Matthijs Kooijman) #2

Note that this library currently fits very tightly on an Arduino Uno / 328p, but this will likely be improved in a next version by exchaning the AES implementation. See this other topic for some more info on this effort.


I am waitng for this very cheap Maple Mini sort of clone

Chip ATmega328 (Uno etc) |STM32F103C8T6
Speed 16MHz |72MHz
Flash 32 KB |128 KB
RAM 2 KB |20 KB
Power 5V |3

(don't know how to 'format this.. sorry :smile: )

(Addy de Jongh) #4

Thanks for making this available. Unfortunately I get a compiling error like this:

In function 'u4_t hal_ticks()':
D:\Addy\Documenten\Arduino\libraries\arduino-lmic-master\src\hal\hal.cpp:154:115: error: 'static_assert' was not declared in this scope

What is wrong here?

Thanks is advance for your answer.

(Matthijs Kooijman) #5

As mentioned by e-mail as well, you'll need to use Arduino 1.6.6 or above, which enables C++11 and C99 support (at least for the regular Arduino cores, third-party cores might be different).


@matthijs : compiling with Arduino-1.6.7 / Teensyduino for Teensy-3.2 not successful
compiling output as PM
for an UNO : OK

(Matthijs Kooijman) #7

Right, it seems that Teensyduino does not have C99 mode enabled, like the regular Arduino core has since 1.6.6. I've contacted Paul (Teensy maintainer) about this, I'll keep you posted. For now, you can probably fix this by finding the Teensy platform.txt, looking for a line starting with compiler.c.flags and adding the -std=gnu11 op;tion (this enables C11 mode with GNU extensions, which includes C99).

A second problem is that the printf support I used turns out to be AVR-specific. You can either disable it in config.h, or try this branch (I'll merge this to master once someone confirms printf works on Teensy or some other non-avr platform with it).


@matthijs I used also the branch non-avr-print. no luck

(Matthijs Kooijman) #9

I've just pushed and tagged an update that has the printf support disabled by default (but still has assertion failure enabled, which no longer coupled to the printf support). This fixes half of the problem for Teensy, for the C99 thing I've contacted Paul to see if he can change that.

(Arjan) #10

@matthijs, did you ever try to understand the DO_DEVDB macro?

I wondered if frame counters survive Arduino restarts, but they don't. This might soon become troublesome, as per Security in LoRaWAN and TTN in the staging wiki:

[...] you should realize that these frame counters reset to 0 every time the device restarts (when you flash the firmware or when you unplug it). As a result, The Things Network will block all messages from the device until the FCntUp becomes higher than the previous FCntUp. Therefore, you should re-register your device in the backend every time you reset it.

When a counter increments, it seems LMiC allows for storing it in some "device database" (beware: my own terminology). Like for LoRaWAN's FCntUp:

LMIC.seqnoUp += 1;

The same macro is used after Over-the-Air Activation, or for adaptive date rate. However, DO_DEVDB is a no-operation in oslmic.h:

#define DO_DEVDB(field1,field2) /**/

(Which, I think, is also why this code even compiles, as the bare seqnoUp is not even defined?)

But maybe my guess about the meaning of the DO_DEVDB macro is wrong. First, for a database, I'd have expected LMiC to define that in the HAL, not in oslmic.h. Also, I don't see how the values are ever read back from such database, in the IBM code. And the LMiC documentation states:

2.5.4 void LMIC_setSession (u4_t netid, devaddr_t devaddr, u1_t* nwkKey, u1_t* artKey)

Set static session parameters. Instead of dynamically establishing a session by joining the network, precomputed session parameters can be provided. To resume a session with precomputed parameters, the frame sequence counters (LMIC.seqnoUp and LMIC.seqnoDn) must be restored to their latest values.

I guess that implies one should store the counters in the EV_TXCOMPLETE event. And when doing so, the following might be relevant too:


Session reset due to rollover of sequence counters. Network will be rejoined automatically to acquire new session.

Reading the LMiC documentation makes me think this is only a problem for Activation by Personalization (as used in the example code). But I've not checked if Over-the-Air Activation automatically resets the counters in the TTN backend.

New backend. How to connect?
(Matthijs Kooijman) #11

I haven't actually noticed it before, to be honest :smile:

I agree with your analysis, though - the macro seems to be intended for storing the frame counter, but I suspect it is used for internal debugging purposes or something, since it is not exposed as a proper API currently. Storing the counter in TX_COMPLETE seems sensible to me, though that's not currently implemented in the library or examples.

AFAIU the spec, it should reset the counters, since that's pretty much the only way to handle rollover currently. Note that it should not just reset the counters, but also allocate a new key (which is why resetting the counters does not hurt security). For personalized devices, I guess the same could happen by re-registering a device in the TTN backend, but then the burden of actually changing the key is on whoever does the registration.


I have checked your question/assumption about the counters and OTAA. If I restart the LMIC code (a port to RaspberryPi) while using OTAA, the device gets new keys (as can be seen in the ttnctl devices info DEVEUI output) and the counters are reset to 0.
So no need to store the counters with OTAA.

(Arjan) #13

You mean the counters in the TTN backend? (I knew they are reset in the node; I just didn't check if they are reset in the TTN backend too. But indeed, I agree with matthijs that they should be reset in the backend as well; if not then that would be a bug.)


They are reset correctly in the TTN backend.


@matthijs: I just joined a Ideetron Nexus with OTAA using your library without much trouble (once I figured out which bits go where, reversed or not), so that seems to be working (in case you didn't know yet).

Had to DISABLE_PING and/or DISABLE_BEACONS to fit it in the Nexus.

And getting non-zero FCntUp in ttnctl devices info :grinning:

Edit: downlink works, too!

Edit2: after a while I get EV_LINK_DEAD, followed by EV_REJOIN_FAILED (SF increments each time this happens), but everything keeps working, FCntUp increments, EV_TXCOMPLETEs, and uplink messages keep showing up in mqtt.

(Matthijs Kooijman) #16

@melvin, cool, I hadn't actually tested OTAA so far.

I also have a Nexus lying around here, waiting for a testrun. Could you share the the pin map from your sketch so I can include it as an example for the Nexus?

I suspect that using OTAA pulls in additional code from the LMIC library, pushing the flash size over the limit. If we replace the AES implementation (which is waiting for a license change), then more space should become available (see

Not sure what happens there, perhaps ACKs aren't being sent by TTN, which makes LMIC think the link is dead? I can't remember if the staging backend already supports ACKs.

(Hylke Visser) #17

Yes, the backend supports ACKs, but apparently we're sending them on the wrong port, see this issue on github.



const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET
    .dio = {4, 5, 7},

Want to get started with NEXUS, don't know were to begin

Has anyone been able to get this running using It works for me in the Arduino IDE, but not as soon as I use the arduino-mk - I added the CFLAGS_STD = -std=c99 setting, but no difference.


@joscha : if I remember correctly, I had to add in boards.txt to each type of teensy; because it was empty.
probably that helps