LoRaBlink Kit

NOTE: This website has now been moved to GitHub.

This kit contains a HAL, runtime, radio drivers and example code to program a LoRa (Semtech SX1272/SX1276) based sensor node. The framework is derived from IBM LMiC. Some of the changes from LMiC are the removal of LoRaWAN, providing direct access to the radio, supporting node-to-node communication, enabling CRC checks and exposing RSSI and SNR register values for received packets. LoRaBlink, a simple TDMA-style tree collection protocol, is build on top of this.

Contents

LoRa for the Internet of Things, Martin Bor, John Vidler, and Utz Roedig, MadCom 2016.

Download

Download LoRaBlink (968 kB, last updated 14 December 2015)

Platforms

LoRaBlink is tested on the NetBlocks XRange SX1272 LoRa RF module. The original LMiC support for the Semtech SX1276MB1 and IMST/WiMod LoRa Radio Starter kit is still available, but hasn’t been tested. Any other platform that has a STM32L151 and Semtech SX1272/SX1276 should also work, though you may need to change some pin definitions. For any other platform, have a look at stm32/ to see how to implement a HAL layer for your platform.

Getting Started

Toolchain

For compiling the code, all you need is the standard gcc-arm-embedded, with corresponding binutils and newlib. Either downloaded or install the package appropriate for your operating system, or use the package manager of your favourite distro, e.g.:

For flashing, you need to install either dfu-util, openocd or stlink, e.g.:

Flashing

Flashing the XRange can be done in 3 different ways.

DFU mode

The STM32 has a built-in bootloader that shows up as an USB DFU device. To start the node in DFU mode, set BOOT0 high (for example connect a jumper wire from BOOT0 to Vout 3V) and plug in the node. The node should show up as a DFU device:

$ dfu-util -l
dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

Found DFU: [0483:df11] ver=2200, devnum=8, cfg=1, intf=0, alt=2, name="@DATA Memory /0x08080000/02*004Ke", serial="64A18F335E5A"
Found DFU: [0483:df11] ver=2200, devnum=8, cfg=1, intf=0, alt=1, name="@Option Bytes  /0x1FF80000/01*032 e", serial="64A18F335E5A"
Found DFU: [0483:df11] ver=2200, devnum=8, cfg=1, intf=0, alt=0, name="@Internal Flash  /0x08000000/1024*256 g", serial="64A18F335E5A"

Then, to flash the code run:

$ make flash-dfu

JTAG/SWDIO

To flash the XRange via SWDIO, connect a ST/LINK-V2 to the SWD port on the XRange via an ARM-JTAG-20-10 adapter. Flashing can be done either with stlink or OpenOCD.

$ make flash-stlink

Or with OpenOCD:

$ make flash-ocd

Note that when flashing with OpenOCD, the node needs to be power cycled (unplugged and replugged) to work.

Output

On the XRange, a debug LED should be connected to PC8/MCO. Serial debug output defaults to UART1 (PC6/PC7). Connect a Serial-to-USB convert (TTL) to these pins and configure a terminal for 115200 8-N-1.

Documentation

Documentation of the HAL and runtime can be found in doc/.

Examples

More examples can be found in examples/.

Hello world

Toggles the debug led and prints out ‘Hello, world!’ every second.

#include "enzo.h"
#include "debug.h"

// counter
static u2_t counter = 0;

// log text to USART and toggle LED
static void initfunc (osjob_t* job) {
    // say hello
    debug_str("Hello World!\r\n");
    // log counter
    debug_val("counter = ", counter);
    // toggle LED
    debug_led(++counter & 1);
    // reschedule job every second
    os_setTimedCallback(job, os_getTime()+sec2osticks(1), initfunc);
}

// application entry point
int main () {
    osjob_t initjob;

    // initialize runtime env
    os_init();
    // initialize debug library
    debug_init();
    // setup initial job
    os_setCallback(&initjob, initfunc);
    // execute scheduled jobs and events
    os_runloop();
    // (not reached)
    return 0;
}

Program one node as node 0, which will act as a root node. Program another as node 1. The node 1 will send a ‘ping’ messages every minutes once it gets sync.

#include "enzo.h"
#include "debug.h"
#include "blink.h"

#define NODEID 1

// fwd decl
static void ping(osjob_t *job);
static void initfunc(osjob_t *job);

static u1_t counter;
static osjob_t pingjob;

void on_event(event_t ev) {
  switch(ev) {
    case EVENT_SYNC:
      debug_str("got sync\r\n");
      os_setCallback(&pingjob, FUNC_ADDR(ping));
      break;
    case EVENT_LOST_SYNC:
      debug_str("lost sync\r\n");
      os_clearCallback(&pingjob);
      break;
    case EVENT_RXCOMPLETE:
      debug_str("rx complete\r\n");
      u1_t payload[MAX_LEN_PAYLOAD];
      blink_rx(payload, MAX_LEN_PAYLOAD);
      debug_buf(payload, MAX_LEN_PAYLOAD);
      break;
    case EVENT_TXCOMPLETE:
      debug_str("tx complete\r\n");
      break;
    default:
      // nop
      break;
  }
}


static void ping(osjob_t *job) {
  debug_val("ping ", counter);
  u1_t payload[6] =  {'P', 'i', 'n', 'g', ' ', counter++};
  blink_tx(payload, SIZEOFEXPR(payload));
  os_setTimedCallback(job, os_getTime() + sec2osticks(5), FUNC_ADDR(ping));
}

static void initfunc(osjob_t* job) {
  BLINK.nodeid = NODEID;
  blink_reset();
  blink_start_sync();
}

int main(void) {

  osjob_t initjob;

  // init runtime
  os_init();
  // init debug lib
  debug_init();
  // init blink
  blink_init();
  // setup initial job
  os_setCallback(&initjob, initfunc);
  // execute scheduled jobs and events
  os_runloop();
  // (not reached)
  return 0;
}

LMiC - Copyright (c) 2014-2015 IBM Corporation. LMiC is published under the terms of the Eclipse Public License v1.0, which is available at http://www.eclipse.org/legal/epl-v10.html

LoraBlink Kit - Copyright (c) 2015 Lancaster University. As derivative works of LMiC, LoraBlink Kit is also published under the Eclipse Public License v1.0, which is available at http://www.eclipse.org/legal/epl-v10.html

Contact

Martin Bor m.bor@lancaster.ac.uk