Only this pageAll pages
Powered by GitBook
1 of 7

Guides

Loading...

Integrations

Loading...

Loading...

Functions & Data Processing

Loading...

Loading...

How to Publish and Decode Multiple Hex Payloads via MQTT on Qubitro

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


🧩 Prerequisites

  • A Qubitro account

  • An existing Device

  • MQTTX Desktop App

  • Your device's MQTT credentials


Step 1: Get Your Qubitro MQTT Credentials

  1. Go to your device in the Qubitro Portal

  2. Click the MQTT Credentials tab

  3. 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


Step 2: Configure MQTTX (Secure Connection)

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.


Step 3: Add Decoder Function in Qubitro

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.

  1. Save and activate the function.

Step 4: Publish Multiple Hex Payloads

  1. Go to the Publish tab in MQTTX.

  2. In the topic field, paste your Device ID.

  3. Set the payload type to JSON.

  4. Use the following payload:

{
  "payload_hex": "00C08802808560A184000FFB648EAD0F,00c088028085909784000ffb648ead46,00c089028085109284000ffe648ead89,00c088028085e09c84000ffe648eadcb"
}
  1. Click the green send button to publish the message.

Step 5: View Decoded Data

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

Guides

Explore the recommended guides below to quickly familiarize yourself with core platform features and workflows.

Getting Started with Qubitro

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 Here

Creating Your First Project

Start your IoT journey by setting up your project in Qubitro.

Adding Your First Device

Easily add, configure, and manage IoT devices.

Visualizing Data with Dashboards

Build interactive dashboards to monitor real-time device data.

🛠️ Essential Features

Understanding Time Series Storage

Learn about data storage and retrieval options.

Working with Functions

Automate tasks using Decoder, Transformation, Rule, and Scheduled functions.

Managing API and External Credentials

Securely integrate Qubitro with other services.


Need more help? Visit our complete or contact .

documentation
Developers

How to Use JSONata for Real-Time Data Transformation in Qubitro

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:

Data can be modified on-the-fly before storage and automation.

TF applies immediately to incoming data, whether raw or already decoded.

How Transformation Functions Work in Qubitro

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.

Basic JSONata Syntax

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.

Practical Implementations

Example 1: Flattening Nested JSON

Convert a multi-level JSON object into a flat structure for better Qubitro compatibility.

Example 2: Extracting Values from Arrays

Transform an array of objects into structured key-value pairs.

1

Incoming Data:

{
  "sensors": [
    {"type": "temperature", "value": 22},
    {"type": "humidity", "value": 55}
  ]
}
2

JSONata Expression:

{
  "temperature_value": sensors[type='temperature'].value,
  "humidity_value": sensors[type='humidity'].value
}
3

Transformed Data:

{
  "temperature_value": 22,
  "humidity_value": 55
}

Example 3: Performing Mathematical Calculations

Use JSONata to compute sums, averages, and find min/max values dynamically.

1

Incoming Data:

{
  "values": [5, 10, 15, 20, 25]
}
2

JSONata Expression:

{
  "sum": $sum(values),
  "average": $average(values),
  "max": $max(values),
  "min": $min(values)
}
3

Transformed Data:

{
  "sum": 75,
  "average": 15,
  "max": 25,
  "min": 5
}

Example 4: Time Formatting and Duration Calculation

Modify timestamps into human-readable formats or compute durations.

1

Incoming Data:

{
  "event": {
    "start_time": "2020-01-01T08:00:00Z",
    "end_time": "2020-01-01T12:00:00Z"
  }
}
2

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"
}
3

Transformed Data:

{
  "start_date": "2020-01-01",
  "end_date": "2020-01-01",
  "duration_hours": "4 hours"
}

Example 5: Dynamically Accessing Data Using $lookup()

This method is useful when field names may change dynamically.

1

Incoming Data:

{
  "sensorData": {
    "temperature": 22,
    "humidity": 78
  }
}
2

JSONata Expression:

{
  "temp_value": $lookup(sensorData, 'temperature'),
  "humidity_value": $lookup(sensorData, 'humidity')
}
3

Transformed Data:

{
  "temp_value": 22,
  "humidity_value": 78
}

How to Use JavaScript for Custom Rule Conditions in Qubitro

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:

Rule Functions operate only on real-time data – They cannot query historical data from the database.

Each Rule Function executes a single pre-configured action – It does not support executing multiple actions within one function.

Actions are predefined during function setup – The trigger() function executes only the action assigned when creating the rule.


Although the examples here focus on environmental monitoring and geofencing, the same approach applies to any scenario requiring event-driven automation.

Accessing Data with Predefined Keys

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.

Syntax and Usage

Define variables using predefined keys:

Each variable directly pulls the corresponding real-time value from the incoming data.

Writing Rule Conditions

With variables defined, you can create logical conditions to decide when the function should trigger an action.

Example: Trigger an Action Based on Temperature and Humidity

This function triggers an action if the temperature is above 25°C and humidity exceeds 70%.

Utilizing the trigger() Function

The trigger() function is used to execute predefined action when conditions are met.

Example: Alert When Temperature and Humidity Are Too High

Practical Implementations

Example 1: Geofencing a Restricted Area

This example ensures that a device does not enter a restricted assembly area. If it does, the function triggers an alert.

Example 2: Advanced Environmental Monitoring

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
    }
}
1

Incoming Data:

2

JSONata Expression:

3

Transformed Data:

{
  "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
}
support
Decoder Function
ProjectManage and organize your Qubitro projects, including devices, data sources, and collaboration settings, all in one place.
No-Code IntegrationsForward data from connectivity providers without custom code, automatically create devices in Qubitro.
MQTTPublish device data to Qubitro using the lightweight MQTT protocol for real-time ingestion.
HTTPSend JSON payloads from your devices or applications to Qubitro via a simple HTTP endpoint.
MonitoringThe Monitoring page in Qubitro provides a way to view and manage data through dashboards and blueprints.
Time Series StorageQubitro Time Series Storage enables scalable, real-time device data storage with customizable retention, API access, and seamless dashboard integration.
FunctionsQubitro Functions enable no-code or low-code data processing, automation, and transformation.

How to Decode Uplink Messages with Qubitro Decoder Functions

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.

Key Concepts

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.

Input Properties

• 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.

How It Works

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.

Accessing Input Properties

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
}

Decoding a Temperature Sensor Message

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,
  };
}

Decoding Temperature and Humidity Data

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;
}

Accessing Metadata for Network Metrics

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
  };
}