arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 7

Guides

Loading...

Integrations

Loading...

Loading...

Functions & Data Processing

Loading...

Loading...

Guides

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

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

hashtag
🚦 Start Here

hashtag
Creating Your First Project

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

hashtag
Adding Your First Device

Easily add, configure, and manage IoT devices.

hashtag
Visualizing Data with Dashboards

Build interactive dashboards to monitor real-time device data.

hashtag
πŸ› οΈ Essential Features

hashtag
Understanding Time Series Storage

Learn about data storage and retrieval options.

Working with Functions

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

hashtag
Managing API and External Credentials

Securely integrate Qubitro with other services.


Need more help? Visit our complete or contact .

Developerschevron-right
documentation
supportarrow-up-right
rectangle-historyProjectchevron-right
swap-arrowsNo-Code Integrationschevron-right
tower-broadcastMQTTchevron-right
brackets-curlyHTTPchevron-right
chart-mixedMonitoringchevron-right
Time Series Storagechevron-right
boltFunctionschevron-right

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.

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

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

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

hashtag
Accessing Input Properties

Create a simple decoder function that extracts the raw bytes, fPort, and metadata:

hashtag
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:

hashtag
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:

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

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 Decoder Function to decode GPS and sensor data

  • βœ… View structured decoded data in the Qubitro portal


hashtag
🧩 Prerequisites

  • A

  • An existing Device

  • Your device's MQTT credentials


hashtag
Step 1: Get Your Qubitro MQTT Credentials

  1. Go to your device in the Qubitro Portal

  2. Click the MQTT Credentials tab

  3. Copy:


hashtag
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


hashtag
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:

  1. Save and activate the function.

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

  1. Click the green send button to publish the message.

hashtag
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

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

  • Client ID: (same as Device ID)

  • Username: Device ID

  • Password: Device Token

  • SSL/TLS: βœ… Enabled

  • SSL Secure: βœ… Enabled

  • Certificate: CA signed server certificate

  • Use the following payload:
    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
      };
    }
    {
      "payload_hex": "00C08802808560A184000FFB648EAD0F,00c088028085909784000ffb648ead46,00c089028085109284000ffe648ead89,00c088028085e09c84000ffe648eadcb"
    }
    circle-check

    Click Connect to establish a secure MQTT connection.

    circle-check

    You can also test a single hex payload on the Qubitro Portal to verify your decoder.

    Qubitro accountarrow-up-right
    MQTTX Desktop Apparrow-up-right

    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:

    circle-info

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

    circle-info

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

    circle-info

    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.

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

    hashtag
    Syntax and Usage

    Define variables using predefined keys:

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

    hashtag
    Writing Rule Conditions

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

    hashtag
    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%.

    hashtag
    Utilizing the trigger() Function

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

    hashtag
    Example: Alert When Temperature and Humidity Are Too High

    hashtag
    Practical Implementations

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

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

    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:

    circle-info

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

    circle-info

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

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

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

    hashtag
    Practical Implementations

    hashtag
    Example 1: Flattening Nested JSON

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

    1

    hashtag
    Incoming Data:

    2

    JSONata Expression:

    3

    hashtag
    Example 2: Extracting Values from Arrays

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

    1

    hashtag
    Incoming Data:

    2

    JSONata Expression:

    3

    hashtag
    Example 3: Performing Mathematical Calculations

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

    1

    hashtag
    Incoming Data:

    2

    JSONata Expression:

    3

    hashtag
    Example 4: Time Formatting and Duration Calculation

    Modify timestamps into human-readable formats or compute durations.

    1

    hashtag
    Incoming Data:

    2

    JSONata Expression:

    3

    hashtag
    Example 5: Dynamically Accessing Data Using $lookup()

    This method is useful when field names may change dynamically.

    1

    hashtag
    Incoming Data:

    2

    JSONata Expression:

    3

    Using the root object: $ refers to the root JSON object.

  • Accessing fields: fieldname retrieves a specific property.

  • Transformed Data:

    {
      "sensor_type": "temperature",
      "current_temperature": 22,
      "average_temperature": 21
    }

    Transformed Data:

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

    Transformed Data:

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

    Transformed Data:

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

    Transformed Data:

    {
      "temp_value": 22,
      "humidity_value": 78
    }
    {
      "sensor": {
        "type": "temperature",
        "readings": {
          "current": 22,
          "average": 21
        }
      }
    }
    {
      "sensor_type": sensor.type,
      "current_temperature": sensor.readings.current,
      "average_temperature": sensor.readings.average
    }
    {
      "sensors": [
        {"type": "temperature", "value": 22},
        {"type": "humidity", "value": 55}
      ]
    }
    {
      "temperature_value": sensors[type='temperature'].value,
      "humidity_value": sensors[type='humidity'].value
    }
    {
      "values": [5, 10, 15, 20, 25]
    }
    {
      "sum": $sum(values),
      "average": $average(values),
      "max": $max(values),
      "min": $min(values)
    }
    {
      "event": {
        "start_time": "2020-01-01T08:00:00Z",
        "end_time": "2020-01-01T12:00:00Z"
      }
    }
    {
      "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"
    }
    {
      "sensorData": {
        "temperature": 22,
        "humidity": 78
      }
    }
    {
      "temp_value": $lookup(sensorData, 'temperature'),
      "humidity_value": $lookup(sensorData, 'humidity')
    }
    spinner