Stand with Ukraine flag
Try it now Pricing
PE Edge
Documentation > Integrations > ChirpStack
Getting Started
Devices Library Installation Architecture API FAQ
On this page

ChirpStack Integration

Doc info icon

Edge ChirpStack Integration is implemented similarly to the ChirpStack Integration. The only difference is how the integration is created and deployed. Before proceeding, refer to the ChirpStack Integration documentation.

Overview

ChirpStack is an open-source LoRaWAN Network Server which can be used to setup LoRaWAN networks. After integrating ChirpStack with ThingsBoard Edge, you can locally connect, process, and visualize device data at the edge, ensuring low-latency insights and offline capabilities.

To learn more, please see the integration diagram.

image

Prerequisites

  • ThingsBoard Edge Professional Edition up and running.
  • Install ChirpStack Network Server via Docker Compose or Ubuntu.
  • Have a device connected to the network. Read how to connect the LoRaWAN device with ChirpStack.

Create converter and integration templates

Only the ThingsBoard Professional Edition creates converters and integration templates. So please use ThingsBoard Cloud or install your own platform instance to log in as a Tenant administrator.

Basic settings

To add the ChirpStack integration:

  • Go to the Edge management > Integration templates section and click the “Add” button to add a new integration.
  • Select the ChirpStack type and enter the integration name.
  • Click the “Next” button.
Doc info icon

Debug mode is extremely useful for development and troubleshooting. However, having it on all the time can significantly increase the disk space used by the database since all the debug data is stored there.

Therefore, starting from version 3.9, ThingsBoard stores all debug events for integrations only during the first 15 minutes. After that, only failure events are retained. These settings can be combined or completely disabled.

Uplink is necessary to convert the incoming data from the device into the required format for displaying them in ThingsBoard.

  • Select the “Create new” tab
  • Enter the Converter name
  • The Main decoding configuration block:
    • Select the Entity type (Device or Asset) from the drop down-menu and enter the entity name. The corresponding entity will be created as a result of the integration.
Doc info icon

$eui is a placeholder that will automatically be replaced by the device’s unique identifier (e.g., 70B3D57ED003B6F5) from the Loriot (or ChirpStack, similar MQTT message) payload.
For example, if the device entity is named “Loriot Device $eui”, the created device name may look like “Loriot Device 70B3D57ED003B6F5”.

  • Enter the script into the “function payloadDecoder” field. For the Edge version 3.9 and older, the following script can be used:

The script used in the example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
var data = decodeToJson(payload);
var deviceName = data.deviceInfo.deviceName;
var deviceType = data.deviceInfo.deviceProfileName;
var groupName = 'IAQ devices';
// var customerName = 'Customer A';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';

// If you want to parse incoming data somehow, you can add your code to this function.
// input: bytes
// expected output:
//  {
//    "attributes": {"attributeKey": "attributeValue"},
//    "telemetry": {"telemetryKey": "telemetryValue"}
//  }
//
// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.
//

function decodePayload(input) {
var output = { attributes:{}, telemetry: {} };
// --- Decoding code --- //

    output.telemetry.HEX_bytes = bytesToHex(input);

    // If the length of the input byte array is odd - we cannot parse it using the example below
    if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = input[i++];
            if (i < input.length) {
                var channel_type = input[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = input[i];
                    i += 1;
                }
                // PIR
                else if (channel_id === 0x03 && channel_type === 0x00) {
                    output.telemetry.pir = input[i] === 0 ? "normal" : "trigger";
                    i += 1;
                }
                // DAYLIGHT
                else if (channel_id === 0x04 && channel_type === 0x00) {
                    output.telemetry.daylight = input[i] === 0 ? "dark" : "light";
                    i += 1;
                }
            }
        }
    }

    // --- Decoding code --- //
    return output;
}

// --- attributes and telemetry objects ---
var telemetry = {};
var attributes = {};
// --- attributes and telemetry objects ---

