For the past year I’ve been working on a background project called LoraDeviceLib (LDL for short).
Since this is not my day job, I’ve been focusing on the parts of the problem that interest me (the MAC layer) and avoiding anything that seems tedious (buying a gateway, setting up a gateway, debugging radio/hardware related problems, complaining about late kickstarter projects).
To make this work, I’ve been exercising all features on my development machine using small tests with varying degrees of mocking vs. integration. This is effective up until the point where all the parts are ready to come together to talk to a network service. For me, I got to this point at the beginning of the year when I was ready to start talking to The Things Network (TTN).
To continue with my “no hardware” approach, I decided to write a software layer in Ruby that would emulate the hardware components required to connect a free-running instance of LDL up to TTN. Basically, everything in the box marked “emulator”:
Implementation
The emulator is not one complete program, rather a collection of classes that are composed into a scenario. The classes developed for the emulator live here and everything is arranged in the usual Rubygems pattern.
The classes developed for the emulator look a bit like this:
ExtMAC is a native extension (Ruby written in C) that wraps up LDL.
MAC is a pure Ruby subclass that extends and improves the basic interfaces in ExtMAC. Native extensions are more difficult to write and maintain than pure
Ruby so this pattern helps to keep ExtMAC simple.
Device combines a MAC and a Radio to emulate a LoRaWAN device. It runs an application which is passed in as a block upon initialisation.
Radio contains the minimum amount of logic required to send and receive LoRaWAN frames via an instance of Broker. It can detect overlapping transmit and receive windows but doesn’t go so far as to simulate the effect of distance and obstacles on signal quality.
Gateway is a fully functional LoRaWAN gateway. It implements the Semtech forwarder protocol to connect up to TTN. To avoid dealing with configuration, the gateway will send and receive frames on any frequency. Also, since I’m not simulating radio, every frame relayed upstream has perfect signal quality.
Clock implements a event queue which drives Device and Gateway instances. It ensures that system time stands still until an event is complete. This means that timing sensitive code in LDL is never late even if the garbage collector kicks in.
FrameLogger logs radio frames which it subscribes to via Broker. It includes a packet dissector built from another native extension (not shown).
Broker implements publish/subscribe messaging used to pass frames and events.
Running a Scenario
The first thing I wanted to try on the emulator was a scenario in which the device would join and then send and receive data. In bullet point form:
device starts
device joins
device sends a short unique message upstream as often as duty cycle limits permit
while running, device receives downstream messages in the receive slots
device prints messages to the terminal as it runs so we can see what is happening
Implemented as a scenario:
Running the scenario for a few seconds produces the following lines on the terminal:
The following CSV format lines are logged by FrameLogger:
We can see the same thing on the TTN data console:
Was It Worth The Trouble?
Yes. It’s just as useful as I thought it would be.
The emulator helps me to:
Try out complete instances of LDL against TTN without any physical setup
Iterate through development/debug cycles just as quickly as a non-embedded software project
Conserve the ISM band by not blasting out useless “hello world” messages
Run LDL in different regions (LoRaWAN works differently in EU compared to US, for example)
Setup automated end-to-end tests
It’s not perfect. There are plenty of wrinkles and missing features, the most obvious being that the emulator doesn’t simulate the effect of distance and obstacles on radio signals.
This means that at this time it’s not useful for debugging ADR or for observing the effect of many devices and gateways operating in the same space.