Loading...
Loading...
Loading...
Loading...
Loading...
Learn how to publish multiple hex payloads using MQTTX and decode them using Qubitro’s Decoder Function. This guide walks you through secure MQTT setup, payload formatting, and real-time data decoding
This guide shows how to:
✅ Send multiple hex payloads using MQTTX
✅ Use a custom Qubitro to decode GPS and sensor data
✅ View structured decoded data in the Qubitro portal
An existing Device
Your device's MQTT credentials
Go to your device in the Qubitro Portal
Click the MQTT Credentials tab
Copy:
Device ID (used as Username
and topic)
Device Token (used as Password
)
Host: broker.qubitro.com
Port: 8883
(SSL) or 1883
(non-SSL)
Topic: Device ID
Open MQTTX and configure your connection as shown below:
Name: broker.qubitro.com
Host: mqtts://broker.qubitro.com
Port: 8883
Client ID: (same as Device ID)
Username: Device ID
Password: Device Token
SSL/TLS: ✅ Enabled
SSL Secure: ✅ Enabled
Certificate: CA signed server certificate
Click Connect to establish a secure MQTT connection.
1. Go to your device → Functions tab
2. Click Add Function → Choose Decoder Function
3. Paste the following JavaScript code:
function decoder(input) {
const longitude = ((input.bytes[0] << 24) | (input.bytes[1] << 16) | (input.bytes[2] << 8) | (input.bytes[6] & 0xC0)) /1000000;
const latitude = ((input.bytes[3] << 24) | (input.bytes[4] << 16) | (input.bytes[5] << 8) | ((input.bytes[6] & 0x30) << 2)) /1000000;
const hMSL32 = ((((input.bytes[6] & 0x0F) << 8 ) + input.bytes[7]) * 2) - 191;
const hAccCoeff = (input.bytes[8] & 0xE0) >> 5;
const vAccCoeff = (input.bytes[8] & 0x1C) >> 2;
const heading = ((input.bytes[8] & 0x03) << 2) + ((input.bytes[9] & 0xC0) >> 6);
const speed = (input.bytes[9] & 0x3F);
const battery = ((input.bytes[10]*256) + input.bytes[11]);
const year = (input.bytes[12] >> 2) + 2000;
const month = ((input.bytes[12] & 0x03) << 2) + ((input.bytes[13] & 0xC0) >> 6);
const day = (input.bytes[13] & 0x3E) >> 1;
const hour = ((input.bytes[13] & 0x01) << 4) + ((input.bytes[14] & 0xF0) >> 4);
const minute = ((input.bytes[14] & 0x0F) << 2) + ((input.bytes[15] & 0xC0) >> 6);
const second = (input.bytes[15] & 0x3F);
const timestampSeconds = Math.floor(new Date(year, month, day, hour, minute, second).getTime() / 1000);
return {
longitude,
latitude,
coordinates: [latitude, longitude],
hMSL32,
hAccCoeff,
vAccCoeff,
heading,
speed,
battery,
time: timestampSeconds
};
}
You can also test a single hex payload on the Qubitro Portal to verify your decoder.
Save and activate the function.
Go to the Publish tab in MQTTX.
In the topic field, paste your Device ID.
Set the payload type to JSON.
Use the following payload:
{
"payload_hex": "00C08802808560A184000FFB648EAD0F,00c088028085909784000ffb648ead46,00c089028085109284000ffe648ead89,00c088028085e09c84000ffe648eadcb"
}
Click the green send button to publish the message.
Open the Storage tab in your Qubitro device and you’ll see structured decoded values like:
You’ve successfully:
• Connected MQTTX to Qubitro securely
• Published multiple hex payloads
• Used a custom decoder to parse sensor/GPS values
• Verified decoded output in Qubitro
Explore the recommended guides below to quickly familiarize yourself with core platform features and workflows.
This page helps you quickly get started with Qubitro by recommending essential guides and documentation. Follow these steps to set up your first project, connect devices, and explore core features.
Start your IoT journey by setting up your project in Qubitro.
Easily add, configure, and manage IoT devices.
Build interactive dashboards to monitor real-time device data.
Learn about data storage and retrieval options.
Working with Functions
Automate tasks using Decoder, Transformation, Rule, and Scheduled functions.
Securely integrate Qubitro with other services.
Need more help? Visit our complete or contact .
This guide explains how to use JSONata expressions in Qubitro Transformation Functions to manipulate and structure real-time data before it is stored or used for automated actions.
JSONata provides a flexible, lightweight way to filter, calculate, and restructure data without writing traditional code.
A few things to remember:
Once a device sends data, TF automatically applies the JSONata expression configured by the user.
If a Decoder Function exists, it will process raw data first, and then TF will apply JSONata expressions on the decoded output. If there is no decoder, TF will apply transformations directly to the published data.
Here are some core JSONata concepts:
Mathematical functions: $sum(values), $average(values), $max(values), $min(values).
Ternary operators: condition ? trueValue : falseValue.
String and numeric literals: "text" for strings, 42 for numbers.
Using the root object: $ refers to the root JSON object.
Accessing fields: fieldname retrieves a specific property.
Convert a multi-level JSON object into a flat structure for better Qubitro compatibility.
Transform an array of objects into structured key-value pairs.
JSONata Expression:
{
"temperature_value": sensors[type='temperature'].value,
"humidity_value": sensors[type='humidity'].value
}
Transformed Data:
{
"temperature_value": 22,
"humidity_value": 55
}
Use JSONata to compute sums, averages, and find min/max values dynamically.
JSONata Expression:
{
"sum": $sum(values),
"average": $average(values),
"max": $max(values),
"min": $min(values)
}
Transformed Data:
{
"sum": 75,
"average": 15,
"max": 25,
"min": 5
}
Modify timestamps into human-readable formats or compute durations.
JSONata Expression:
{
"start_date": $fromMillis($toMillis(event.start_time), '[Y0001]-[M01]-[D01]'),
"end_date": $fromMillis($toMillis(event.end_time), '[Y0001]-[M01]-[D01]'),
"duration_hours": ($toMillis(event.end_time) - $toMillis(event.start_time)) / (1000*60*60) & " hours"
}
Transformed Data:
{
"start_date": "2020-01-01",
"end_date": "2020-01-01",
"duration_hours": "4 hours"
}
This method is useful when field names may change dynamically.
By following this guide, you can customize Qubitro rule functions using JavaScript to create dynamic conditions.
This guide explains how to use JavaScript within Qubitro’s rule functions to define custom conditions and automate actions based on real-time data.
A few notes to remember:
Although the examples here focus on environmental monitoring and geofencing, the same approach applies to any scenario requiring event-driven automation.
Qubitro provides a simple way to access sensor values in JavaScript using predefined keys. These keys allow you to reference the latest device data without complex parsing.
Define variables using predefined keys:
Each variable directly pulls the corresponding real-time value from the incoming data.
With variables defined, you can create logical conditions to decide when the function should trigger an action.
This function triggers an action if the temperature is above 25°C and humidity exceeds 70%.
The trigger()
function is used to execute predefined action when conditions are met.
This example ensures that a device does not enter a restricted assembly area. If it does, the function triggers an alert.
function run(){
let temperature = ${{TEMPERATURE}};
let humidity = ${{HUMIDITY}};
let light = ${{LIGHT}};
let motion = ${{MOTION}};
let voltage = ${{VDD}};
}
function run() {
let temperature = ${{TEMPERATURE}};
let humidity = ${{HUMIDITY}};
if (temperature > 25 && humidity > 70) {
trigger(); // Executes the pre-configured action
}
}
function run() {
let temperature = ${{TEMPERATURE}};
let humidity = ${{HUMIDITY}};
if (temperature > 30 && humidity > 80) {
trigger(); // This executes the pre-configured action (e.g., send a notification)
}
}
function run() {
let coordinates = ${{coordinates}}; // Assuming format [latitude, longitude]
// Convert coordinates array to an object
let incomingLocation = {
lat: coordinates[0],
lon: coordinates[1]
};
// Define the restricted zone (assembly area)
const restrictedZone = {
A: { lat: 40.822608, lon: 29.352864 },
B: { lat: 40.822652, lon: 29.353246 },
C: { lat: 40.822289, lon: 29.352890 },
D: { lat: 40.822327, lon: 29.353346 }
};
// Check if the incoming coordinates fall within the restricted area
const isInsideRectangle = (point, rect) => {
const minLat = Math.min(rect.A.lat, rect.B.lat, rect.C.lat, rect.D.lat);
const maxLat = Math.max(rect.A.lat, rect.B.lat, rect.C.lat, rect.D.lat);
const minLon = Math.min(rect.A.lon, rect.B.lon, rect.C.lon, rect.D.lon);
const maxLon = Math.max(rect.A.lon, rect.B.lon, rect.C.lon, rect.D.lon);
return point.lat >= minLat && point.lat <= maxLat && point.lon >= minLon && point.lon <= maxLon;
};
if (isInsideRectangle(incomingLocation, restrictedZone)) {
trigger(); // Triggers the predefined alert (e.g., send a warning notification)
}
}
function run() {
let temperature = ${{TEMPERATURE}};
let humidity = ${{HUMIDITY}};
let lightLevel = ${{LIGHT}};
let co2Level = ${{CO2}};
// Trigger alert if temperature is too high and humidity is too low
if (temperature > 28 && humidity < 40) {
trigger(); // Executes the pre-configured alert
}
// Trigger alert if light levels drop too low while CO2 is high
if (lightLevel < 200 && co2Level > 1000) {
trigger(); // Executes the pre-configured alert
}
// Immediate action required for extreme conditions
if (temperature > 35 || humidity > 80 || co2Level > 1500) {
trigger(); // Executes the pre-configured alert
}
}
{
"sensor": {
"type": "temperature",
"readings": {
"current": 22,
"average": 21
}
}
}
{
"sensor_type": sensor.type,
"current_temperature": sensor.readings.current,
"average_temperature": sensor.readings.average
}
{
"sensor_type": "temperature",
"current_temperature": 22,
"average_temperature": 21
}
By following this guide, you can customize Qubitro decoder functions to decode various sensor payloads and extract valuable network metrics from any LNS integration.
This guide explains how to use Qubitro’s decoder functions to parse raw uplink messages received via LNS integrations—such as The Things Stack, Loriot, and others.
Although the examples here refer to decoding sensor data (like temperature or humidity), these functions work the same way for any LNS provider.
Decoder Function
A custom function that Qubitro automatically invokes with an input object containing the uplink message’s raw data, the fPort, and additional metadata.
• bytes: A byte array representing the raw data payload.
• fPort: The port number used for the uplink message.
• metadata: Rich network details from the LNS (e.g., signal quality, gateway information), regardless of the provider.
Qubitro supplies the decoder function with an input object that includes all necessary properties.
Your function should extract the required sensor values by converting raw bytes into human-readable data. You can also extract network metrics—like RSSI and SNR—from the metadata.
Create a simple decoder function that extracts the raw bytes, fPort, and metadata:
function decoder(input) {
var bytes = input.bytes; // Retrieve the raw data payload
var fPort = input.fPort; // Retrieve the fPort number
var metadata = input.metadata; // Retrieve LNS metadata
return {}; // Customize and return a JSON object as needed
}
In this example, assume that the sensor sends temperature data encoded in two bytes. The function converts these bytes into a decimal temperature value:
function decoder(input) {
const bytes = input.bytes;
const fPort = input.fPort;
// Combine two bytes into one number and adjust the scale (divide by 100)
const temperature = ((bytes[0] << 8) + bytes[1]) / 100;
return {
"bytes": bytes,
"fPort": fPort,
"temperature": temperature,
};
}
This example demonstrates handling multiple sensor readings. The function first checks if the payload is long enough and then extracts temperature and humidity from the last four bytes:
function decoder(input) {
var bytes = input.bytes;
var fPort = input.fPort;
var data = {};
// Ensure the payload contains enough data for both sensors
if (bytes.length < 4) {
return { error: 'Payload too short' };
}
// Extract bytes for temperature and humidity
var temperatureBytes = bytes.slice(bytes.length - 4, bytes.length - 2);
var humidityBytes = bytes.slice(bytes.length - 2);
// Convert the byte arrays into 16-bit integers and scale accordingly
var temperature = ((temperatureBytes[0] << 8) | temperatureBytes[1]) / 100.0;
var humidity = ((humidityBytes[0] << 8) | humidityBytes[1]) / 100.0;
data["temperature"] = temperature;
data["humidity"] = humidity;
return data;
}
Decoder functions can also extract network metrics (such as SNR and RSSI) from the metadata. This example demonstrates how to do so:
function decoder(input) {
var bytes = input.bytes; // Sensor data payload
var fPort = input.fPort; // fPort value
var metadata = input.metadata; // LNS metadata from the uplink
var snr, rssi;
// Extract network metrics if available (works with any LNS provider)
if (metadata && metadata.gws && metadata.gws.length > 0) {
snr = metadata.gws[0].snr; // SNR from the first gateway
rssi = metadata.gws[0].rssi; // RSSI from the first gateway
}
// Additionally decode sensor-specific data (example calculations)
return {
STATUS: bytes[0] & 0x01, // Sensor status
BATTERY: (25 + (bytes[1] & 0x0f)) / 10, // Battery level estimate
COUNT: (bytes[7] << 16) | (bytes[6] << 8) | bytes[5], // Count extracted from multiple bytes
SNR: snr, // Signal-to-noise ratio from metadata
RSSI: rssi // Received signal strength indicator from metadata
};
}