// --- Timestamp parsing
var dateString = data.time;
var timestamp = -1;
if (dateString != null) {
  timestamp = new Date(dateString).getTime();
  if (timestamp == -1) {
    var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;
    var millisecondsEndIndex = dateString.lastIndexOf('+');
    if (millisecondsEndIndex == -1) {
      millisecondsEndIndex = dateString.lastIndexOf('Z');
    }
    if (millisecondsEndIndex == -1) {
      millisecondsEndIndex = dateString.lastIndexOf('-');
    }
    if (millisecondsEndIndex == -1) {
      if (dateString.length >= secondsSeparatorIndex + 3) {
        dateString = dateString.substring(0, secondsSeparatorIndex + 3);
      }
    } else {
      dateString = dateString.substring(0, secondsSeparatorIndex + 3) +
        dateString.substring(millisecondsEndIndex, dateString.length);
    }
    timestamp = new Date(dateString).getTime();
  }
}
// If we cannot parse timestamp - we will use the current timestamp
if (timestamp == -1) {
timestamp = Date.now();
}
// --- Timestamp parsing

// You can add some keys manually to attributes or telemetry
attributes.deduplicationId = data.deduplicationId;

// You can exclude some keys from the result
var excludeFromAttributesList = ["deviceName", "rxInfo", "confirmed", "data", "deduplicationId","time", "adr", "dr", "fCnt"];
var excludeFromTelemetryList = ["data", "deviceInfo", "txInfo", "devAddr", "adr", "time", "fPort", "region_common_name", "region_config_id", "deduplicationId"];

// Message parsing
// To avoid paths in the decoded objects we passing false value to function as "pathInKey" argument.
// Warning: pathInKey can cause already found fields to be overwritten with the last value found.

var telemetryData = toFlatMap(data, excludeFromTelemetryList, false);
var attributesData = toFlatMap(data, excludeFromAttributesList, false);

var uplinkDataList = [];

// Passing incoming bytes to decodePayload function, to get custom decoding
var customDecoding = decodePayload(base64ToBytes(data.data));

// Collecting data to result
if (customDecoding.?telemetry.size() > 0) {
  telemetry.putAll(customDecoding.telemetry);
}

if (customDecoding.?attributes.size() > 0) {
  attributes.putAll(customDecoding.attributes);
}

telemetry.putAll(telemetryData);
attributes.putAll(attributesData);

var result = {
    deviceName: deviceName,
    deviceType: deviceType,
    //  assetName: assetName,
    //  assetType: assetType,
    //  customerName: customerName,
    groupName: groupName,
    attributes: attributes,
    telemetry: {
    ts: timestamp,
    values: telemetry
    }
};

return result;

The script used in the example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Decode an uplink message from a buffer
// payload - array of bytes
// metadata - key/value object

/** Decoder **/

// decode payload to string
var payloadStr = decodeToString(payload);

// decode payload to JSON
// var data = decodeToJson(payload);

var deviceName = 'Device A';
var deviceType = 'thermostat';
var customerName = 'Customer C';
var groupName = 'thermostat devices';
var manufacturer = 'Example corporation';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';

// Result object with device/asset attributes/telemetry data
var result = {
// Use deviceName and deviceType or assetName and assetType, but not both.
  deviceName: deviceName,
  deviceType: deviceType,
// assetName: assetName,
// assetType: assetType,
// customerName: customerName,
  groupName: groupName,
  attributes: {
    model: 'Model A',
    serialNumber: 'SN111',
    integrationName: metadata['integrationName'],
    manufacturer: manufacturer
  },
  telemetry: {
    temperature: 42,
    humidity: 80,
    rawData: payloadStr
  }
};

/** Helper functions **/

function decodeToString(payload) {
  return String.fromCharCode.apply(String, payload);
}

function decodeToJson(payload) {
  // covert payload to string.
  var str = decodeToString(payload);

  // parse string to JSON
  var data = JSON.parse(str);
  return data;
}

return result;

