(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.

ManufacturerCMS Device TypeDevice XIF NameProgram IDDescriptionUsage  
DraginoLHT65 E1LHT65_E18002A40A5A04E100Temperature and Humidity SensorEurope, US
ElsysElsys ERS CO2Elsys_ERS_CO28000840A5A04E100CO2, Humidity, Temperature, Light and Motion SensorEurope, US
ElsysElsys ERS Eye Elsys_ERS_Eye8000840A5A04E101Occupancy, Humidity, Temperature, Light and Motion SensorEurope, US
ElsysElsys ERS CO2 LiteElsys_ERS_CO2_Lite8000840A5A04E102CO2, Humidity and Temperature SensorEurope, US
ElsysElsys ERS LiteElsys_ERS_Lite8000840A5A04E103CO2 SensorEurope, US
ElsysElsys ERS VOCElsys_ERS_VOC8000840A5A04E104VOC levels, Light, Temperature, Motion, and Humidity SensorEurope, US
Hawken IOLeakLeak90FFFF0A5204E100Leak SensorUS
IMBuildingIMBuildings People CounterIMBuildings_People_Counter9002C40A5204E100People CounterEurope
MaddalenaMaddalena SJ EVOMaddalena_SJ_EVO8000FF0A5A04E100Water MeterEurope
MClimateMClimate VickiMClimate_Vicki8003380A5A04E100Wet Radiator Control ValveEurope
MClimateMClimate HTMClimate_HT8003380A5A04E101Humidity and Temperature SensorEurope
MilesightMilesight AM319Milesight_AM3198002590A5A04E100Indoor Air Quality, Temperature, Humidity, Pressure, CO2, Light and OccupancyEurope, US
MilesightMilesight VS121Milesight_VS1218002590A5A04E101AI Workplace Occupancy Sensor/People CounterEurope, US
MilesightMilesight VS132Milesight_VS1328002590A5A04E102Time of Flight People Counting SensorEurope, US
MilesightMilesight WS301Milesight_WS3018002590A5A04E103Magnetic Contact SwitchEurope, US
MilesightMilesight EM300-THMilesight_EM300-TH8002590A5A04E104Temperature and Humidity SensorEurope, US
MilesightMilesight AM103Milesight_AM_1038002590A5A04E105Temperature, Humidity and CO2 SensorEurope, US
MilesightMilesight EM320-THMilesight_EM320-TH8002590A5A04E106Temperature and Humidity SensorEurope, US
MilesightMilesight UC300Milesight_UC3008002590A5A04E107IoT ControllerEurope, US
MilesightMilesight WTS305Milesight_WTS3058002590A5A04E108IoT Weather StationEurope, US
MilesightMilesight WTS506Milesight_WTS5068002590A5A04E109IoT Weather StationEurope, US
MilesightMilesight EM500-LGTMilesight_EM500-LGT8002590A5A04E110Light SensorEurope, US
NetvoxNetvox R311FANetvox_R311FA9001210A5A04E100Activity SensorEurope, US
NetvoxNetvox R712Netvox_R7128001210A5A04E101Temperature and Humidity SensorEurope, US
NetvoxNetvox R718N17Netvox_R718N179001210A5A04E102Single Phase 75A Current SensorEurope, US
NetvoxNetvox R718ENetvox_R718E9001210A5A04E103

Three-Axis Digital Accelerometer and NTC Thermistor

Europe, US
NetvoxNetvox R718ADNetvox_R718AD9001210A5A04E104Temperature Sensor With ProbeEurope, US
NetvoxNetvox R718N37Netvox_R718N379001210A5A04E106Three Phase 75A Current SensorEurope, US
NetvoxNetvox R718N315Netvox_R718N3159001210A5A04E105Three Phase 150A Current SensorEurope, US
NetvoxNetvox R718ABNetvox_R718AB9001210A5A04E107Temperature and Humidity SensorEurope, US
NetvoxNetvox R718DBNetvox_R718DB9001210A5A04E108Wireless Vibration Sensor, Spring TypeEurope, US
NetvoxNetvox R313WANetvox_R313WA9001210A5A04E1092-Gang Seat Occupancy SensorEurope, US
SyneticaenLink ENL-AIRenLink_ENL-AIR90FFFF0A5A04E101Indoor Air Quality MonitorEurope
SyneticaenLink ENL-STS-DPenLink_ENL-STS-DP90FFFF0A5A04E101Differential Pressure SensorEurope

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:

  1. Enable LoRaWAN as described in Add a LoRaWAN Interface.

  2. 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.

  3. 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.

  4. 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
#program_ID,9B008C011E00BB05
#version,beta
#product_name,NetvoxR718N17
#manufacturer,Netvox
#description,xif with all uplink datapoints
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

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.

#filetype,LoRaWAN_xif

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:

#program_ID,<Program ID>
#version,<Version>
#product_name,<Product Name>
#manufacturer,<Manufacturer>
#description,<Description>

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:

#program_ID,9F:00:96:05:28:04:E1:01
#version,v2.21.019
#product_name,Netvox R712
#manufacturer,Netvox #description,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
Sensors,0,Raw Current,rawcurrentma,,SNVT_amp_f,,-,,,,,,,,,1000,10000,1,10,,,
Sensors,0,Scaled Current,currentma,,SNVT_amp_f,,-,,,,,,,,,1000,10000,1,10,,,
Sensors,0,Current Multiplier,multiplier,,SNVT_count_f,,-,,,,,,,,,,,,,,,

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/
Custom

Required/
Optional

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:

  • Base types – bit, boolean, multistate, sint8uint8, uint16, sint16, uint32, sint32, uint64, sint64, floatfloat32, float64, mod10_32, mod10_48, mod10_64.

    Base type values are case-sensitive and must use lower-case letters.

  • Standard types – the types listed in the SNVT, SCPT, and ENUM sections of Online Resource Files - LonMark. As of SmartServer 3.2, the IAP standard resource file set is based on the version 16.00 LonMark standard resource file set and the version 2.01 IoT resource file set. These types are made available to IAP applications by the Resource Publisher. 

    You can specify an IAP type that provides some context for the data point. You can also specify some common, generic IAP types for certain Native Types. For example:

    Native Type

    IAP Type

    UINT8

    SNVT_count

    UNIT16

    SNVT_count

    SINT16

    SNVT_count_inc

    UINT32

    SNVT_count_32

    FLOAT

    SNVT_count_inc_f

    FLOAT

    SNVT_count_f (positive values only)


  • Custom types – types defined by a custom IAP resource file set.  Custom types are defined by an XML format export from the IzoT Resource Editor. Custom IAP resource file sets include a program ID and a scope identifier that specifies a mask for the program ID.  The scope ID may be 3, 4, 5, or 6 corresponding to program ID masks of 0xFFFFFF0000000000 for 3, 0xFFFFFFFFFF000000 for 4, 0xFFFFFFFFFFFFFF00 for 5, and 0xFFFFFFFFFFFFFFFF for 6.  Custom types are specified as <program ID>-<scope>/<type name> where the <program ID>-<scope> specification is the set identifier.

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.

#filetype,lorawan_xif,,,,,,,,,,,,,,,,,,,,,
#program_ID,8002590A5A04E100,,,,,,,,,,,,,,,,,,,,,
#version,beta,,,,,,,,,,,,,,,,,,,,,
#product_name,Milesight AM319,,,,,,,,,,,,,,,,,,,,,
#manufacturer,Milesight,,,,,,,,,,,,,,,,,,,,,
#description,xif for Milesight AM319,,,,,,,,,,,,,,,,,,,,,
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
Sensors,0,PM10,pm10,,SNVT_count_f,,-,,,,,,,,,,,,,,,
Sensors,0,Temperature,temperature,,SNVT_temp_f,,-,,,,,,,,,,,,,,,
Sensors,0,Light Level,light_level,,SNVT_count,,-,,,,,,,,,,,,,,,
Sensors,0,TVOC,tvoc,,SNVT_count_f,,-,,,,,,,,,,,,,,,
Sensors,0,PM2_5,pm2_5,,SNVT_count_f,,-,,,,,,,,,,,,,,,
Sensors,0,Pressure,pressure,,SNVT_count_f,,-,,,,,,,,,,,,,,,
Sensors,0,HCHO,hcho,,SNVT_count_f,,-,,,,,,,,,,,,,,,
Sensors,0,CO2,co2,,SNVT_ppm_f,,-,,,,,,,,,,,,,,,
Sensors,0,PIR,pir,,SNVT_count,,-,,,,,,,,,,,,,,,
Sensors,0,Humidity,humidity,,SNVT_lev_cont_f,,-,,,,,,,,,,,,,,,

LoRaWAN XIF Development Process

The LoRaWAN XIF development process is a multi-step process as outlined and described in more detail below.

  1. Create the tenant device profile.
  2. Create the device.
  3. Examine the uplink data on the application/# MQTT topic.
  4. List the datapoints from the device.
  5. Create the .lorawan file.
  6. Create the .btm file.
  7. Create the .dtd file.
  8. Create the .dtp.
  9. Import the .dtp into the CMS.
  10. Validate the uplink data in the Datapoints widget.
  11. 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.

  1. 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
  2. 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 higher
    sudo 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.0
    sudo 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 

https://www.base64decode.org/ and then with respect to the manufacturer payload specification, develop and test the codec using Visual Studio Code.
  1. 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.

  2. 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);
    }												
  3. 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);
    }


  4. Log into the ChirpStack administration page by entering http://<SmartServer IoT IP address>:8080 in a web browser URL.

    Example
    http://10.0.1.21:8080

    The ChirpStack administration login page appears.




  5. Enter the login username / email and password (default is admin admin), and click Submit.

  6. 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.



  7. If you have imported the library, click Select device profile template and choose the relevant template using the subsequent dialogs as shown below.



  8. If you are using a manufacturer supplied codec, then enter the Name and Description fields and click Codec as shown below.



  9. 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:



  10. Select Javascript functions from the Payload codec field as shown below. Remove all of the existing code and paste in the manufacturer's codec.



  11. Click Submit.

Step 2: Create the Device

Perform the following steps to create the device:

  1. 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.

  2. 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:

  1. 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"}}}}
  2. List the data (highlighted in red in the example above) that is returned in the object JSON data. 

  3. 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.

Downlink Encoder
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.

  1. Writing output to a local file
  2. Using the cat command with a JSON file
  3. 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"
            ]
        },
    ]
}