The ChirpStack open-source LoRaWAN Network Server stack provides open-source components for LoRaWAN networks. After integrating ChirpStack with ThingsBoard, you can connect, communicate, process and visualize data from devices in the ThingsBoard IoT platform.
Go to the “Integrations” page of the “Integrations center” section. Click “plus” button to start adding new integration. Select type “ChirpStack” integration and click “Next”;
Note: While debug mode is very useful for development and troubleshooting, leaving it enabled in production mode can significantly increase the disk space used by the database since all debug data is stored there. After debugging is complete, it is highly recommended turning off debug mode.
2. Uplink data converter.
Uplink is necessary in order to convert the incoming data from the device into the required format for displaying them in the ThingsBoard.
Click on the “plus” and on “Create new converter”. To view the events, use “Debug mode”.
In the function decoder field, specify a script to parse and transform data. For our example, use the default decoder function (or use your own configuration) when adding the integration. Then, click “Next”;
One can use either TBEL (ThingsBoard expression language) or JavaScript to develop user defined functions.
We recommend utilizing TBEL as it’s execution in ThingsBoard is much more efficient compared to JS.
vardata=decodeToJson(payload);vardeviceName=data.deviceInfo.deviceName;vardeviceType=data.deviceInfo.deviceProfileName;vargroupName='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.//functiondecodePayload(input){varoutput={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 belowif(input.length>0){for(vari=0;i<input.length;){varchannel_id=input[i++];if(i<input.length){varchannel_type=input[i++];// BATTERYif(channel_id===0x01&&channel_type===0x75){output.telemetry.battery=input[i];i+=1;}// PIRelseif(channel_id===0x03&&channel_type===0x00){output.telemetry.pir=input[i]===0?"normal":"trigger";i+=1;}// DAYLIGHTelseif(channel_id===0x04&&channel_type===0x00){output.telemetry.daylight=input[i]===0?"dark":"light";i+=1;}}}}// --- Decoding code --- //returnoutput;}// --- attributes and telemetry objects ---vartelemetry={};varattributes={};// --- attributes and telemetry objects ---// --- Timestamp parsingvardateString=data.time;vartimestamp=-1;if(dateString!=null){timestamp=newDate(dateString).getTime();if(timestamp==-1){varsecondsSeparatorIndex=dateString.lastIndexOf('.')+1;varmillisecondsEndIndex=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=newDate(dateString).getTime();}}// If we cannot parse timestamp - we will use the current timestampif(timestamp==-1){timestamp=Date.now();}// --- Timestamp parsing// You can add some keys manually to attributes or telemetryattributes.deduplicationId=data.deduplicationId;// You can exclude some keys from the resultvarexcludeFromAttributesList=["deviceName","rxInfo","confirmed","data","deduplicationId","time","adr","dr","fCnt"];varexcludeFromTelemetryList=["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.vartelemetryData=toFlatMap(data,excludeFromTelemetryList,false);varattributesData=toFlatMap(data,excludeFromAttributesList,false);varuplinkDataList=[];// Passing incoming bytes to decodePayload function, to get custom decodingvarcustomDecoding=decodePayload(base64ToBytes(data.data));// Collecting data to resultif(customDecoding.?telemetry.size()>0){telemetry.putAll(customDecoding.telemetry);}if(customDecoding.?attributes.size()>0){attributes.putAll(customDecoding.attributes);}telemetry.putAll(telemetryData);attributes.putAll(attributesData);varresult={deviceName:deviceName,deviceType:deviceType,// assetName: assetName,// assetType: assetType,// customerName: customerName,groupName:groupName,attributes:attributes,telemetry:{ts:timestamp,values:telemetry}};returnresult;
// Decode an uplink message from a buffer// payload - array of bytes// metadata - key/value object/** Decoder **/// decode payload to stringvarpayloadStr=decodeToString(payload);// decode payload to JSON// var data = decodeToJson(payload);vardeviceName='Device A';vardeviceType='thermostat';varcustomerName='Customer C';vargroupName='thermostat devices';varmanufacturer='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 datavarresult={// 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 **/functiondecodeToString(payload){returnString.fromCharCode.apply(String,payload);}functiondecodeToJson(payload){// covert payload to string.varstr=decodeToString(payload);// parse string to JSONvardata=JSON.parse(str);returndata;}returnresult;
You can always change the decoder function after creating it.
3. Downlink data converter.
At the step of adding a downlink converter, you can also select a previously created or create a new downlink converter. But for now, leave the “Downlink data converter” field empty. Click “Skip”;
4. Connection.
To complete adding integration, you need to:
Specify your “Base URL”;
Note down “HTTP endpoint URL” we will use this value later;
Specify “Application server URL” - address of application server or REST API service. Usually, with a standard installation, only the port is changed to 8090;
Specify “Application server API Token” - taken from the application server. To get its we need to open ChirpStack application server UI, navigate to the “API keys” page from the left top menu and create new an API key.
Finally, click “Add” button to complete adding the ChirpStack integration.
Configure integration on your ChirpStack application
In order for data to be transferred from ChirpStack to ThingsBoard, you need to configure an integration in your ChirpStack application.
To create integration on ChirpStack Network server stack, we need to do the following steps:
Go to the “Applications” page in the left menu of the ChirpStack Network server user interface, and click “Add application” button;
Named it and click “Submit” button;
Application created. Now, navigate to the “Integrations” tab;
Find and add a HTTP integration by clicking “+” icon;
Fill in the field with the “HTTP endpoint URL” previously copied from the ChirpStack integration in the ThingsBoard. Then, click “Submit” button.
HTTP integration created.
Processing uplink message
When your device sends an uplink message, a new device will appear in the ThingsBoard user interface.
You will receive an uplink event in the ChirpStack integration.
Received data can be viewed in the uplink converter. In the “In” and “Out” blocks of the “Events” tab:
Use the Dashboards to work with data. Dashboards are a modern format for collecting and visualizing data sets. Visibility of data presentation is achieved through a variety of widgets.
ThingsBoard has examples of several types of dashboards that you can use. Learn more about Solution templateshere.
Advanced usage: downlink
For sending downlink messages from the Thingsboard to the device, we need to define a downlink converter. You can customize the downlink according to your configuration.
Add downlink converter
Let’s consider an example where we send an attribute update message.
One can use either TBEL (ThingsBoard expression language) or JavaScript to develop user defined functions.
We recommend utilizing TBEL as it’s execution in ThingsBoard is much more efficient compared to JS.
// Encode downlink data from incoming Rule Engine message// msg - JSON message payload downlink message json// msgType - type of message, for ex. 'ATTRIBUTES_UPDATED', 'POST_TELEMETRY_REQUEST', etc.// metadata - list of key-value pairs with additional data about the message// integrationMetadata - list of key-value pairs with additional data defined in Integration executing this converter/** Encoder **/// Result object with encoded downlink payloadvarresult={// downlink data content type: JSON, TEXT or BINARY (base64 format)contentType:"TEXT",// downlink datadata:btoa(msg.downlink),// Optional metadata object presented in key/value formatmetadata:{DevEUI:metadata.cs_devEui,fPort:metadata.cs_fPort}};returnresult;
To add the downlink converter to the integration, follow this steps:
Go to the “Integrations” page, click ChirpStack integration to open its details, and enter integration editing mode by clicking the “pencil” icon;
Enter a name for the downlink data converter and click “Create new converter”;
Paste the script to the encoder function section, and click “Add”;
Apply changes.
You can use our example of downlink converter, or write your own according to your configuration:
// Encode downlink data from incoming Rule Engine message// msg - JSON message payload downlink message json// msgType - type of message, for ex. 'ATTRIBUTES_UPDATED', 'POST_TELEMETRY_REQUEST', etc.// metadata - list of key-value pairs with additional data about the message// integrationMetadata - list of key-value pairs with additional data defined in Integration executing this converter/** Encoder **/// Result object with encoded downlink payloadvarresult={// downlink data content type: JSON, TEXT or BINARY (base64 format)contentType:"TEXT",// downlink datadata:btoa(msg.downlink),// Optional metadata object presented in key/value formatmetadata:{DevEUI:metadata.cs_devEui,fPort:metadata.cs_fPort}};returnresult;
To add the downlink converter to the integration, follow this steps:
Go to the “Integrations” page, click ChirpStack integration to open its details, and enter integration editing mode by clicking the “pencil” icon;
Enter a name for the downlink data converter and click “Create new converter”;
Paste the script to the encoder function section, and click “Add”;
Apply changes.
You can add a downlink converter when creating or editing an integration.
Modify Root Rule Chain
In order to send downlink, we’ll use the rule chain to process shared attribute update. Let’s import this rule chain:
Go to the “Rule Chains” page. To import this JSON file, click the “+” icon in the upper right corner of the screen and select “Import rule chain”;
Drag the downloaded JSON file into the import rule chain window. Click “Import”;
The “Downlink to Chirpstack” rule chain will open. Double-click on the “integration downlink” node, specify ChirpStack integration in the “Integration” field and save changes;
Save rule chain by pressing on checkmark.
Now you need to configure the Root Rule Chain:
Open the "Root Rule Chain", and find a "check relation presence" node;
Drag it to the rule chain. Name it "Check relation to ChirpStack integration", select the direction - "To originator", specify "ManagedByOriginator" relation type. Specify ChirpStack integration and click "Add";
Tap on a right grey circle of "message type switch" node and drag this circle to the left side of "check relation presence" node. Here, add the "Attributes Updated" link, and click "Add";
Find a "rule chain" node;
Drag it to the rule chain. Name it "Downlink to Chirpstack", specify "Downlink to Chirpstack" rule chain, and click "Add";
Tap on the right grey circle of the "check relation presence" node and drag this circle to left side of “rule chain” node. Here, select the "True" link, and click "Add". Finally, save Root Rule Chain.
Test downlink
A downlink message will be sent to the integration when an attribute is added or modified.
To see this with an example, we go to the “Devices” page. Select your device and navigate to the “Attributes” tab.
Select “Shared attributes” and click on the “plus” icon to add new attribute. Then enter the attribute name and its value (for example, the key name is ‘downlink’, value: ‘01040203’) and click “Add”.
Received data and data that was sent can be viewed in the downlink converter. In the “In” block of the “Events” tab, we see what data entered and the “Out” field displays messages to device:
Next steps
Getting started guides - These guides provide quick overview of main ThingsBoard features. Designed to be completed in 15-30 minutes.
Data visualization - These guides contain instructions on how to configure complex ThingsBoard dashboards.