Starting with Edge version 4.0, you can:

  • Select the “Library” tab, choose the Vendor and find the appropriate model in the “Model” from the drop-down menus.
  • Or use the default script provided in the converter.
  • The Advanced decoding parameters block:
    • The “Device profile”, “Device label”, “Customer name”, and “Device group name” fields are not mandatory, and you can also use the $-pattern to fill them dynamically.
    • In the Attributes and Telemetry sections specify the keys that should be interpreted as attributes and telemetry, respectively.
    • In the Update only keys list section, define keys whose values will be saved to the database only if they have changed from the previous incoming message. This applies to both Attributes and Telemetry, helping optimize data storage.
  • Click the “Next” button.

Configuring a Downlink data converter is optional and can be omitted if not required. To proceed without configuring the downlink converter, select the “Skip” tab and click the “Skip” button.

Connection

Before you proceed with the Connection configuration, create the Application server API Token at the ChirpStack UI.

  • Open the ChirpStack UI at http://chipstack-server-ip:8080 (e.g., http://10.7.2.193:8080/) and login with your credentials.
    • If the credentials are unmodified, use:
      Username: admin
      Password: admin
  • Go to the Tennant > API Keys section and click the “Add API Key” button.
  • Enter the API key name and click the “Submit” button.
  • Once the API key name is created, copy and paste it into the “Application server API Token” field in the ThingsBoard UI.

On the ThingsBoard UI, continue with the Connection configuration. Fill in the following fields:

  • Base URL: Enter the URL in the format: http://edge-ip:edge-port (e.g., http://10.7.2.193:8080).
  • HTTP endpoint URL: Copy to use it while configuring the ChirpStack application.
  • Application server URL: Enter the address of the application server or the REST API service in the format: http://chipstack-server-ip:8090 (e.g., http://10.7.2.193:8090).
  • Application server API Token: Paste the API Token obtained from the ChirpStack UI.
  • Click the “Add” button.

Configure the integration on the ChirpStack application

To forward device data from ChirpStack to ThingsBoard, the ChirpStack Application Integration should be configured as well. Log in to the ChirpStack UI at http://chipstack-server-ip:8080 to proceed.

Add a device profile

Create a device profile before, before configuring the ChirpStack Application. Fill in the mandatory fields:

  • Go to the Tenant > Device Profiles section and click the “Add device profile” button.
  • On the “General” tab, fill in the following fields:
    • Name: Enter the device profile name.
    • Region: Enter the LoRaWAN regional parameters.
    • MAC Version: The LoRaWAN MAC version the device uses.
    • Regional parameters revision: Parameter set version for your region.
    • ADR Algorithm: Adaptive Data Rate logic.
    • Expect uplink interval (secs): Enter the number of seconds ChirpStack expects the device to send an uplink message.
  • Click the “Submit” button.

Add integration

To configure an integration in the ChirpStack application:

  • Go to the Tenant > Applications section and click the “Add application” button.
  • Enter the application name and click the “Submit” button.
  • Select the “Integrations” tab.
  • Find the HTTP integration in the list and click the ”+” button to add it to the application.
  • Paste the HTTP endpoint URL obtained at the Connection configuration step in the ThingsBoard UI.
  • Click the “Submit” button.

Add the device

Once the integration is added, add the device:

  • Select the “Devices” tab and click the “Add device” button.
  • On the “Device” tab, enter the device name.
  • Device EUI (EUI64): Enter the unique 64-bit (8-byte) identifier assigned to a LoRaWAN device.
    • Select the byte order in which a DevEUI is represented or transmitted (MSB or LSB).
  • Device profile: Select the device profile from the drop-down menu.
  • Click the “Submit” button.

Add the gateway (optional)

In a testing environment, adding gateway can be omitted. However, the device itself will send data through the gateway. To add the gateway:

  • Go to the Tenant > Gateways section and click the “Add gateways” button.
  • Enter the gateway name, gateway ID and the number of seconds at which gateway is expected to send its statistics.

