This post is now out of date after a number of MBED wrapper related bugs were fixed in 0.4.2. Long story short the EventQueue caused all kinds of problems that took a while to track down. The updated examples are similar to the one in this post, the only change being the EventQueue instance is no longer required.

Version 0.4.0 of LDL has been released and it includes an updated MBED wrapper with support for CMWX1ZZABZ based hardware.

The CMWX1ZZABZ module combines:

  • SX1276 transceiver
  • three IO lines for RF switching (RFI, RFO, PA_BOOST)
  • TCXO with power on/off control
  • a STM32L072CZ MCU (192K flash, 20K RAM)
  • 32768Hz XTAL for the MCU

If you want to evaluate this module on MBED the best development kit in my opinion is the DISCO_L072CZ_LRWAN1.

B-L072Z-LRWAN1

This post shows how to use LDL 0.4.0 on MBED with the DISCO_L072CZ_LRWAN1 development kit. It also points out some noteworthy practical features of the wrapper.

Importing the Wrapper

The wrapper can be added to a project with the following mbed-cli command:

cd <your mbed project>
mbed add https://github.com/cjhdev/lora_device_lib

The wrapper source is not in the root of the LDL repository but MBED will find it without any special steps.

Demo App

This demo is much like every other I have written for LDL. It performs over-the-air-activation before dropping into a loop that uses a data service to send “hello world” as often as possible.

This app must be compiled using the MBED bare-metal profile since the RTOS profile doesn’t work so well when you only have 20K of RAM.

/* mapping suitable for DISCO_L072CZ_LRWAN1 */

#include "mbed_ldl.h"

SPI spi(PA_7, PA_6, PB_3);

LDL::CMWX1ZZABZ radio(
    spi,
    PA_15,      // nss
    PC_0,       // reset
    PB_4, PB_1, // DIO0, DIO1
    PC_1,       // enable_boost
    PC_2,       // enable_rfo
    PA_1,       // enable_rfi
    PA_12       // enable_tcxo
);

const uint8_t app_key[] = MBED_CONF_APP_APP_KEY;
const uint8_t nwk_key[] = MBED_CONF_APP_NWK_KEY;

LDL::DefaultSM sm(app_key, nwk_key);

const uint8_t dev_eui[] = MBED_CONF_APP_DEV_EUI;
const uint8_t join_eui[] = MBED_CONF_APP_JOIN_EUI;

LDL::DefaultStore store(dev_eui, join_eui);

EventQueue events;

LDL::MAC mac(events, store, sm, radio);

int main()
{
    mac.start(LDL_EU_863_870);

    for(;;){

        if(mac.ready()){

            if(mac.joined()){

                const char msg[] = "hello world";

                mac.unconfirmed(1, msg, strlen(msg));
            }
            else{

                mac.otaa();
            }
        }

        mac.process();
    }

    return 0;
}

Configuration File (mbed_app.json)

I’ve used the configuration file to define EUIs, define keys, and enable tracing. The LDL wrapper itself doesn’t require you to define anything in the configuration file. I’m defining keys and EUIs here because it’s convenient.

{
    "requires": ["bare-metal", "ldl", "events", "mbed-trace"],
    "config" : {

        "app_key" : {
            "value" : "{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}"
        },
        "nwk_key" : {
            "value" : "{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}"
        },
        "join_eui" : {
            "value" : "{0,0,0,0,0,0,0,3}"
        },
        "dev_eui" : {
            "value" : "{0,0,0,0,0,0,0,4}"
        }
    },
    "target_overrides": {
        "*": {
            "platform.stdio-baud-rate": 115200,
            "target.c_lib": "small",
            "mbed-trace.enable": 1,
        },
        "DISCO_L072CZ_LRWAN1" : {

            "ldl.default-rate" : 0
        }
    }
}

Noteworthy Features

Radio Driver Subclassing

The LDL wrapper lets the developer implement accessory IO by way of subclassing the basic radio driver.

In the case of the CMWX1ZZABZ module there are four accessory IO lines that have nothing to do with the SX1276 driver:

  • TCXO on/off
  • receive enable
  • RFO enable
  • Boost enable

The LDL::CMWX1ZZABZ class is able to handle these lines correctly without any changes being made to the common driver code.

Security Module Subclassing

Developers with particular security requirements (e.g. a requirement to use a security chip) can implement them by subclassing the abstract security module (LDL::SM).

The LDL::DefaultSM subclass is provided for demo apps like the one in this post.

const uint8_t app_key[] = MBED_CONF_APP_APP_KEY;
const uint8_t nwk_key[] = MBED_CONF_APP_NWK_KEY;

LDL::DefaultSM sm(app_key, nwk_key);
  • It uses the cryptographic code packaged with LDL
  • It is constructed with pointers to the root keys
  • It stores derived keys in volatile memory

Data Store Subclassing

LoRaWAN devices need to persist counters, identifiers, and session state. Developers with particular storage requirements can implement them by subclassing the abstract data store (LDL::Store).

The LDL::DefaultStore subclass is provided for demo apps like this one in this post.

const uint8_t dev_eui[] = MBED_CONF_APP_DEV_EUI;
const uint8_t join_eui[] = MBED_CONF_APP_JOIN_EUI;

LDL::DefaultStore store(dev_eui, join_eui);
  • you pass it the identifiers in the constructor
  • it stores devNonce and joinNonce in volatile memory
  • it doesn’t store session

Compiled Size

Using arm-none-eabi-gcc 8.28 and the debug bare-metal profile:

| Module                   |     .text |   .data |     .bss |
|--------------------------|-----------|---------|----------|
| [fill]                   |    90(+0) |   0(+0) |   23(+0) |
| [lib]/c_nano.a           |  5074(+0) | 100(+0) |   21(+0) |
| [lib]/gcc.a              |  1996(+0) |   0(+0) |    0(+0) |
| [lib]/misc               |   200(+0) |   4(+0) |   28(+0) |
| [lib]/nosys.a            |    32(+0) |   0(+0) |    0(+0) |
| lora_device_lib/src      | 19502(+0) |   0(+0) |    0(+0) |
| lora_device_lib/wrappers |  4002(+0) |   0(+0) |    0(+0) |
| main.o                   |   472(+0) |   0(+0) | 1744(+0) |
| mbed-os/drivers          |  1568(+0) |   0(+0) |  532(+0) |
| mbed-os/events           |  1698(+0) |   0(+0) |    0(+0) |
| mbed-os/features         |  1188(+0) |  64(+0) |    0(+0) |
| mbed-os/hal              |  2122(+0) |   8(+0) |  130(+0) |
| mbed-os/platform         |  6166(+0) | 260(+0) |  428(+0) |
| mbed-os/rtos             |   332(+0) |   0(+0) |    0(+0) |
| mbed-os/targets          | 14926(+0) |   4(+0) | 1230(+0) |
| Subtotals                | 59368(+0) | 440(+0) | 4136(+0) |
Total Static RAM memory (data + bss): 4576(+0) bytes
Total Flash memory (text + data): 59808(+0) bytes