The LoRaWAN ecosystem continues to grow, and so does the length of the decoders needed to transform binary or Base64 payloads into friendly JSON ones. Copy/pasting such decoders can get confusing for two reasons:
Some of them have hundreds of lines of code — and continue to grow as more sensors and features are added to the devices.
Most —if not all— LoRaWAN vendors offer their decoders in NodeJS, making them incompatible if you're developing your functions in Python.
Because of this, we created a public DaaS (Decoder as a Service) endpoint.
Requirements
An Ubidots active account
A NodeJS code hosted publicly
Table of Contents
Troubleshooting
Feedback, suggestions and related articles
1. Understanding manufacturer-provided decoders
Most IoT devices send their data in binary or hexadecimal formats. However, most IoT cloud applications expect incoming data to be in a JSON structure, with sensor readings in decimal format. This process is known as "decoding" the device data.
Given that most LoRaWAN device manufacturers provide decoding functions, we created a public decoding service so you can run such code through a simple API request, erasing the need to copy-paste it into a LoRaWAN console, run it in your own server, or even in an UbiFunction.
2. How to use Ubidots DaaS
In order to use Ubidots DaaS all you need to do is make a POST request to the following URL:
HTTP Method | URL |
| https://functions.ubidots.com/pub/decode/custom |
Using the following HTTP Headers:
Header | Value |
|
|
| <your-ubidots-token> |
With a JSON payload containing the following keys:
Key | Value | Example |
| The public URL containing the code you wish to execute. Expects a raw text. | https://raw.githubusercontent.com/Seeed-Solution/TTN-Payload-Decoder/master/SenseCAP_S2120_Weather_Station_Decoder.js |
| JSON payload, whose keys are the arguments expected by the decoder | { |
| The main function in the decoder | decodeUplink |
Note: When using GitHub, please set the URL to raw text mode.
Example
Consider that the decoder you wish to use is hosted in the following URL:
https://raw.repository.com/myDecoder.js
Such decoder contains a function called decodeUplink
, such as:
function decodeUplink(args)
{
var bytes = args.bytes;
var port = args.fPort;
//Some other code that parses data
return {"temperature" : xxx, "humidity" : yyy};
}
Since it expects both the fPort and bytes keys, you'd need to send exactly the same JSON in the payload.
The body for a request to this decoder would look like:
{
"url": "https://raw.repository.com/myDecoder.js",
"payload": {
"fPort": 1,
"bytes": "SGVsbG8sIHdvcmxkIQ="
},
"function": "decodeUplink"
}
After making the request, it should return the same object as in the decoder, in this case:
{"temperature" : xxx, "humidity" : yyy}
3. DaaS implementation in NodeJS
The following is a simple implementation in NodeJS on how to make such request. You can incorporate the DaaS function in your code in order to call the decoder that you require.
const axios = require('axios')
async function DaaS(decoderURL, functionName, payloadToDecode, ubidotsToken)
{
var headers = {"X-Auth-Token": ubidotsToken, "Content-Type": "application/json"};
var payload =
{
"url" : decoderURL,
"payload" : payloadToDecode,
"function" : functionName
}
var options =
{
"method" : 'post',
"url" : 'http://functions.ubidots.com/pub/decode/custom',
"data" : payload,
"json" : true,
"headers" : headers,
};
const req = await axios.request(options);
return req.data.decoded;
}
4. DaaS implementation in Python
The following is a simple implementation in Python on how to make such request. You can incorporate the DaaS function in your code in order to call the decoder that you require.
def daas(decoder_url, entry_point, payload_to_decode, ubidots_token):
endpoint_url = 'http://functions.ubidots.com/pub/decode/custom'
headers = {"X-Auth-Token": ubidots_token, "Content-Type": "application/json"}
payload = {
"url": decoder_url,
"payload": payload_to_decode,
"function": entry_point
}
try:
response = requests.request('POST', endpoint_url, headers=headers, json=payload)
response.raise_for_status()
req = response.json()
return req['decoded']
except requests.exceptions.RequestException as e:
print(f"An error occurred while making the request: {e}")
return None
5. Sample decoder
We've created a dummy decoder and hosted it in this URL: https://gist.github.com/RandomGenericUsername/ed777da3346928fcdbc8ff7bb8daf922
Such decoder contains the following code, and returns an Ubidots-compatible JSON payload:
function myDecoder(args)
{
var bytes = args.bytes;
var port = args.port;
var startFlag = bytes[0];
var temperature;
var humidity;
if(startFlag != 0xAA)
{
return {"Error" : "Invalid start flag"};
}
temperature = bytes[1] << 8 | bytes[2];
humidity = bytes[3] << 8 | bytes[4];
return {"temperature" : temperature, "humidity" : humidity};
}
Now, consider a device that sends its data in a hexadecimal string with the following structure:
Byte | Description | Example |
1 | Start flag. Must always be 0xAA |
|
2-3 | Temperature data (big endian) |
|
4-5 | Humidity data (big endian) |
|
1-5 | full payload |
|
Let's see how this request looks like in NodeJS and Python.
Sample decoder using NodeJS
const axios = require('axios');
const ubidotsToken = 'YOUR-UBIDOTS-TOKEN';
const entryPoint = 'myDecoder';
const decoderUrl = 'https://gist.githubusercontent.com/RandomGenericUsername/ed777da3346928fcdbc8ff7bb8daf922/raw/248d5e88b3341d16ffa9e1e22e52c2a06f030545/exampleDecoder.js';
async function main(args)
{
var data = args.data;
var port = args.port;
var decoderExpectedObject = {"bytes" : Buffer.from(data, 'hex'), "port" : port};
var ubidotsPayload = await DaaS(decoderUrl, entryPoint, decoderExpectedObject, ubidotsToken);
console.log(ubidotsPayload);
//If this a UbiFunction, you need to make the request to the devices endpoint to send the data to that Device
//If this is a plugin, return an Ubidots compatible payload containing the device's data
}
async function DaaS(decoderURL, functionName, payloadToDecode, ubidotsToken)
{
var headers = {"X-Auth-Token": ubidotsToken, "Content-Type": "application/json"};
var payload =
{
"url" : decoderURL,
"payload" : payloadToDecode,
"function" : functionName
}
var options =
{
"method" : 'post',
"url" : 'http://functions.ubidots.com/pub/decode/custom',
"data" : payload,
"json" : true,
"headers" : headers,
};
const req = await axios.request(options);
return req.data.decoded;
}
The following GIF shows the simulated device sending data and using the decoder hosted on Gist:
Sample decoder using Python
import requests
decoder_url = 'https://gist.githubusercontent.com/RandomGenericUsername/ed777da3346928fcdbc8ff7bb8daf922/raw/d36303186c629d73dc3fd91c3d906e5697cd5b46/exampleDecoder.js'
entry_point = 'myDecoder'
ubidots_token = 'BBFF-ij2eOkYbTCrdrk6c7fdAxvpK7CTtVy'
def main(args):
data = args['data'] # incoming binary data
port = args['port']
decoder_expected_object = {"bytes": list(bytes.fromhex(data)), "port": port}
ubidots_payload = daas(decoder_url, entry_point, decoder_expected_object, ubidots_token)
print(ubidots_payload)
#If this a UbiFunction, you need to make the request to the devices endpoint to send the data to that Device
#If this is a plugin, return an Ubidots compatible payload containing the device's data
def daas(decoder_url, entry_point, payload_to_decode, ubidots_token):
endpoint_url = 'http://functions.ubidots.com/pub/decode/custom'
headers = {"X-Auth-Token": ubidots_token, "Content-Type": "application/json"}
payload = {
"url": decoder_url,
"payload": payload_to_decode,
"function": entry_point
}
try:
response = requests.request('POST', endpoint_url, headers=headers, json=payload)
response.raise_for_status()
req = response.json()
return req['decoded']
except requests.exceptions.RequestException as e:
print(f"An error occurred while making the request: {e}")
return None
The following GIF shows the simulated device sending data and using the decoder hosted on Gist:
6. Troubleshooting
Please keep in mind the following when using Ubidots DaaS:
Identify the structure of the incoming arguments into the function/plugin. This is so you can access the incoming data. The format of the incoming arguments is usually imposed by the LNS from which the device is sending data.
Regarding this, for both the Python and NodeJS examples, shown below, assume that the raw binary data gets to the UbiFunction or Plugin inside the data key in the incoming function's arguments.
Identify the arguments that the manufacturer decoder expects, so you can build the JSON with those same keys.
Regarding this, for both the Python and NodeJS examples, shown below, note that the fictitious decoder expects the bytes and port keys on its arguments, so you need to build the JSON accordingly to send to the decoder.
7. Feedback, suggestion and related articles
Feel free to post questions or suggestions in our community portal, or contact us via support@ubidots.com.
Other users also found helpful...