Assign the integration to Edge

Once the integration template is created and the ChirpStack Application Integration is configured, assign the integration template to the Edge instance:

  • Go to the Edge management > Instances section and click the “Manage edge integrations” button.
  • On the “Integration” page, click the “Assign to edge” button. In the “Assign the Integration to the Edge” pop-up window, select the integration from the drop-down menu and click the “Assign” button.
  • To confirm the ChirpStack integration on the Edge, login to your ThingsBoard Edge instance and go to the Integrations center > Integrations section.

To simulate uplinks, use ChirpStack Device Simulator or manually send HTTP message to the ChirpStack Application Server.

The example of the HTTP message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
curl -v -X POST -d '{
    "deduplicationId": "7658d04d-7f1c-4eb6-900b-d948f3061a9d",
    "time": "2025-06-13T12:44:52.653+00:00",
    "deviceInfo": {
        "tenantId": "6e073e9a-6b4f-4747-b22c-7507568debfb",
        "tenantName": "ChirpStack",
        "applicationId": "c3f2c4aa-07c8-4d6c-8a86-7ea2d4a31dca",
        "applicationName": "Sample Application",
        "deviceProfileId": "d8ee6c09-414c-4b2e-888a-8e8f86e3187a",
        "deviceProfileName": "Default device profile",
        "deviceName": "chirp device",
        "devEui": "24e124538b223213",
        "deviceClassEnabled": "CLASS_A",
        "tags": {}
    },
    "devAddr": "01a44c4c",
    "adr": true,
    "dr": 5,
    "fCnt": 153,
    "fPort": 84,
    "confirmed": false,
    "data": "AXU9AwABBAAB",
    "rxInfo": [{
        "gatewayId": "e4e124dadef64eee",
        "uplinkId": 25127,
        "gwTime": "2025-06-13T12:44:52.653481+00:00",
        "nsTime": "2025-06-13T12:44:52.670509207+00:00",
        "timeSinceGpsEpoch": "1433853910.653s",
        "rssi": -68,
        "snr": 13.2,
        "channel": 4,
        "location": {},
        "context": "Hw9+zQ==",
        "crcStatus": "CRC_OK"
    }],
    "txInfo": {
        "frequency": 867300000,
        "modulation": {
            "lora": {
                "bandwidth": 125000,
                "spreadingFactor": 7,
                "codeRate": "CR_4_5"
            }
        }
    },
    "regionConfigId": "eu868"
}'  $YOUR_HTTP_ENDPOINT_URL -H "Content-Type:application/json"

Where:

  • deduplicationId: For testing purposes, you may generate any valid UUID v4. It’s used for simulation realism only and helps debug integrations that rely on it.
  • tenantId: Replace the value with your ChirpStack tenant ID.
  • applicationId: Replace the value with the ChirpStack Application ID.
  • applicationName: Replace the value with the ChirpStack Application name.
  • deviceProfileId: Replace the value with the ChirpStack device profile ID.
  • deviceProfileName: Replace the value with the ChirpStack device profile name.
  • devEui: Replace the value with the Device EUI you add in the ChirpStack device.
  • gatewayId: Replace the value with the ChirpStack gateway ID.
  • $YOUR_HTTP_ENDPOINT_URL: Replace it with the actual value obtained from the ThingsBoard integration.

In production environments, devices automatically send uplink messages at regular intervals or in response to events, without manual intervention.

After the message is sent, a new device will be created in the ThingsBoard Edge user interface.

  • To view the received time-series data, go to the Entities > Devices section, click the device and select the “Latest telemetry” tab.

To view the received uplink message:

  • Go to the Integrations center > Integrations section, click the ChirpStack integration and select the “Events” tab.

The received data can be viewed in the Uplink converter:

  • Go to the Integrations center > Data converters section and click the Uplink converter.
  • On the “Data converter details” page, select the “Events” tab.
  • View the message details in the “In” and “Out” columns.

Next steps