LoRaWAN™

LoRa is a low-power radio technology that allows users to deploy devices that may last a decade on a tiny battery, with LoRaWAN being the most common network layer. It's unique in that it uses "chirps" to encode 1s and 0s, which allows it to be read in a noisy environment while using little power over a long distance.

To connect LoRaWAN devices to Qubitro, the network service provider The Things Network (TTN) is used, this lets you either deploy your own gateway or use public gateways other people deployed around you. Any gateway on the TTN network that receives your device's data will forward it to Qubitro and even format the data. To start making an account in the cluster closest to your area:

After logging in and creating a new application, you have the ability to add a device to the network with both the ability to select a preset through the repository or a manual setup. Following that TTN requests a frequency plan, AppEUI, DevEUI, AppKey, and device ID.

Device registration
  • Frequency plan - Make sure to select a plan in the frequency band allowed in your country which should match the gateway and device. More info can be read here.

  • AppEUI - This identifier allows the application to be identified, unless the vendor of your device sets this for you, press the 00 button to the right of the input box to zero this out. TTN will assign one to you.

  • DevEUI - If working with a company, it is required to register a MAC block, otherwise, this can be random with the last few bytes incrementing for future devices you deploy

  • AppKey- The application key assists with encryption and authentication of your data so that in routing it won't be in cleartext. Go ahead and press the random button to the right of the input to be copied later.

Once the device was created, the device page is available in the application's end devices tab. Here all of the information to copy and paste into your new node is available with buttons on the right of the input box for showing the variables. Additionally next to the show button, there is a <> which encodes the EUI as a C-style byte array for easy copy and paste into code.

TTN Device Page

How to configure the device with the required EUIs is specific to the hardware vendor and framework, however, most Arduino-based devices will have an example code with the arrays to paste into at the top of the code as shown below. Note that, polished LoRaWAN products which are ready to deploy out of the box may have a setup guide that should be followed for configuration.

Example Arduino Implementation of EUI & key assignment

Adding Qubitro Integration

To connect a TTN application to Qubitro, on the applications page, there is a tab for integrations with webhooks as a subpage. After pressing add device in the upper right, Qubitro is listed as a template that can be selected.

Below is the next page you'll see, where you can add a name for the webhook (The ID), your Qubitro project ID, and a webhook signing key.

Configuring Qubitro Webhook

The web hook signing key can be found on the Qubitro website under your account, in the API Keys section, with the project ID under the settings for the project you wish to add the LoRaWAN application to. Both are pictured below. Once these are added to the TTN website, any message sent from a LoRaWAN device in that application will be forwarded to Qubitro. If you add a new device to the TTN application, once it first connects it will appear within the Qubitro project automatically.

Payload Formatting

One disadvantage of LoRaWAN is the packets take a while to send, and this limits us in how much data we can transmit, we don't want to hog the airspace, and we want to save battery. The Things Stack provides a handy tool in the application page (or device page for per device formatting) so we can take the packet format of the end device, and encode the data in a JSON format Qubitro expects.

The first thing to do would be make a basic packet specification for your device, if I were running a temperature and humidity sensor, I would say I want two floats, with the temperature first, followed by humidity. In the Arduino framework, floats are 32-bit, so the first four bytes of the packet bytes[0]-bytes[3] is the temperature, and the following four bytes bytes[4]-bytes[7] is humidity. To decode we can use the following decoder-

function decodeUplink(input) {
var data = {};
var events = {
1: "setup",
2: "interval",
};
function bytesToFloat(bytes) {
// Taken from SO, sorry for not linking, but we need to do this because js is weird
var bits = bytes[3]<<24 | bytes[2]<<16 | bytes[1]<<8 | bytes[0];
var sign = (bits>>>31 === 0) ? 1.0 : -1.0;
var e = bits>>>23 & 0xff;
var m = (e === 0) ? (bits & 0x7fffff)<<1 : (bits & 0x7fffff) | 0x800000;
var f = sign * m * Math.pow(2, e - 150);
return f;
}
// Get temp from packet
data.temp = bytesToFloat(input.bytes.slice(0,4));
// Get humidity from packet
data.humidity = bytesToFloat(input.bytes.slice(4,8));
return {
data: data,
warnings: [],
errors: [],
event: events[input.fPort]
};
}

The bytesToFloat() is due to JS being a little strange, but it allows us to take the first and last four bytes of the packet, and turn them into floats for JSON encoding. Another variable type you may want to send is a unsigned integer, these can be 8-64-bit (Possible to be more/less, just whats common) values taking 2 to 8 bytes to send. Below are examples to decode them, however keep in mind they are MSB first, meaning the most signifigant bit of the binary number is the first byte. Its important when working with byte protocols to be aware of which order the bytes represent a value.

function decodeUplink(input) {
var data = {};
var events = {
1: "setup",
2: "interval",
};
// 8-bit value can represent 0-255
data.counter = input.bytes[0] << 8;
// 16-bit value can represent 0-65535
data.input_mv = (input.bytes[1] << 8) | input.bytes[2];
// 32-bit value can represent 0-4294967295
data.input_mv = (input.bytes[1] << 20) | (input.bytes[1] << 16) | (input.bytes[1] << 8) | input.bytes[2];
return {
data: data,
warnings: [],
errors: [],
event: events[input.fPort]
};
}

Another handy tool in packet formatting will be flags, a byte composed of flags lets you transport eight boolean values at once, this can be handy for packets which may be a little dynamic.

function decodeUplink(input) {
var data = {};
var warnings = [];
var events = {
1: "setup",
2: "interval",
};
// We're using 0x1 or 0b0000001 as a mask, if there is a value in the mask, we have a number greater than 0 meaning the boolean is set
if ((input.bytes[0] & 0x1) > 0) {
warnings.append("Oh no we got alerted to something critical!");
}
// 0x2 aka 0b0000010
if ((input.bytes[0] & 0x1) > 0) {
// Decode the rest of the packet differently?
}
// 0x4 aka 0b0000100
if ((input.bytes[0] & 0x1) > 0) {
// Set data.temp to be the bytes at the end of the packet?
}
return {
data: data,
warnings: warnings,
errors: [],
event: events[input.fPort]
};
}