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.
LoRa for the Internet of Things, Martin Bor, John Vidler, and Utz Roedig, MadCom 2016.
Download LoRaBlink (968 kB, v1.0.0, last updated 14 December 2015)
Access source code via the GitHub repo
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.
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.:
pacman -S arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib
apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi binutils-arm-none-eabi
For flashing, you need to install either dfu-util, openocd or stlink, e.g.:
pacman -S dfu-util
apt-get install dfu-util
pacman -S openocd
apt-get install openocd
pacman -S stlink
Flashing the XRange can be done in 3 different ways.
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
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.
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 of the HAL and runtime can be found in doc/
.
More examples can be found in examples/
.
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 Martin Bor. 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
Martin Bor m.c.bor@uva.nl