Payload Formatter

Payload Formatter 

Introducing Payload Formatter support for Uplink Messages

May 10, 2022

Payload formatters make it possible to process data on-the-fly from end devices. This is useful for converting binary payloads to human-readable fields or any other kind of data conversion.

Converting binary data into JSON objects is the only requirement for integrating The Things Stack and Qubitro.

From now on, it is possible to create Javascript Payload Formatters for Uplink messages directly on Qubitro Portal. (API endpoint is also scheduled for launch in the coming days).

Qubitro Portal The Things Stack Uplink Message Formatter

Support & Limitations

It is possible to write directly in Qubitro Portal, and Javascript functions are executed using a JavaScript ECMAScript 5.1 engine under the hood.

We strongly recommend following The Things Stack guidance and using the existing formatters, if any.

- Existing Formatters on The Things Stack

It is still possible to create formatters on The Thing Stack, and Qubitro will automatically detect it. Existing formatters are also not affected.

If the formatter is enabled on The Things Stack and configured properly, our system will ignore the formatter created on Portal.

- Function name & return value

The first requirement is the function name. The function name must be decodeUplink and must accept only a single parameter.

The second requirement is the return value. The return value must be a JSON object with the name 'data'.

These two simple requirements make possible true no-code integration without compromising on performance. This way, we automatically synchronize the sensor values and other platform-specific information to visualize on Portal.

An example function:

function decodeUplink(input) {
  return {
    data: {
      bytes: input.bytes

A more complex example:

The following example is from Seeed Studio's SenseCap.

function decodeUplink(input) {
  var data = {};
  var errors = [];
  let index = 0;
  while(input.bytes.length - index > 6) {
    let channel = input.bytes[0+index];
    if(channel > 2){
      errors.push("Invalid channel "+channel.toString());
    let data_type = (input.bytes[1+index] << 8) | input.bytes[2+index];
    let value = ((input.bytes[3+index] << 0) | (input.bytes[4+index] << 8) | (input.bytes[5+index] << 16) | (input.bytes[6+index] << 24));
    switch (data_type) {
      case 0x0700:
        data.battery = value & 0xFFFF;
      case 0x0110:
        data.temp = value/ 1000;
      case 0x0210:
        data.humidity = value/ 1000;
      case 0x0310:
        data.light = value/ 1000;
      case 0x0410:
        data.co2_ppm = value/ 1000;
      case 0x0510:
        data.baro_pressure = value / 1000;
      case 0x0610:
        data.soil_temp = value/ 1000;
      case 0x0710:
        data.soil_moisture = value/ 1000;
      errors.push("0x"+data_type.toString(16) + " not a valid sensor value");
    index += 7;
  return {
    data: data,
    warnings: [],
    errors: errors,

Working with Data

Once the formatter is saved, the Data tab will automatically show sensor values.

After that, it is possible to utilize all Qubitro features, including visualizing data and building custom applications with Qubitro APIs.

Need help or have a question?

Visit Documentation ->

Join our Discord Server ->

Join our Community Forum ->

Follow us on Twitter ->


Beray Bentesen

Founder & CEO of Qubitro, Inc.