lorawan-device

This is an experimental LoRaWAN device stack. It can be tested by using the
example in the sx12xx-rs repository. You
may also consider the example from
the Drogue Device framework.
The device stack supports two modes, both designed for concurrency in non-threaded environments:
- A state machine implementation based off an article by Ana Hobden.
- An async-await implementation that can be used with async radio interfaces.
State machine implementation
There are two super-states that the Device can be in:
- NoSession: default state upon initialization
- Session: achieved after successful OTAA
A state machine diagram is provided in src/state_machines/session
The following LoRaWAN features are implemented:
- Class A device behavior
- Over-the-Air Activation (OTAA) and Activation by Personalization (ABP)
- Regional support for US915, EU868, and CN470
- Supports CFList in JoinAccept for EU868 and CN470
- the stack starts deriving a new session when the FCnt maxes out the 32-bit
counter; new session may also be created by any time by the user, as long the
stack is not mid-transmit
- MAC commands are minimally mocked, as a ADRReq is responded with an ADRResp,
but not much is done with the actual payload
The following design features are implemented:
- a radio abstraction layer by the following traits defined here:
radio::PhyRxTx + Timings
- the
radio::PhyRxTx
trait enables a state machine design by the implementor
- a pass through for the LoRaWAN crypto abstraction provided by the
lorawan crate, paving the way for secure elements and other hardware
peripherals
- RX windows for data and join accepts are implemented by
Timeouts
which are
passed by Response up to the user, minimizing borrowing or owning of such
bindings by the library
- Timeouts can be adjusted by the radio abstraction layer thanks to the
Timing
trait
This is a work in progress and the notable limitations are:
- Class A behavior only, not B or C
- no retries on Joins or Confirmed packets and the user is instead given
NoAck and NoJoinAccept responses
Async implementation
The async implementation uses the async-await capabilities of Rust to drive the state machine. It
differs from the state machine implementation in the following ways:
- Join, send and send_recv are all async methods that can be awaited until the state transition is
complete.
- When a session is expired, a SessionExpired error will be returned when attempting to send data. A
new call to join must be made to establish a new session.
- The radio implementation is fully async
- A trait for an asynchronous timer is defined and must be implemented to use the async stack
- Uses the RngCore trait for random number generation
In terms of features, the async stack supports the same set of features as the state machine driven
implementation.