LoRaWAN is a wireless communication protocol designed for the Internet of Things. This protocol is desirable for IoT solutions because it has a long range (up to 10km in optimal conditions) and requires a minimal amount of battery power. This longer range allows for the deployment of larger scale projects, such as smart parking structures, agricultural monitoring, tracking weather conditions, asset tracking, etc.
Each LoRa network consists of a "gateway", and one or more "nodes". This network uses a star-based topology, in which each node communicates directly with the gateway. Data can be sent and received through each gateway/node connection. As data is received by the gateway, the gateway can then convert the data to a readable format and send it up to an IoT platform for further processing.
To set up this project, several pieces of hardware will need to be acquired which are listed below in the prerequisites section.
Gateway (Raspberry Pi):
- packet-forwarder - This program runs as a daemon and listens for incoming LoRa packets
- Raspbian - Raspberry Pi operating system
End Node (Adafruit LoRa feather):
Development Machine:
Gateway:
- Raspberry Pi
- 915 MHz antenna
- Gateway Shield (Optional, but highly recommended)
- LoRA Concentrator Module
- Female Header pins
End Node:
- LoRA Feather (Microcontroller)
- Solderless Breadboard
- 2500mAh Lithium Batteries
- Sensors (choose depending on your use case)
Gateway Hardware We'll begin setting up the Gateway by connecting the Raspberry Pi GPIO to the pins on the LoRa concentrator module. This can be done two ways: One method is to connect standard female jumper wires as seen in the diagram below.
Manually wiring the GPIO pins to the concentrator can be a bit difficult, as it's likely to misplace a connection since the hardware is not labeled, and wires can easily be disconnected when moving the gateway. So I opted to use a "shield" instead, which is a PCB that can connect the Raspberry Pi to the LoRa concentrator for a cleaner build. Below we have a photograph of the gateway shield, Raspberry Pi, and LoRaWAN concentrator next to one another
The first step is to solder a pair of 20 pin headers to the bottom, unlabeled side of the gateway shield. As an alternative to soldering, we can use a set of solderless "Hammer" headers which can be found here
Next, place the gateway shield on top of the concentrator module so the pins are going through the shield through-holes, and solder the pins so the connection stays in place.
Now, place the headers on top of the Raspberry Pi like so
*side view*
*top view*
At this point, confirm that all connections are secure, and then plug in a micro USB cable to provide power to your Raspberry Pi. Lights on the Raspberry Pi and the LoRa concentrator should power on.
Gateway Software
Connect to your Raspberry Pi via SSH or directly with a USB keyboard
Next install git, if you haven't already
apt-get install -y git-core
Next, we'll fetch the "packet forwarder" project. This project allows us to compile a binary program that'll allow our Raspberry Pi to listen for incoming LoRa packets, and forward them to a Cloud application. First, clone the project
git clone https://github.com/Lora-net/packet_forwarder
And now build the project dependencies
cd packet_forwarder
./compile.sh
Create a free account on The Things Network console at https://console.thethingsnetwork.org/
Next, log in and navigate to the "Gateways" section. Next, click the "Register Gateway" link.
Then, fill out the required information, a unique gateway ID and a frequency plan. The frequency plan differs by country. Since we're in the US, we'll select 915MHz
After submitting the form, we'll be able to see a set of credentials, which consist of an ID and Key. The Key can be copied to your clipboard using the icon circled below.
These credentials will then need to be placed in a YAML file on the Raspberry Pi at /home/pi/.pktfwd.yml
like so
If successful, this should print the following output
The End node arduino-lmic library requires a set of credentials (Network Session Key and App Session Key) to authenticate each device. These credentials can be generated by going back to The Things Network console. After logging in click on the "Applications" menu selection. Then, click "Add Application".
Here we'll need to provide an Application ID to the form, and then click the "Add Application" button.
Each Application can have one or more associated end nodes, referred to here as "Devices". Navigate to your newly created application, and c,lick "Register Device"
Provide a name as a Device ID, click the icon circled below to randomly generate a Device EUI and then click "Register"
Click the generated device. This view displays the Network Session Key and App Session Key, which we'll need in the next step.
End Node Hardware We can continue on by setting up an end node, which is responsible for reading sensor data and forwarding the packaged data to the gateway.
In this context, each end node consists of a microprocessor, a LoRa radio, an antenna, and one or more sensors.
We opted to use an Adafruit LoRa Feather M0, which is a microcontroller that includes a LoRa radio, Cortex M0 processor, and a USB/battery port.
First, solder the included male headers to both sides of the LoRa Feather.
Next, plug the LoRa feather into a solderless breadboard.
Now, we'll have to solder two additional points. As seen in the diagram below, solder an antenna to the joint with the yellow arrow labeled "antenna" pointing to it (we used a standard copper wire). See this link for more info.
Next, solder a different wire to the joint labeled "dio2", and connect the other end of the wire to the pin labelled "11". Finally, connect a wire from the pin labeled "io1" to the pin labeled "6".
Once this is complete, connect a micro-usb cable from our laptop to the LoRa feather node to supply power.
End Node Software
Now we'll need to carry out a few more configuration steps to allow our node to send data to the gateway
Install and configure the Arduino IDE
Begin by visiting the following link on the Arduino web here, scroll down to the "Download the Arduino IDE", and click the corresponding link for your operating system. Unzip the downloaded archive file, and open the resulting "Arduino" application.
Next, open the "Preferences" menu.
We'll need to add support to the Arduino IDE for the Adafruit LoRa feather by adding this url https://adafruit.github.io/arduino-board-index/package_adafruit_index.json to the "Additional Boards Manager URLs" section
We'll also need to add "SAMD" support which is the architecture utilized by this particular microcontroller. Navigate to the "tools" menu, hover over the "Boards" options, and then click "Boards Manager".
In the search bar, enter "Arduino SAMD", select the resulting package, and click "Install".
Next, enter "Adafruit SAMD" in the search bar, and then select the package titled "Adafruit SAMD Boards". Ensure that the Adafruit Feather M0 is listed as a supported board, and click "Install"
Close and re-open the Arduino application.
Next we'll need to download the LMIC (LoraMAC-in-C) library and edit the included configuration file.
Download the arduino-lmic
library with the following command
git clone https://github.com/matthijskooijman/arduino-lmic
(USA only) Before loading this library to our gateway node, we'll need to adjust the configuration file at arduino-lmic/src/lmic/config.h
to transmit signals using the standard American LoRaWAN frequency (915MHz). This can be done by simply commenting out the 8th line containing the CFG_eu868
value, and ensuring the CFG_us915
line is set like so. This step should be skipped if you are located within the EU.
Use the Arduino IDE to flash the LMIC library onto the node
Install the arduino-lmic
library by navigating to the "Sketch" menu, hover over the "Include Library" selection, and then click "Add .zip library".
Select the cloned arduino-lmic
folder and then click the "Choose" button
Next, we'll need to take the Device credentials (Network Session Key and App Session Key) and place them into an Arduino "sketch". A sketch is a C code snippet that runs in a loop on the node. This snippet defines node behavior, and can place the node in "deep sleep" mode at a given interval, define input/output pins to read and write to sensors, listen for updates from the gateway, etc.
In this example, we'll use the sketch included in our cloned arduino-lmic repository at arduino-lmic/examples/ttn-abp/ttn-abp.ino
. This sketch repeatedly transmits a string "Hello World" to a nearby LoRa gateway. The Network Session key should be inserted as a value to the NWSKEY variable, and the Application Key set as the APPSKEY variable, as seen below
Finally, update the values in the lmic_pins struct as seen below. This struct tells the LMIC library how to access the LoRa radio module.
const lmic_pinmap lmic_pins = {
.nss = 8,
.rxtx = LMIC_UNUSED_PIN,
.rst = 4,
.dio = {3,6,11} // dio0, dio1, dio2
};
Now, we can test that everything has been set up properly by sending a "Hello World" string from our LoRa node to our gateway. Upload the updated "sketch" to the node by clicking the button in the upper section of the Arduino IDE. Once successfully uploaded, this snippet will repeatedly run in a loop.
Finally, open "The Things Network" console to confirm that data is successfully being sent from the node to the gateway. We should see a view with a timestamp and payload of bytes
We can view the original payload by using the standard "Buffer" library in node.js like so
Now that we've verified that a basic "Hello world" string can be published from our node to the gateway, let's update our node to read and publish values from a sensor. To do this, we can plug a sensor into the breadboard, connect its negative/positive pins to the breadboard power rails, and its data pin to one of the feather “Analog input” pins (6 in total labeled like A0, A1, etc). In this example, I’ll use a sound sensor.
To test the sensor, create a simple sketch in which the “loop” method constantly prints out the sound sensor value to Arduino’s serial monitor. This can be done like so.
String soundPin = "A0";
void setup() {
// put your setup code here, to run once:
pinMode(soundPin, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
soundValue = constrain(analogRead(soundPin), 0, 255);
Serial.println(F(soundValue));
}
After verifying that the sensor was able to successfully detect sound levels, the next step is to publish the sensor values to the gateway, so they could be aggregated and analyzed. Each published packet can have a maximum size of 52 bytes (8 bits), and each byte can contain a value in the range of 0–255. ASCII strings take up a lot of bandwidth, (“Hello, World” was 26 bytes!), so it’s best to send a buffer of binary values instead of text or JSON objects.
Since this particular sound sensor has low precision unsigned values (constrained range from 0 to 255), we can fit each value in a single byte. To do this, we’ve adjusted the do_send
method on line 143 of the ttn-abp-sensor.ino sketch to initialize a byte array, run through a "for" loop and append sensor values to the array during each iteration. We've set the sampling interval to measure the sensor level once every second, and append the average sound level to the byte array. Once the byte array is the maximum permitted size (52 bytes), it will be published to the gateway
After flashing the updated sketch to the board, we can see a different set of incoming values in the TTN console, which takes the form of hex values. So now we’re able to publish sensor values at periodic intervals to an internet-connected service. And to access the data without going through a web application, we can use a MQTT client to subscribe to all incoming values published to our TTN account. For example, we can use the Node.js MQTT client cli like so.
mqtt_sub -h us-west.thethings.network -p 1883 -u "${TTN_APPLICATION}" -t "${TTN_APPLICATION}/devices/${TTN_DEVICE}/up" -P "${TTN_ACCESS_KEY}"
Now that we can publish and receive sensor data from the TTN service, our final portion of this pattern shows how to parse the sensor values and publish them to the Watson IoT Platform
First, you’ll need to provision a Watson IoT service here, and a Cloudant NoSQL db here. Once those are up and running, go to the IoT dashboard, click the "Extensions" option in the sidebar, and enable the “Historian Data Storage” extension. This allows all incoming data received by the platform to be archived in the associated Cloudant database.
Also, create a set of MQTT credentials that can be used to publish sensor data to the platform with the following steps
Enter the IoT Platform dashboard, select "Devices" from the left-hand menu, and then click the "Add Device" button
Next, provide a device type and ID.
The next two steps (Device Information, Groups) can be skipped.
In the "Security" tab, an Authentication token can be entered as long as it meets certain criteria (between 8 and 36 characters, contains mix of lowercase/uppercase letters, numbers, and symbols). Leave this field blank if you'd like for one to be generated instead.
Clicking the "Finish" button will generate a set of credentials that can be used to publish messages to the IoT Platform
Now, MQTT publish commands can be made from a device in the following format
Client ID: d:${organization_id}:${device_type}:${device_id}
Username: use-token-auth
Password: ${authentication_token}
Endpoint: ${organization_id}.messaging.internetofthings.ibmcloud.com
Place these values in the "Watson IoT platform credentials" section in the ttn-to-watson.js
script. Also add the Device Id, Application Id, and Access key from the TTN console. Once the credentials for both the Watson IoT platform and "The Things Network" have been placed in the script, start the script up with the following steps
cd scripts
npm install underscore
npm install -g mqtt
npm ttn-to-watson.js
Each individual sensor reading will be published to the Watson IoT Platform as a JSON object. In our case, each of our messages take the following format
{
"d": {
"sound": 83,
"timestamp": "2018-02-07T17:52:49.398Z"
}
}
As each batch of our sensor values are published, we leverage one of the Watson IoT features to create a time-series visual, called a “board”. This can be created by going to the platform dashboard and clicking “Create New Board”
This will present a form asking for the event name and value(s) to be plotted
Once that form has been filled out, we can view the archived data by clicking the date, unchecking the “real-time” radio button, and selecting the date/time range. Since this data payload was published to TTN on 2/7/18, we can adjust the date range to look between the 6th to the 8th of February, and zoom in using the slider icons.
Additional information and troubleshooting steps for the Adafruit feather can be found on the official site here
This code pattern is licensed under the Apache Software License, Version 2. Separate third party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the Developer Certificate of Origin, Version 1.1 (DCO) and the Apache Software License, Version 2.