(Optional) Creating a LoRaWAN Device Interface (XIF) Definition
LoRaWAN devices are supported with SmartServer 4.0 and higher.
The SmartServer ships with support for a range of LoRaWAN device types. While you can create your own LoRaWAN device interface definitions, device type definitions and associated LNS profiles, they must not conflict with the supplied types and associated LNS device profiles.
This section provides information on how to create a LoRaWAN device interface (XIF) definition and consists of the following:
Supported Devices
The table below lists the device types that were tested with SmartServer 4.0.
Manufacturer | CMS Device Type | Device XIF Name | Program ID | Description | Usage |
---|---|---|---|---|---|
Dragino | LHT65 E1 | LHT65_E1 | 8002A40A5A04E100 | Temperature and Humidity Sensor | Europe, US |
Elsys | Elsys ERS CO2 | Elsys_ERS_CO2 | 8000840A5A04E100 | CO2, Humidity, Temperature, Light and Motion Sensor | Europe, US |
Elsys | Elsys ERS Eye | Elsys_ERS_Eye | 8000840A5A04E101 | Occupancy, Humidity, Temperature, Light and Motion Sensor | Europe, US |
Elsys | Elsys ERS CO2 Lite | Elsys_ERS_CO2_Lite | 8000840A5A04E102 | CO2, Humidity and Temperature Sensor | Europe, US |
Elsys | Elsys ERS Lite | Elsys_ERS_Lite | 8000840A5A04E103 | CO2 Sensor | Europe, US |
Elsys | Elsys ERS VOC | Elsys_ERS_VOC | 8000840A5A04E104 | VOC levels, Light, Temperature, Motion, and Humidity Sensor | Europe, US |
Hawken IO | Leak | Leak | 90FFFF0A5204E100 | Leak Sensor | US |
IMBuilding | IMBuildings People Counter | IMBuildings_People_Counter | 9002C40A5204E100 | People Counter | Europe |
Maddalena | Maddalena SJ EVO | Maddalena_SJ_EVO | 8000FF0A5A04E100 | Water Meter | Europe |
MClimate | MClimate Vicki | MClimate_Vicki | 8003380A5A04E100 | Wet Radiator Control Valve | Europe |
MClimate | MClimate HT | MClimate_HT | 8003380A5A04E101 | Humidity and Temperature Sensor | Europe |
Milesight | Milesight AM319 | Milesight_AM319 | 8002590A5A04E100 | Indoor Air Quality, Temperature, Humidity, Pressure, CO2, Light and Occupancy | Europe, US |
Milesight | Milesight VS121 | Milesight_VS121 | 8002590A5A04E101 | AI Workplace Occupancy Sensor/People Counter | Europe, US |
Milesight | Milesight VS132 | Milesight_VS132 | 8002590A5A04E102 | Time of Flight People Counting Sensor | Europe, US |
Milesight | Milesight WS301 | Milesight_WS301 | 8002590A5A04E103 | Magnetic Contact Switch | Europe, US |
Milesight | Milesight EM300-TH | Milesight_EM300-TH | 8002590A5A04E104 | Temperature and Humidity Sensor | Europe, US |
Milesight | Milesight AM103 | Milesight_AM_103 | 8002590A5A04E105 | Temperature, Humidity and CO2 Sensor | Europe, US |
Milesight | Milesight EM320-TH | Milesight_EM320-TH | 8002590A5A04E106 | Temperature and Humidity Sensor | Europe, US |
Milesight | Milesight UC300 | Milesight_UC300 | 8002590A5A04E107 | IoT Controller | Europe, US |
Milesight | Milesight WTS305 | Milesight_WTS305 | 8002590A5A04E108 | IoT Weather Station | Europe, US |
Milesight | Milesight WTS506 | Milesight_WTS506 | 8002590A5A04E109 | IoT Weather Station | Europe, US |
Milesight | Milesight EM500-LGT | Milesight_EM500-LGT | 8002590A5A04E110 | Light Sensor | Europe, US |
Netvox | Netvox R311FA | Netvox_R311FA | 9001210A5A04E100 | Activity Sensor | Europe, US |
Netvox | Netvox R712 | Netvox_R712 | 8001210A5A04E101 | Temperature and Humidity Sensor | Europe, US |
Netvox | Netvox R718N17 | Netvox_R718N17 | 9001210A5A04E102 | Single Phase 75A Current Sensor | Europe, US |
Netvox | Netvox R718E | Netvox_R718E | 9001210A5A04E103 | Three-Axis Digital Accelerometer and NTC Thermistor | Europe, US |
Netvox | Netvox R718AD | Netvox_R718AD | 9001210A5A04E104 | Temperature Sensor With Probe | Europe, US |
Netvox | Netvox R718N37 | Netvox_R718N37 | 9001210A5A04E106 | Three Phase 75A Current Sensor | Europe, US |
Netvox | Netvox R718N315 | Netvox_R718N315 | 9001210A5A04E105 | Three Phase 150A Current Sensor | Europe, US |
Netvox | Netvox R718AB | Netvox_R718AB | 9001210A5A04E107 | Temperature and Humidity Sensor | Europe, US |
Netvox | Netvox R718DB | Netvox_R718DB | 9001210A5A04E108 | Wireless Vibration Sensor, Spring Type | Europe, US |
Netvox | Netvox R313WA | Netvox_R313WA | 9001210A5A04E109 | 2-Gang Seat Occupancy Sensor | Europe, US |
Synetica | enLink ENL-AIR | enLink_ENL-AIR | 90FFFF0A5A04E101 | Indoor Air Quality Monitor | Europe |
Synetica | enLink ENL-STS-DP | enLink_ENL-STS-DP | 90FFFF0A5A04E101 | Differential Pressure Sensor | Europe |
Guidance on the LoRaWAN Program ID format can be found at Device Type Definition. If a manufacturer is not a member of the LoRa Alliance, then the Program ID starts with 90FFFF.
The .dtp file for each manufacturer's device types shown above can be found here; they can be used as the basis for customer types.
LoRaWAN XIF Definition
To create a LoRaWAN XIF definition, follow these steps:
- Enable LoRaWAN as described in Add a LoRaWAN Interface.
- If you are importing a device type package (.dtp) with a BACnet Type Map (.btm), then enable BACnet as described in Add a BACnet Interface.
- Create a CSV file with a base name and .LoRaWAN, .csv, or .LoRaWAN.csv extension. The base name is the part of the XIF filename that precedes the extension. For example, if the XIF filename is NetvoxR712V2.LoRaWAN, then the base name is NetvoxR712V2 and the extension is .LoRaWAN. And, if NetvoxR712V2.LoRaWAN and NetvoxR712V3.LoRaWAN are identical in content including the program ID, then they will still be treated as two different XIFs because of the different XIF filenames. The filename must match the device profile name in ChirpStack.
- Specify monitoring parameters using the CMS Datapoint Properties widget, or using a Datapoint Monitoring, Logging, and Alarming file (.dla).
A LoRaWAN XIF file has the following three sections:
- A file type specification that identifies the file as a LoRaWAN XIF file.
- A product details section that specifies the program ID, version, product name, and manufacturer for the LoRaWAN device.
- A datapoint list that specifies the datapoints on the device to be available to the SmartServer. The first line of the datapoint list is a header with column headings for a datapoint list.
The following figure illustrates the top portion of a LoRaWAN XIF file:
#filetype,lorawan_xif |
File Type Specification
The file type for a LoRaWAN XIF file must be specified using a #filetype metadata tag name and a LoRaWAN_xif metadata tag value as shown in the example below.
# |
The LoRaWAN driver accepts device type XIF files that use the extension .LoRaWAN, .csv, or .LoRaWAN.csv extension and start with LoRaWAN_xif.
Product Details
The product details identify the product specified by the LoRaWAN XIF file. The product details specification has the following contents:
|
If you open a CSV file with this line in Excel, then it is displayed as two cells, one with #filetype and the other with the LoRaWAN_xif metadata tag value. If you save a LoRaWAN XIF file from Excel, then Excel will append commas to the file type specification. The LoRaWAN driver and the CMS will ignore the commas, if present.
Specify the details as follows:
- <Program ID> – (optional) a 64-bit hexadecimal number used for identifying resource definitions (e.g., 9F:00:96:05:28:04:E1:01) with optional colon and hyphen separators. See Device Type Definition for further information. For LoRaWAN, the LoRa Alliance Vendor ID is used for the manufacturer ID.
<Version> – (optional) version of the device.
<Product Name> – (optional) manufacturer's name for the device.
<Manufacturer> – (optional) device manufacturer.
<Description> – (optional) comment description of the device.
Following is an example product details specification for a Netvox R712 Wireless Outdoor Temperature Humidity Sensor:
|
Datapoint List
The datapoint list specifies the datapoints on the device that are to be available to the SmartServer. The first line of the datapoint list is a header with column headings for a datapoint list. The following example shows a datapoint list with a single datapoint defined in the second line:
Block Name,Block Index,Datapoint Name,Address,Native Type,IAP Type,IAP Field,Write Enable,Downlink Encoder,Downlink Code,Downlink Port,Downlink String Length,Downlink Binary 0 Constant,Downlink Binary 1 Constant,Precision,Description,Native Value 1,Native Value 2,Scaled Value 1,Scaled Value 2,Range Min,Range Max,ASCII Length |
The following table describes the column contents for the datapoint list. You can arrange the columns in any order. The SmartServer uses the name in the heading row entry to identify the contents of the column.
Parameter | Standard/ | Required/ | Description |
Block Name | Standard | Optional | Block XIF name for the datapoint. Blocks can be used to group related datapoints together and can provide a descriptive name to identify the block. The same block can be specified for multiple datapoints to logically group the datapoints. |
Block Index | Standard | Optional | Specifies a numeric block index. The block index can be any positive (>=0) integer value and is not required to be sequential. If it is not specified, then the block index is zero. You can use the block index to create multiple blocks of the same type. Example: for a device with 8 digital outputs, you can define 8 blocks, each named DO, using indexes 0 through 7 (it is not required for the index names to be sequential and they may not start wit zero). |
Datapoint Name | Standard | Required | The datapoint XIF name. This is a descriptive name to identify the datapoint. The name must be unique for all the datapoints in a block containing the datapoint, or in the device if a block is not specified for the datapoint. The datapoint XIF name is the name that appears at the top-level attribute for a datapoint schema defined in an IAP/MQ block object. Example: in the following excerpt from a LON example device block object with an IAP/MQ glp/0/17q4c4n/fb/dev/lon/3/if/Lamp/0 topic, the datapoint XIF name is nviLamp: "nviLamp": { "value": { "value": 0, "state": 0 }, Multiple lines in the datapoint list may specify the same block name, block index, and datapoint name. These lines specify the definition of a single IAP datapoint based on multiple LoRaWAN datapoints. |
Address | Standard | Required | The LNS Decoder JSON key name for the datapoint. Example: temperature |
Native Type | Standard | Optional | Identifies how the data appears in the native protocol. The native type is specified as an IAP type. If it is not specified, then the default native type for a LoRaWAN XIF is float. The native type for use by the LoRaWAN driver is base64. |
IAP Type | Standard | Optional | IAP type that identifies how the data appears in IAP and identifies the datapoint type to be reported in IAP/MQ. If it is not specified, then the default IAP type for a LoRaWAN XIF is float. The IAP type for use by the LoRaWAN driver is hex_string. Example: 0x0101003C003C0100640064 An IAP type may also specify the semantic meaning of a datapoint. Each driver may support a different subset of IAP types for the native types. The following IAP types are defined:
|
IAP Field | Standard | Optional | Name of a field within the IAP type. The field is specified as a path in a nested structure. Example: the CIE luminance field in a SNVT_color_2 datapoint is named color_value/CIE1931_lumen/y. If this field is not specified, then the entire type is used. This field may be used for creating aggregate data types. |
Write Enable | Standard | Required | Specifies if a datapoint is read-only (-) [uplink] or read-write (+) [downlink]. |
Downlink Encoder | Custom | Required* | Specifies the name of an external JavaScript encoder for the datapoint. * Downlink Encoder is required for a read-write downlink datapoint and encoding is required that cannot be handled by binary constant hexadecimal strings. |
Downlink Code | Custom | Optional | Specifies the manufacturer-specific hexadecimal code that is used when writing downlink data. |
Downlink Port | Custom | Required* | Specifies the hexadecimal port that is used when writing downlink data. This parameter may be specified as decimal or hexadecimal. Example: 21 or 0x15 for port 21. * Downlink Port is required for a read-write (Write Enable = +) datapoint. |
Downlink String Length | Custom | Required* | Specifies the length in bytes of the downlink hexadecimal string. * Downlink String Length is required for a read-write (Write Enable = +) datapoint. |
Downlink Binary 0 Constant | Custom | Required* | Raw hex string to send downlink to control the datapoint to off. If the specified string length is longer than the Download String Length value, then the right-most Download String Length characters will be sent. If the string length is shorter than the Download String Length value, then the specified string will be sent first and zero-padded at the end to make the transmitted string length equal to the Downlink String Length. The value will be base64 encoded, before sending to the LNS. Example: 0x4000000000001000000 * Downlink Binary 0 Constant is required for a read-write (Write Enable = +) datapoint with a binary data type and no downlink encoder. |
Downlink Binary 1 Constant | Custom | Required* | Raw hex string to send downlink to control the datapoint to on. If the specified string length is longer than the Download String Length value, then the right-most Download String Length characters will be sent. If the string length is shorter than the Download String Length value, then the specified string will be sent first and zero-padded at the end to make the transmitted string length equal to the Downlink String Length. The value will be base64 encoded, before sending to the LNS. Example: 0x4000000000000000000 * Downlink Binary 1 Constant is required for a read-write (Write Enable = +) datapoint with a binary data type and no downlink encoder. |
Precision | Standard | Optional | Specifies the number of digits of precision. The precision must be a positive or negative integer, or zero. If the precision is positive, then the value is rounded to the specified number of decimal places after the decimal point. If the precision is zero, then the value is rounded to the nearest integer. If the precision is negative, the number is rounded to the left of the decimal point. |
Description | Standard | Optional | Provides a human-readable freeform description of the datapoint or datapoint field. |
Native Value 1 | Standard | Optional* | Specifies the first of two native values for the datapoint. To specify scaling for a datapoint, specify two scaled values for the datapoint. The SmartServer uses the two sets of values to determine the scaling factors for converting a native value to an IAP value, as well as to convert an IAP value to a native value. Example: a native value can be scaled to an IAP value with the following: Scaled Value = ((Native Value - Native Value 1) * ((Scaled Value 2- Scaled Value 1) / (Native Value 2- Native Value 1))) + Scaled Value 1 Example: an IAP value can be scaled to a native value with the following: Native Value = ((Scaled Value - Scaled Value 1) * ((Native Value 2 - Native Value 1) / (Scaled Value 2 - Scaled Value 1))) + Native Value 1 * If a Native Value 1 value is specified, then the Native Value 2, Scaled Value 1, and Scaled Value 2 values are required. |
Native Value 2 | Standard | Optional* | Specifies the second of two native values for the datapoint. If a Native Value 2 value is specified, then the Native Value 2, Scaled Value 1, and Scaled Value 2 values are required. See the Native Value 1 parameter for usage and value requirements. |
Scaled Value 1 | Standard | Optional* | Specifies the first of two scaled values for the datapoint. If a Scaled Value 1 value is specified, then Native Value 1, Native Value 2, and Scaled Value 2 values are required. See the Native Value 1 parameter for usage and value requirements. |
Scaled Value 2 | Standard | Optional* | Specifies the second of two scaled values for the datapoint. If a Scaled Value 2 value is specified, then the Native Value 1, Native Value 2, and Scaled Value 1 values are required. See the Native Value 1 parameter for usage and value requirements. |
Range Min | Standard | Optional | Specifies the minimum scaled value for the datapoint. If specified, then the minimum range has the same behavior as the minimum value defined in the IAP type definition, and the specified minimum overrides the minimum defined in the IAP type definition. If a minimum value is not defined in the IAP type, then the default minimum is undefined, disabling any minimum value range checking. The minimum value is specified as an IAP type, with any type mapping completed including value scaling. A minimum value can only be specified if the IAP value is a scalar type. |
Range Max | Standard | Optional | Specifies the maximum scaled value for the datapoint. If specified, then the maximum range has the same behavior as the maximum value defined in the IAP type definition, and the specified maximum overrides the maximum defined in the IAP type definition. If a maximum value is not defined in the IAP type, the default maximum is undefined, disabling any maximum value range checking. The maximum value is specified as an IAP type, with any type mapping completed including value scaling. A maximum value can only be specified if the IAP value is a scalar type. |
Example
The example shown below is for the Milesight AM319 indoor air quality sensor.
The filename for the example shown below is Milesight_AM319.lorawan.
|
LoRaWAN XIF Development Process
The LoRaWAN XIF development process is a multi-step process as outlined and described in more detail below.
- Create the tenant device profile.
- Create the device.
- Examine the uplink data on the application/# MQTT topic.
- List the datapoints from the device.
- Create the .lorawan file.
- Create the .btm file.
- Create the .dtd file.
- Create the .dtp.
- Import the .dtp into the CMS.
- Validate the uplink data in the Datapoints widget.
- Validate the uplink data in a BACnet browser.
Step 1: Create the Tenant Device Profile
Create the ChirpStack tenant device profile in one of the following ways:
- Download the ChirpStack device profile template library, which may already have the relevant device profile.
- Obtain the device codec from the manufacturer.
Download the ChirpStack Device Profile Template Library
This library will consume about 4% of available space in /dev/root.
- Install git (it may already be installed on your SmartServer).
To determine whether or not git is already installed on your SmartServer, open an SSH session and enter the following command:
git –version
If a response similar to the following is returned, then git is already installed and you can proceed to the next step.
git version 2.25.1
If a response is not returned, then install git using the following commands:
sudo apt update sudo apt install git get –version
Import the library. This process will take a few minutes to complete and will consume about 750MB on the SD Card.
To import the library, from an SSH session, enter the following commands:
For SmartServer 4.1 (Beta) and highersudo mkdir -p /media/sdcard/chirpstack/lorawan-devices/ sudo git clone https://github.com/brocaar/lorawan-devices /media/sdcard/chirpstack/lorawan-devices/ sudo find /media/sdcard/chirpstack/lorawan-devices/ -type d -exec chmod 755 {} + sudo find /media/sdcard/chirpstack/lorawan-devices/ -type f -exec chmod 644 {} + sudo podman run --rm -ti --network host \ --mount type=bind,source=/var/apollo/conf.d/chirpstack/chirpstack/,target=/etc/chirpstack/ \ --mount type=bind,source=/media/sdcard/chirpstack/lorawan-devices/,target=/opt/lorawan-devices/ \ docker.io/chirpstack/chirpstack:4.1.1 -c /etc/chirpstack import-legacy-lorawan-devices-repository -d /opt/lorawan-devices
For SmartServer 4.0sudo mkdir -p /media/sdcard/chirpstack/lorawan-devices/ sudo git clone https://github.com/brocaar/lorawan-devices /media/sdcard/chirpstack/lorawan-devices/ sudo find /media/sdcard/chirpstack/lorawan-devices/ -type d -exec chmod 755 {} + sudo find /media/sdcard/chirpstack/lorawan-devices/ -type f -exec chmod 644 {} + sudo docker run --rm -ti --env REDIS_HOST=redis --env POSTGRESQL_HOST=postgres \ --network lora_gateway_network \ --mount type=bind,source=/var/apollo/data/lora_gw/configuration/chirpstack/,target=/etc/chirpstack/ \ --mount type=bind,source=/media/sdcard/chirpstack/lorawan-devices/,target=/opt/lorawan-devices/ \ docker.io/chirpstack/chirpstack:4.1.1 -c /etc/chirpstack import-legacy-lorawan-devices-repository -d /opt/lorawan-devices
Obtain the Device Codec From the Manufacturer
Example
Milesight codecs are published at https://github.com/Milesight-IoT/SensorDecoders.
If no codec is available, after creating the device, take several snap shots of uplink data and the base64 data. Convert to the base64 data to hex at
- Obtain the ChirpStack 4 javascript device codec from the manufacturer and use the contents when creating the tenant device profile as shown in section 8 below.
If only the TTN codec is available, apply the wrapper shown below to match the ChirpStack 4 calling convention:
ChirpStack 4 Wrapper for TTN Codec// ChirpStack 4 wrapper for TTN Codec } function decodeUplink(input) { return Decoder(input.bytes, input.fPort); }
If only a ChirpStack 3 codec is available, use the following wrapper:
ChirpStack 4 Wrapper for ChirpStack 3 Codec// ChirpStack 4 wrapper for ChirpStack 3 Codec function decodeUplink(input) { return Decode(input.fPort, input.bytes); }
Log into the ChirpStack administration page by entering http://<SmartServer IoT IP address>:8080 in a web browser URL.
Examplehttp://10.0.1.21:8080
The ChirpStack administration login page appears.
- Enter the login username / email and password (default is admin / admin), and click Submit.
- From within ChirpStack, click Device Profiles in the left hand pane, and click Add device profile as shown below. See Discovering, Defining, or Importing Devices for details on how to log into ChirpStack. The device profile name should be the same as the xif file, but without the .lorawan extension. For example: for the xif file Elsys_ERS-Lite.lorawan, the device profile name should be Elsys_ERS-Lite.
- If you have imported the library, click Select device profile template and choose the relevant template using the subsequent dialogs as shown below.
- If you are using a manufacturer supplied codec, then enter the Name and Description fields and click Codec as shown below.
- If the device supports Class-C communications, then click the Class-C tab and enable the Device supports Class-C option and set the Class-C Confirmed downlink timeout period (seconds) as shown below:
- Select Javascript functions from the Payload codec field as shown below. Remove all of the existing code and paste in the manufacturer's codec.
- Click Submit.
Step 2: Create the Device
Perform the following steps to create the device:
- Create a device using the device profile and with the Dev EUI and App Key. See the Provisioning LoRaWAN Gateways and Devices section in Discovering, Defining, or Importing Devices.
- Verify that the join request/join accept phase concludes correctly.
Step 3: Examine Uplink Data
Within an hour, the device should start sending unconfirmed uplink data.
Step 4: List the Datapoints
Perform the following steps to list the datapoints:
From an SSH session, subscribe to the application/# topic using the following command (leave the subscription running for at least an hour since data may be supplied over multiple frames):
mosquitto_sub -t application/#
Uplink data will appear similar to the following:
apollo@smartserver-17qampp:~$ mosquitto_sub -v -t application/# application/4d2b4765-ff93-49c3-93d8-86edcf864bf5/device/24e124710c143951/event/up {"deduplicationId":"8c89914a-04f5-455f-96b5-35d78682c693","time":"2023-06-09T11:51:31.858367905+00:00","deviceInfo":{"tenantId":"52f14cd4-c6f1-4fbd-8f87-4025e1d49242","tenantName":"ChirpStack","applicationId":"4d2b4765-ff93-49c3-93d8-86edcf864bf5","applicationName":"Sensors","deviceProfileId":"9cf4283c-3595-4bf0-98a5-c6e5b3c10db2","deviceProfileName":"Milesight_AM319","deviceName":"AM319","devEui":"24e124710c143951","tags":{}},"devAddr":"005f42c9","adr":true,"dr":5,"fCnt":31,"fPort":85,"confirmed":false,"data":"A2fPAARoYwUAAAbLAgd9gQIIfScACXMYJwp9BAALfQoADH0PAA==","object":{"hcho":0.04,"temperature":20.7,"pressure":1000.8,"pm2_5":10.0,"pm10":15.0,"co2":641.0,"light_level":2.0,"pir":0.0,"tvoc":39.0,"humidity":49.5},"rxInfo":[{"gatewayId":"ac1f09fffe06047c","uplinkId":50882,"rssi":-62,"snr":14.0,"rfChain":1,"location":{},"context":"0BrjFQ==","metadata":{"region_common_name":"EU868","region_name":"eu868"}}],"txInfo":{"frequency":868100000,"modulation":{"lora":{"bandwidth":125000,"spreadingFactor":7,"codeRate":"CR_4_5"}}}}
List the data (highlighted in red in the example above) that is returned in the object JSON data.
If there are errors in the codec, they will be highlighted in the uplink data. Debug accordingly.
Step 5: Create the .lorawan File
Create the .lorawan file as described in the LoRaWAN XIF Definition section above using the datapoints list, selecting a suitable IAP Type, and scaling the value as necessary.
The datapoint JSON key name that is used in the codec is the name that is used in the "Address" field of the .lorawan file, which is the name used by IAP to refer to the datapoint is used in the "Datapoint Name" field. This, in conjunction with scaling, allows the use of standard manufacturer codec names such as "currentma" or vdd and Current or Battery Voltage subsequently in IAP.
Choose names and units that will be useful in both IAP and BACnet. The names of the .lorawan and .btm files should match the name of the Device profile in ChirpStack.
Step 6: Create the .btm File
Create the .btm file as described in (Optional) Creating a BACnet Type Map (BTM).
Step 7: Create the .dtd File
Create the .dtd file as described in Defining Device Types.
Step 8: Create the .dtp File
Create the .dtp file (to include the .lorawan, .btm, .dtd, and associated image file in a compressed .zip file with the .dtp extension) as described Defining Device Types.
Step 9: Import the .dtp File into the CMS
Enable BACnet in the Configuration User Interface along with the LoRaWAN Driver. Import the .dtp file using the SmartServer CMS as described in Defining Device Types. See also Discovering, Defining, or Importing Devices.
Step 10: Validate the Uplink Data in the Datapoints Widget
Once the .dtp file has been imported and the device has sent uplink data, it will appear in the CMS.
Check the data in the CMS Datapoints widget for the device against the uplink data in the application/# MQTT topic using the last update received. Use a suitable device filter to limit the amount of displayed data.
Step 11: Validate the Uplink Data in a BACnet Browser
Using a BACnet browser such as YABE, check that the present value is correct for each datapoint.
LoRaWAN Driver External Downlink Encoder
Overview
When complex downlink hex strings need to be created once a given datapoint is updated by IAP (either via the REST or MQTT APIs, from the CMS, the scheduler or BACnet), an external encoder can be used to achieve do this.
The optional “Downlink Encoder” field in a .lorawan XIF may contain a command that is executed by the LoRaWAN driver in a child process.
The standard input of the child process is always supplied with a JSON object that contains the following key value pairs:
- devEUI (the LoRaWAN devEUI)
- devName (the device name in ChirpStack)
- datapointName (the name of the datapoint that was updated by IAP or an external protocol such as BACnet, as defined in the XIF file)
- inputValue (the current value of the datapoint “datapointName”, that will be used to derive the downlink data to ChirpStack
- datapoint (the current value of all datapoints associated with the device in question)
An example of the JSON object is shown below. For this example, “Light Level” is the current value that IAP has for the datapoint, and “inputValue” is the new requested value for the datapoint.
{ "devEUI": "2c1165fffe7aea35", "devName": "ISDE_ISNN_100L_EU", "datapointName": "Light Level", "inputValue": "42", "datapoint": { "Burn Hours": "194", "Energy": "16", "Light Level": "88", "Voltage": "240.30000000000001", "fPort": "2" } }
The command can also accept arguments, such as one of the datapoint’s XIF field wrapped in curly brackets for example: {Datapoint Name}. The referenced fields are resolved into their respective values when the command is executed.
The Downlink Encoder Command
The command should always start with an executable (e.g., bash / python), followed by a script name and any other arguments. The encoder is not executed in a shell, so any script’s interpreter must be explicitly used.
The executable, which is always the first word in the command, will be searched for in the actual PATH environment variable of the local machine. Commas cannot be used in the command.
An example XIF field containing a valid downlink encoder command is shown below.
python /var/apollo/data/lwd/res/encoder.py {Block Name} {Block Index}
Downlink Encoder Output
The only accepted output on the STDOUT of the encoder process either a hexadecimal string such as “0x102fa8” or a decimal string such as “100200”. Only one line is expected, without whitespace padding. The new line character at the end is ignored. The output of the encoder will be base64 encoded by the LoRaWAN driver and then passed to the MQTT application topic which is being monitored by ChirpStack. ChirpStack will send the update to the device using confirmed service.
Activity Logging
Normal and erroneous activity is logged, and the log can be accessed using the following command:
journalctl -f -u smartserver-lorawan
In case the call was successful (exit code 0), the log will contain an entry with the sent downlink data.
If the encoder process ends with an exit code other than 0, then the downlink message processing is interrupted, and the encoder process’ STDERR (if any) is printed in the log accordingly.
Example of a successful call is shown below.
Example of an unsuccessful call is shown below.
Worked Example for ISDE ISNN-100L Outdoor Luminaire Controller (OLC)
An example of a working downlink encoder for the ISDE ISNN-100L Outdoor Luminaire Controller can be found here.
In the case of the ISNN-100L, it requires a scalar with a value between 010 and 25410 which represents 0% (off) to 100% (full on). However, in the case of the example, the datapoint uses SNVT_lev_cont_f so that the light level can be controlled using 0-100% with IAP ensuring data input validation. The encoder will therefore scale up the value to between 010 and 25410. The output of the encoder is a hex string where the scaled “Input Value” is encapsulated within product-specific pre-fix and post-fix data.
For example, the output hex string from the encoder can be 0x0203027e2aff, where the data format is as follows:
Radix | fport | Control Block | Control Data | Equipment (DALI address) | Light Level | Duration (0xFF = infinite) |
0x | 02 | 03 | 02 | 7e | 2a | ff |
The output string is subsequently encoded by the LoRaWAN driver into Base64 and sent as a confirmed downlink update to the MQTT application/ topic as shown below.
Debugging the Encoder
Debugging can be performed in one of the ways listed below. These methods are described in the sections that follow.
- Writing output to a local file
- Using the cat command with a JSON file
- Using Visual Studio Code
Writing Output to a Local File
You can write output to a local file from the encoder, this method is used in the example. At a minimum, this allows you to inspect the data received from the LoRaWAN driver. This mechanism can be used to derive the JSON file for the next two debugging options.
Using the cat command with a JSON file
You can debug the encoder by executing it on a development machine with an example set of data using a command as shown in the example below.
cat input.json | python encoder.py
The JSON file must contain a valid JSON object, with inputValue being a mandatory key/value pair.
An example of a valid input.json file is shown below.
{ "devEUI": "70b3d52dd3003b86", "devName": "MClimateVicki", "datapointName": "childLock", "inputValue": "32", "datapoint": { "batteryVoltage": "2.8999999999999999", "brokenSensor": "0", "childLock": "0", "relativeHumidity": "58.979999999999997", "sensorTemperature": "22.710000000000001", "targetTemperature": "21" } }
Using Visual Studio Code
To debug in Visual Studio Code, the launch.json file needs to be modified to pass the contents of a valid JSON file to the command line. An example of a valid launch.json is shown below.
{ "version": "0.2.0", "configurations": [ { "name": "Python Debugger: Current File with Arguments", "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/encoder.py", "console": "integratedTerminal", "args": [ "${workspaceFolder}/input.json" ] }, ] }