Sensorclouds Docs
Step-by-Step Guides

Writing Payload Decoders

Create custom JavaScript decoders with templates and testing

Payload decoders transform raw device data (binary, hex, or custom formats) into structured JSON that the platform can store and visualize. This guide covers writing and testing custom decoders.

Prerequisites

  • ADMIN or ENGINEER role
  • A device profile to attach the decoder to
  • Understanding of your device's payload format

When Do You Need a Decoder?

Payload FormatDecoder Needed?
JSON object ({"temp": 23.5})No — parsed automatically
Hex-encoded string ("0x1A2B3C")Yes — decode bytes to fields
Raw binary bytesYes — parse buffer to fields
Custom string format ("T:23.5;H:60")Yes — parse string to fields

Steps

Go to Device Profiles, open the profile you want to configure, and select the Payload Formatter tab.

Select Codec Type

Set the Codec Type to CUSTOM. This enables the code editor where you write your decoder function.

Other options:

  • NONE — No decoding (payload must be valid JSON)
  • CAYENNE_LPP — Built-in Cayenne Low Power Payload decoder

Write Your Decoder Function

The decoder receives the raw payload and must return a JSON object with your telemetry fields.

Function signature:

function decode(payload) {
  // payload is a Buffer (array of bytes)
  // Return an object with your telemetry fields
  return {
    field1: value1,
    field2: value2,
  };
}

Example — Decode a 4-byte payload:

A device sends 4 bytes: temperature (2 bytes, signed, big-endian) and humidity (2 bytes, unsigned):

function decode(payload) {
  // Read temperature as signed 16-bit integer (divide by 100 for decimal)
  const temperature = payload.readInt16BE(0) / 100;

  // Read humidity as unsigned 16-bit integer (divide by 100 for decimal)
  const humidity = payload.readUInt16BE(2) / 100;

  return {
    temperature,
    humidity,
  };
}

Example — Parse a custom string format:

Device sends "T:23.50|H:60.00|B:3.7":

function decode(payload) {
  const str = payload.toString("utf-8");
  const parts = str.split("|");
  const result = {};

  parts.forEach((part) => {
    const [key, value] = part.split(":");
    switch (key) {
      case "T": result.temperature = parseFloat(value); break;
      case "H": result.humidity = parseFloat(value); break;
      case "B": result.battery = parseFloat(value); break;
    }
  });

  return result;
}

Example — Decode hex-encoded GPS data:

Device sends hex string with latitude (4 bytes float) and longitude (4 bytes float):

function decode(payload) {
  const lat = payload.readFloatBE(0);
  const lng = payload.readFloatBE(4);

  return {
    latitude: parseFloat(lat.toFixed(6)),
    longitude: parseFloat(lng.toFixed(6)),
  };
}

Test with Sample Payload

Use the built-in Test panel below the code editor:

  1. Enter a sample hex payload (e.g., 09 1A 17 70 for temp=23.30, humidity=60.00)
  2. Click Test Decoder
  3. Verify the decoded output matches your expected fields
{
  "temperature": 23.30,
  "humidity": 60.00
}

Always test with real payloads captured from your device. Edge cases (negative values, maximum ranges, missing bytes) are common sources of bugs.

Save and Apply

Click Save. The decoder immediately applies to all devices using this profile. From now on, every incoming payload from these devices passes through your decoder before being stored.

  • If the decoder throws an error, the raw payload is stored as-is and an error is logged
  • If the decoder returns an empty object, no telemetry fields are created for that message

Common Patterns

Bit Field Extraction

function decode(payload) {
  const flags = payload[0];
  return {
    motorRunning: !!(flags & 0x01),
    doorOpen: !!(flags & 0x02),
    alarmActive: !!(flags & 0x04),
    batteryLow: !!(flags & 0x08),
  };
}

Multi-Sensor Payload

function decode(payload) {
  const sensorCount = payload[0];
  const result = {};

  for (let i = 0; i < sensorCount; i++) {
    const offset = 1 + i * 4;
    const sensorId = payload[offset];
    const value = payload.readInt16BE(offset + 1) / 100;
    result[`sensor_${sensorId}`] = value;
  }

  return result;
}

Tips

  • Keep decoders simple: Extract fields and return them. Avoid complex logic or external calls.
  • Handle errors gracefully: Wrap risky operations in try/catch to prevent the decoder from crashing on unexpected payloads
  • Use meaningful field names: temperature is better than t or field1 — these names appear in dashboards and alarm rules
  • Document the payload format: Add comments in your decoder explaining the byte layout

On this page