Vilant Node

Usage: ./reader [-d #] [-subVw]
  -d   Debuglevel (overrides config, syslog levels)
  -s   Debug output to stdout (overrides config)
  -u   Unbuffered debug output (try this if reader stops at start)
  -w   Write values to config file and exit
  -V   Version info

For example, if you want to view logging only locally you can start with -s -d7.

In a typical setup, libraries cannot be copied to system search paths, so they are installed to installation folder. For this reason, the installation path must be specified with LD_LIBRARY_PATH environment variable when running application manually. For example:

LD_LIBRARY_PATH=/data/custom-application/vilantnode /data/custom-application/vilantnode/reader

Configuration file path is hardcoded. NOTE! Editing configurations using config file is not recommended. Preferred way to modify configuration is using Web UI.

Known issues/limitations

Table of Contents

1. RFID Reading Control

RFID reading can be operated using 1) RFID_READER module, 2) HTTP REST/JSON Interface.

RFID_READER module configures RFID reader antennas as data source to module program. RFID_READER module can start antennas during module startup (default behaviour) or use triggering sources like INPUT modules. For more information, see RFID_READER module documentation in Modules Section.

HTTP REST/JSON Interface can be used to start or stop RFID reading on specific antennas. See more in the HTTP REST interface specification.

NOTE: Triggers are not mutually exclusive, i.e., reading state of antennas can be controlled with multiple triggers simultaneously.

RFID reader power levels are configured in Configuration page in WebUI under RFID Section.

2. Common Tag Data JSON Format

This is the native tag data representation in JSON format. It is used by many internal functions and exposed in STORE. Empty/default fields are left out from the JSON.

{
  "EPC": "301588F858009D4473D8D797", // Raw EPC hex data as in the tag
  "URI": "urn:epc:id:sgtin:6438422.000629.19123459991", // Decoded URN format
  "direction": "OUT", // Results of direction detection if it was used
  "readcount": 1, // How many times the tag was read. Usually, 1 since reads are individually processed
  "rssi": -39, // Received signal strength, in dBm
  "antenna": 1, // Antenna which read the tag
  "freq": 3, // Hardware dependent, frequency used for tag read (Note: Q180/Q300 report read slot)
  "phase": 3732, // Phase angle if supported by hardware
  "dopplerfreq": 7, // Doppler frequency if supported by hardware
  "timestamp": "2019-09-23T07:40:05.520Z" // Timestamp when tag was seen
}

3. Binary Arithmetic For Antennas

RFID_CONNECTED_ANTENNAS config parameter is a bitmask of connected antennas so one number tells state for all the antennas. The value is used to enable antenna status reporting for defined antennas.

Lowest bit means antenna 1, second lowest means antenna 2 and so on.

In Q180/Q300 the antennas 1-4 are the external antenna ports. Internal antenna is ports 5-8 with different polarizations: 5 RHCP, 6 LHCP, 7 horizontal, 8 vertical.

Some examples:

Config Antenna numbers
1 1
2 2
3 1, 2
15 1, 2, 3, 4
16 5
240 5, 6, 7, 8
255 1, 2, 3, 4, 5, 6, 7, 8

4. HTTP And REST Interfaces

App contains internal HTTP server that by default responds in port 8080 and 8081 (SSL). Both are configurable (HTTPD_PORT / HTTPSD_PORT).

In Impinj R700 readers the default ports are 8088 and 8089 (SSL) due to Impinj reserving 8080 for its internal use.

Accessing any URL in above ports requires basic authentication. There is one, hard-coded, user, ‘admin’ and a configurable password, with default value ‘vilantnode’.

4.1. POST /rfid/start

Starts RFID reading with reader.

Parameters | Name | Type | Description | Mandatory | |——|——|————-|———–| | reader | integer | Index of the reader. Defaults to internal reader. | | | antennas | array of integers | Antennas to be started. If empty or not defined, then all antennas are activated. | |

Example: Activate all antennas on internal reader

{}

Example: Activate antennas 1 & 2 on reader 1

{
  "reader": 1,
  "antennas": [1, 2]
}

Response | Code | Description | Response type | |——|—————|———–| | 200 | RFID reading was started successfully | JSON |

{
  "message": "RFID reading was started successfully."
}

Error response | Code | Description | Response type | |——|—————|———-| | 409 | Failed to start RFID reading | JSON |

{
  "message": "Failed to start RFID reading."
}

4.2. POST /rfid/stop

Stops RFID reading.

Parameters No parameters. Request must be supplied with empty JSON.

Example

{}

Response | Code | Description | Response type | |——|—————|———–| | 200 | RFID reading was stopped successfully | JSON |

{
  "message": "RFID reading was stopped successfully."
}

Error response | Code | Description | Response type | |——|—————|———-| | 409 | Failed to stop RFID reading | JSON |

{
  "message": "Failed to stop RFID reading."
}

4.3. POST /rfid/write

Writes an EPC (Electronic Product Code) into a tag. Tag can be defined or selected by the reader. Writing RF power is configured with RFID_WRITE_POWER configuration parameter. Write operation will use all connected antennas defined with RFID_CONNECTED_ANTENNAS.

NOTE! Write command will interrupt any reading operation and will not resume reading automatically.

Parameters | Name | Type | Description | Mandatory | |——|——|————-|———–| | newEpc | string | EPC to be written to the tag. | X | | oldEpc | string | EPC of the old tag. If undefined or empty string, then reader will select the first tag it sees. | |

Example

{
  "newEpc": "300C69F6BC7115D9DEBD01C7"
}

Response | Code | Description | |——|—————| | 200 | operation was successful | JSON |

{
  "message": "Writing was successful"
}

Error response | Code | Description | Response type | |——|—————|———-| | 400 | Parsing request or parameters has failed | JSON |

{
  "message": "Mandatory json element '}' is missing at position..."
}

Error response | Code | Description | Response type | |——|—————|———-| | 409 | Write operation has failed | JSON |

{
  "message": "Failed to write <newEpc>."
}

4.4. GET /rfid/store

Returns list of tags currently in all STORE modules. This can be used for querying buffered tags from the Node application.

Response | Code | Description | Response type | |——|————–|———-| | 200 | Tags in STORE module’s buffer are returned with the response. | JSON |

{
   "tags": {
    "<firstmoduleid>": [
      {
         "EPC":"300C69F6BC7115D9DEBD01C7",
         "URI":"urn:epc:id:sgtin:111111111.1111.111111111111",
         "readerid": 0,
         "readcount":1,
         "rssi":-39,
         "antenna":1,
         "freq":3,
         "phase":3732,
         "dopplerfreq":7,
         "timestamp":"2023-08-23T07:40:05.520Z",
      }
      ...
   ],
   "<secondmoduleid>": [
     {
         "EPC":"300C69F6BC7115D9DEBD01C7",
         "URI":"urn:epc:id:sgtin:111111111.1111.111111111111",
         "readerid": 0,
         "readcount":1,
         "rssi":-39,
         "antenna":1,
         "freq":3,
         "phase":3732,
         "dopplerfreq":7,
         "timestamp":"2023-08-23T07:42:10.124Z",
      }
      ...
   ]
  }
}

4.4.1 GET /rfid/store/

Returns list of tags buffered in the specific STORE module.

4.5. GET /rfid/status

Returns status of reader, errors, GPIO devices and RFID antennas. Antenna state reporting is reader dependent feature and is not reliable. Antenna and GPIO names come from config file.

Response | Code | Description | Response type | |——|————–|—————| | 200 | Current reader status is returned with response | JSON |

{
  "reader": {
    "location": "VilantNode",
    "deviceid": "1213361d",
    "error": 0,
    "time": "2023-08-23T10:41:31.405+0000",
    "uptime": 51,
    "version": "TEST-0.1.0.c3535074-TEST",
    "driver": "LLRP",
    "license": false,
    "model": "Impinj Speedway R420",
    "serial": "370-17-14-0279",
    "licenseId": ""
  },
  "antenna": [
    {
      "port": 1,
      "enabled": 1,
      "state": 1,
      "name": "",
      "action": "TRANSIT"
    },
    ...
  ],
  "iodevs": [
        [
            {
                "device": 0,
                "port": 1,
                "state": 0,
                "name": "GPI 1",
                "type": "GPIO_IN",
                "inverted":1
            },
            ...
            {
                "device": 0,
                "port": 1,
                "state": 1,
                "name": "GPO 1",
                "type": "GPIO_OUT",
                "inverted":1
            },
            ...
        ],
        [
            {
                "device": 1,
                "port": 0,
                "name": "C0/1",
                "type": "GPIO_IN",
                "state": 0,
                "inverted":1
            },
            ...
        ]
  ],
  "dm": [
    {
      "name": "DEVICE",
      "status": "OK",
      "message": ""
    },
    {
      "name": "Reader",
      "status": "OK",
      "message": ""
    },
    ...
  ]
}

4.6. POST /gpo

Controls GPO statuses of the reader or external GPIO devices. Reader GPOs are controlled with device id 0. External devices start by index 1 and get IDs based on order they are configured in IO_DEVICES config.

Request type JSON body

Parameters | Name | Type | Description | Mandatory | |—–|——-|————-|———–| | device | integer | Which device is controlled. Can be reader (device number 0) or external GPIO devices. If undefined, then reader will be selected | | | port | integer | Defines which port is controlled. | X | | state | integer | Defines to which state port is changed to. 1 = ON, 0 = OFF. | X |

Example

{
  "device": 0,
  "port": 1,
  "state": 0
}

Response | Code | Description | Response type | |——|—————|————| | 200 | GPO was successfully changed. | JSON |

Response body

{
  "message": "Output was changed successfully."
}

Error response | Code | Description | Response type | |——|—————|———-| | 400 | Parsing request or parameters has failed | JSON |

{
  "message": "Parameter <paramName> is missing or is not integer"
}

Error response | Code | Description | Response type | |——|—————|———-| | 409 | Failed to change gpo state | JSON |

{
  "message": "Failed to change output."
}

4.7. GET /kill

Allows user to control reader state using url query arguments: - /kill - Disconnect LLRP reader connection. Easy to use with multireader. (Only works with LLRP readers) - /kill?value=stop - Stop app (after a few seconds). - /kill?value=reboot - Execute “reboot” i.e., reboot the whole reader.

Response | Code | Description | Response type | |——|—————|———–| | 200 | Request is received and has been handled | TEXT |

Example response: Stop app

OK, Stopping app, bye

4.8. POST /inject

Allows you to inject tag reads as if they come from reader normally.

Use POST with JSON data in common tag data format as described above. Supports both single tag data and array of tags in the message. If timestamp is specified as now it will be replaced with current time when processing.

NOTE - Extra fields can be left out, they default to zero. Note that customdata is not free()’d by default and specifying it will typically cause memory leak.

Parameters

The endpoint uses the common tag data JSON format provided above. While some of the fields are not necessary, there are a few mandatory fields that need to be supplied with the request:

Name Type Description Mandatory
EPC string EPC to be written to the tag. X
timestamp string ISO 8601 formatted timestring or “now” indicating current reader time X
antenna integer Source antenna which identified tag X

Example

{
  "EPC": "341588F50C6A94D74F4318BA",
  "timestamp":"now",
  "antenna": 3
}

Response | Code | Description | Response type | |——|—————|———–| | 200 | Request is received and has been handled | TEXT |

Example response:

OK

4.9. GET /rfid/config

Generic reader configuration interface. This can be used to change any parameter similarly as in config.json file. GET method returns list of all parameters and POSTing same data changes them (only name and value used in POST). New parameters are saved to config.json and reader is reconfigured or restarted to apply new configs.

Response | Code | Description | Response type | |——|—————|———–| | 200 | Configs are successfully returned | JSON |

{
  "configs": [
    {
      "name": "MY_NAME",
      "value": "FORKLIFT",
      "type": "string",
      "description": "Reader name/Location ID"
    },
    {
      "name": "RFID_POWER",
      "value": "1200",
      "type": "string",
      "description": "Transmit power. Either single number in mBm or for each antenna separated with ,"
    },
    ....
  ]
}

4.10. POST /rfid/config

Generic reader configuration interface. This can be used to change any parameter similarly as in config.json file. GET method returns list of all parameters and POSTing same data changes them (only name and value used in POST). New parameters are saved to config.json and reader is reconfigured or restarted to apply new configs.

NOTE: When Provisioning (DM_ENABLED) is used it will overwrite these configs after a while.

Parameters
Top level message structure: | Name | Type | Description | Mandatory | |——|——|————-|———–| | configs | Array of ConfigParam objects | | X |

ConfigParam structure: | Name | Type | Description | Mandatory | |——|——|————-|———–| | name | string | Config name | X | | value | string or integer | New value for the config | X |

Example

{
  "configs": [
    {
      "name": "MY_NAME",
      "value": "FORKLIFT",
    },
    {
      "name": "RFID_POWER",
      "value": "1200",
    },
    ....
  ]
}

Response | Code | Description | Response type | |——|—————|———–| | 200 | Request has been handled. Errors are indicated in the message structure. | JSON |

Response body

{
  "ret": "Config set OK",
  "success": true
}
Code Description Response type
400 Reader failed to parse request body or found problems with configs. JSON

Error response

{
  "ret": "Param parsing failed at ...",
  "success": false
}
Code Description Response type
403 Reader forbids the use of advanced features with Lite license JSON

Error response

{
  "ret": "Module 'REMAP_ANTENNA' in 'MODULES.MODULES_JSON' requires Pro license.",
  "success": false
}
Code Description Response type
409 Reader failed to store new config JSON

Error response

{
  "ret": "Configs applied but failed to save. Disk might be full.",
  "success": false
}

5. MQTT (Pro License)

Vilant Node provides some standard functionality over MQTT for status monitoring, configuration management and RFID controls.

Standard configuration parameters:

<ID> in topics is replaced with the ID of the reader. ID is same what is used for license activation.

Default client side Keep Alive time is set to 400 seconds.

5.1. App Status - /tvs/<ID>/status

Periodically updated with the latest status of the reader and Last Will and Testament (LWT) is used to provide status offline on unexpected disconnect.

Status update period can be changed with parameter DM.DM_UPDATE_INTERVAL. This parameter controls how often Vilant Node will send status updates to DM and MQTT interfaces.

Example contents:

{
  "name": "Testdev-1733733138.811968",
  "id": "5d9d1d2a",
  "application": "VilantNode/Turck",
  "version": "1.0.0.6d9d683d",
  "status": "online",
  "last_updated": "2024-12-09T10:47:28Z",
  "last_tag_read": "2024-12-09T10:46:47Z",
  "components": [
    {
      "name": "DEVICE",
      "status": "ERROR",
      "message": "Component(s) with errors: Modules"
    },
    {
      "name": "Ambient temperature",
      "status": "OK",
      "message": ""
    },
    ...
  ]
}

5.2. Tag Events

Tag events are sent from the MQTT sender module.

5.3. Configuration Data - /tvs/<ID>/config

Receives app configuration when: 1) Requested with cmd/config/get topic. 2) Configuration gets changed. 3) Vilant Node starts up.

Configurations are as in following example, in JSON format:

{
  "configs": [
    {
      "hiername": "GENERAL.MY_NAME",
      "name": "MY_NAME",
      "value": "RFID_Gate_1",
      "type": "string",
      "description": "Reader name/Location ID"
    },
    {
      "hiername": "RFID.RFID_POPULATION",
      "name": "RFID_POPULATION",
      "value": 10,
      "type": "integer",
      "description": "Gen2 population size"
    },
    {
      "hiername":"DM.ENABLED",
      "name":"DM_ENABLED",
      "value":false,
      "type":"boolean",
      "description":"Enables Device Manager features, including sending of device status updates, remote configuration and software updates"
    },
    {
      "hiername": "GPIO.IO_DEVICES_JSON",
      "name": "IO_DEVICES_JSON",
      "value": [{"id": 0, "type": "DEBUS", "dxpPinDirection": "0011"}],
      "type": "array",
      "description": "IO device configuration data. See README for instructions."
    },
    {
      "hiername":"MODULES.JSON",
      "name":"MODULES_JSON",
      "value":{"modules": {}, "connections": []},
      "type":"json",
      "description":"Module program configuration. See README for instructions."
    },
    ...
  ]
}

5.4. Configuration Get Request - /tvs/<ID>/cmd/config/get

Request current configuration to be sent to /tvs/<ID>/config topic. Message format is not specified, so any data sent to topic will result configuration being sent.

5.5. Configuration Set Request - /tvs/<ID>/cmd/config/set

Setting configs can be done with the same data format as received from config-topic. If both name and shortname are specified shortname takes precedence.

Note: Reader will immediately take new configs into use and might reboot, if required.

5.6. RFID Reader Start - /tvs/<ID>/cmd/rfid/start

Will start RFID reading on specific antennas on specific reader.

Name Type Description Mandatory
reader integer Index of the reader. If empty or not defined, then internal reader will be used.
antennas array of integers Antennas to be started. If empty or not defined, then all antennas are activated.

Example: Activate all antennas

{}

Example: Activate specific antennas on specific reader

{
  "reader": 0,
  "antennas":[1,3]
}

5.7. RFID Reader Stop - /tvs/<ID>/cmd/rfid/stop

Will stop RFID reading on specific antennas on specific reader.

Name Type Description Mandatory
reader integer Index of the reader. If empty or not defined, then internal reader will be used.
antennas array of integers Antennas to be started. If empty or not defined, then all antennas are activated.

Example: Deactivate all antennas on internal reader

{}

Example: Deactivate antennas 1 & 3 on reader 0 (internal reader)

{
  "reader": 0,
  "antennas":[1,3]
}

5.8. GPIO Events Data - /tvs/<ID>/gpio

GPIO events for detected inputs and set outputs will be sent here.

{
  "type": "output",
  "dev": 0,
  "pin": 1,
  "state": 1
}

5.9. GPIO Event Request - /tvs/<ID>/cmd/gpio

Will control GPIO ports. Contents same as in /tvs/<ID>/gpio topic. Specifying type: input will generate fake input events.

6. Modules

Modules should be defined with WebUI or DM in param MODULES.MODULES_JSON. Structure is:

{
  "modules": {
    "id1": {... first module ...},
    "id2:" {... second module ...},
    ...
  },
  "connections": [
    {... first connection ...},
    {... second connection ...},
    ...
  ]
}

Common parameters for modules:

Field Type (value range) Description Required
id str Unique id for the module. X
type str Module type X
loglevel integer (1 - 8) Module specific log level. NOTE! Modules use their own log level, not the application log level. 6 = INFO (default), 7 = DEBUG, 8 = TRACE.
{
  "type": "RFID_READER",
  "logLevel": 6,
  "data": {
    ... module type specific data ...
  }
}

NOTE! Invalid values in configuration can result in incorrect operation.

6.1. RFID_READER

Field Type (value range) Description Required
reader integer (Only ID 0 allowed for now) Unique id for the reader X
antennas array of integers Antennas used by the module. Module will accept events only from the specified antennas. Accepted values for antennas are 1 - 32. X
readingActive bool Starting state for antennas. Default is true (= constant reading)
{
  "type": "RFID_READER",
  "data": {
    "reader": 0,
    "antennas": [1, 2],
    "readingActive": true
  }
}

6.2. DUPLICATE

Duplicate filters tags based on 1) time window or 2) count of unique tags.

Field Type (value range) Description Required
count integer (0 - 2147483647) Duplicate buffer. If the maximum number of tags is exceeded, oldest tags are removed from the first. Either “count” or “timeWindowMs” is required.
timeWindowMs integer (0 - 2147483647) Same tag is filtered until “timeWindowMs” amount of time. Every new read will restart timewindow. Value should be given in milliseconds. Either “count” or “timewindow” is required.
first boolean Extra mode for “timeWindowMs” mode. If enabled, timestamp is taken from first time tag is seen and not updated with new events until “timewindow” amount of time has passed. Disabled by default.
{
  "type": "DUPLICATE",
  "data": {
    "timeWindowMs": 10000,
    "first": true,
  }
}

6.3. EVENTSENDER (Pro License)

The EVENTSENDER is sending tag events to a specified endpoint. The message structure follows the TVS REST/JSON asset interface.

Field Type Description Required
endpoint string Endpoint address to HTTP REST Server including protocol, credentials, hostname and path. X
operation string Operation used for asset event. Can be any but if sending to TVS servers, then need to follow interface specification. If undefined or empty, then defaults to MOVE.
site string Site parameter for the TVS asset event. Describes higher level detail of location.
username string HTTP basic authentication username.
password string HTTP basic authentication password.
locations object Define location to antennas mapping. Keys object properties is the location name and value is the antenna. X
locations.{key} string Key value of the property defines location name. X
locations.{value} array of integers Value of the property defines antenna numbers for the location defined in key field. Allowed antenna values are 1 - 32. X
{
  "type": "EVENTSENDER",
  "data": {
    "endpoint": "https://username:password@some.host/path/to/endpoint",
    "operation": "MOVE",
    "locations": {
      "Loc1": [1,3],
      "Loc2": [2,4],
      ...
      // Usable only with SEEN module's lost events
      "LostLoc1": [101,103],
      "LostLoc2": [102,104],
    }
  }
}

6.4. STORE

Stores tag events that can be collected with the HTTP REST interface. The module collects tag events, meaning that the same tag can be stored multiple times.

Supports either time window based or count based mode.

See section 4.4 for more information on REST calls.

Field Type (value range) Description Required
timeWindowMs integer (1 - 2147483647) “timewindow” field defines how long tag event is accessible by STORE module. Tag event will be dropped after expiration. X
keep boolean “keep” field defines whether a call to HTTP interface would remove tag event or should module keep it in memory until expiration. Defaults to false.
count integer (1 - 2147483647) “count” field defines how many tag events can be stored by module.
{
  "type": "STORE",
  "data": {
    "timeWindowMs": 10000,
    "count": 100,
    "keep": false, // Optional
  }
}

6.5. RSSI

The RSSI module drops tag events based on the Received Signal Strength Indicator (RSSI) values.

Field Type Description Required
limits array of integers “limits” field collects threshold limits for RSSI values. Each value entry reflects a value for antenna. If value is not defined, 1st value is used for missing antennas. Value range for limits is -128 - 0. X
max boolean “max” field inverses threshold. By default, threshold drops tags smaller than limit (=MIN limit). Max inversion would include tag events weaker than limit.
{
  "type": "RSSI",
  "data": {
    "limits": [-50, -55, -50], // Antenna 1 = -50, Ant2 = -55, Ant3 = -50, Ant4 = -50 (because Ant1 = -50)
    "max": false
  }
}

6.6. EPCHEX

The EPCHEX module can be used to filter RFID tag reads based on EPC features. The module supports filtering based on regex patterns or lengths.

Field Type (value range) Description Required
pattern string RegEx pattern for filtering data.
not boolean Extension for pattern field. By default, pattern finds for matching data. With this field set to true, pattern will find for not matching data.
epcLength integer (0 - 2147483647) Allowed length of the EPC in bits. Exact match. Length value must be multiple of 8, i.e. length is defined in even bytes.
{
  "type": "EPCHEX",
  "data": {
    "pattern": "^(30|31)",
    "not": false,
    "epcLength": 96
  }
}

Examples of RegEx use:

"pattern": "1234"                     i.e. EPC should contain 1234, at any place in the EPC
"pattern": "^34"                      i.e. EPC should start with 34
"pattern": "000000", "not": true      i.e. EPC should not contain 000000
"pattern": "(333|444|555)"            i.e. EPC should contain 333 or 444 or 555
"pattern": "^(333|444)"               i.e. EPC should start with 333 or 444
"pattern": "^341(333|444)"            i.e. EPC should start with 341 which is followed by 333 or 444
"pattern": "(333|444)", "not": true   i.e. EPC should not contain 333 or 444

6.7. DIRECTION (Pro License)

Module implementing direction detection. There are four modes:

Field Type (value range) Description Required
stateTimeoutMs integer (1 - 2147483647) State timeout milliseconds. Depends on mode. X
flushTimeoutMs integer (1 - 2147483647) Flush timeout milliseconds. At any time in the process, if the tag is not seen since ‘flush’ milliseconds, it is removed from memory. X
mode integer (1 - 4) Mode selected. Accepts only values 1-4, according to description above. X
zones Object Defines antenna & zone mapping. Key value defines the zone name and value defines a list of antennas mapped represent the zone. Antennas can be assigned to only one zone each. Allowed antenna value range is 1 - 32. X
readCount integer (1 - 2147483647) Extension to MODE3D functionality. Required if using MODE3D. X
sendExtraEvents bool Extension to MODE3 & MODE3D functionality. Defaults to enabled.
useUserMemory bool Singulate tags based on user memory instead of EPC. Tag is dropped if user memory is missing.
debug bool Enable websocket debugging of direction detection.
{
  "type": "DIRECTION",
  "data": {
    "stateTimeoutMs": 1000,
    "flushTimeoutMs": 18000,
    "mode": 1, // One of [1, 2, 3, 4]
    "zones": {
        "IN": [2,4], // Antennas
        "OUT": [1,3]
        ...
    }
  }
}

6.8. TAG MOVEMENT (Pro License)

Module passing tags whose rssi and phase-angle median absolute deviation (MAD) are within thresholds.

Field Type (value range) Description Required
readcount integer (1 - 1023) Minimum size of population from where MAD is calculated. X
timewindowMs integer (1 - 2147483647) Millseconds, how long to keep tag in population. X
phase Object Phase angle set thresholds. x
phase.lowThreshold integer (0 - 127) Low threshold for phase angle. X
phase.highThreshold integer (0 - 127) High threshold for phase angle. X
rssi Object Rssi set thresholds. x
rssi.lowThreshold integer (0 - 127) Low threshold for rssi. X
rssi.highThreshold integer (0 - 127) High threshold for rssi. X

Accept tags if there are enough reads within timewindow on same antenna and calculated deviations for reads are within configured thresholds for either phase angle, RSSI or both.

Note: Config must contain at least one of ‘phase’ or ‘rssi’ objects. Both thresholds must be supplied. Keeping timewindow too wide may slow down calculation, series limit is 1023 data points per EPC per antenna. If, during the time limit, more entries than 1023 would enter the series, then new entries are added and oldest are dropped.

{
  "type": "TAGMOVEMENT",
  "data": {
    "readcount": 10,
    "timewindowMs": 3000,
    "phase": {
        "lowThreshold": 0,
        "highThreshold": 10
    },
    "rssi": {
        "lowThreshold": 0,
        "highThreshold": 1
    }
  }
}

6.9. ANTENNA

The ANTENNA module filters RFID tag data based on accepted antennas.

Field Type Description Required
accept array of integers Events from defined antennas are accepted to pass. Allowed antenna values are 1 - 32. X
{
  "type": "ANTENNA",
  "data": {
    "accept": [1, 2, 3]
  }
}

6.10. INPUT

The INPUT module provides functionality to listen input events from a specific IO device. NOTE! IO devices need to be configured separately.

Field Type (value range) Description Required
device integer (0 - 8) The target IO device ID. X
port integer (0 - 31) The target IO port. X
inverted boolean Defines if status should be inverted. Useful, for instance, for adjusting triggering edge.
{
  "type": "INPUT",
  "data": {
    "device": 0,
    "port": 1,
    "inverted": false
  }
}

6.11. OUTPUT

The OUTPUT module is used for pulsing a specific output port on an IO device. Duration will configure the period for ON and OFF signals in pulse.

Field Type Description Required
ioDeviceId integer (0 - 8) The target IO device ID. If undefined, will default to internal reader (0).
port integer (0 - 31) The target IO port. X
pulses integer (0 - 2147483647) Defines the number of pulses to output port. One pulse includes 1 ON and 1 OFF signal, each lasting durationMs (total time 2* durationMs). X
durationMs integer (1 - 2147483647) Defines the duration for both ON or OFF signals during pulse. Value in milliseconds. Default duration is 500 milliseconds.
{
  "type": "OUTPUT",
  "data": {
    "port": 1,
    "pulses": 10,
    // Optional values
    "ioDeviceId": 0,
    "durationMs": 10000, // milliseconds
  }
}

6.12. EPCMANGLE

The EPCMANGLE module can be used to sanitize some parts of the EPC for the next module’s use. The module is not modifying the EPC on the tag itself.

NOTE! Indexing starts from the beginning, so remember to pad both mask and replacement values with zeroes until the desired position.

Field Type Description Required
mask string Hex string defining what bits are selected for modifying. X
replacement string Hex string defining new bit values for the fields. X
{
  "type": "EPCMANGLE",
  "data": {
    "mask": "FFFF",
    "replacement": "0000"
  }
}

6.13. SEEN

The SEEN module provides a way to get events from both when RFID tag appears and disappears.

Module will send lost tags to next modules with antenna set to 100 + antenna number. For instance, if a tag has been first identified with antenna 2, then lost event will be sent to next modules having antenna number 102.

Field Type (value range) Description Required
lostTimeMs integer (0 - 2147483647) How long time to wait without tag events until RFID tag is considered to have disappeared. Value in milliseconds. X
antennaUnique boolean Should each RFID tag be monitored on antenna level. Disabled by default.
{
  "type": "SEEN",
  "data": {
    "lostTimeMs": 1000, // milliseconds
    // Optional
    "antennaUnique": false
  }
}

6.14. MQTT Sender (Pro License)

The MQTT Sender module can be used for sending RFID tag events to specific MQTT topic. Connection details are defined in configuration page under MQTT and are common for all instances of MQTT sender modules. Message format is the common tag data format as described in Section 2.

Field Type (value range) Description Required
topic string MQTT topic to send the RFID tag event. Using placeholder value <ID> would be replaced with device ID, e.g. value /tvs//reads would result in value /tvs/022f4fd3/reads, if deviceid is 022f4fd3. X
qos integer (0 - 2) Quality of Service parameter for MQTT message. Defaults to 0.
{
  "type": "MQTT",
  "data": {
    "topic": "tvs/reader1", 
    "qos": 0, // Optional, defaults to 0
  }
}

6.15. TIMEOUT

The TIMEOUT module allows you to create timeout-based activations of triggers. After receiving a trigger with the activeTrigger event, the module will send that trigger and wait until timeout. After timeout, the module will send a non-active trigger. One useful scenario for this module is to create a trigger for the RFID_READER module that is activating RFID reading only for the specified timeout period. If new active trigger is received when timeout period is still active, then the timeout period is restarted.

NOTE! The TIMEOUT module supports only TRIGGER connections.

Field Type (value range) Description Required
timeoutMs integer (10 - 2147483647) How long to keep trigger active until timeout. X
activeTrigger integer (0 - 1) Defines whether active trigger with timeout is ON (1) or OFF (0). Defaults to ON (1).
allowCancellation bool Defines whether timeout period can be cancelled before timeout with non-active trigger events. Defaults to disabled.
{
  "type": "TIMEOUT",
  "data": {
    "timeoutMs": 1000,
    "activeTrigger": 1, // Optional, defaults to 1
    "allowCancellation": false, // Optional, defaults to false
  }
}

6.16. DELAY

The DELAY module allows to delay trigger signal sent to next modules. If delayed activation is ongoing, then each new trigger signal will reset delay timer. By default, the trigger status opposite to delayedTrigger will be ignored if activation is ongoing. However, if allowCancellation is enabled, then delayed trigger is cancelled if opposite trigger status is captured during delay period.

This module is useful to delay handling of data buffer. For instance, one use case could be delaying send action of RFID events in EVENTSENDER module until all relevant data has been processed.

NOTE! DELAY module supports only TRIGGER connections.

Field Type (value range) Description Required
delayTimeMs integer (0 - 2147483647) How long to delay sending of the delayed trigger event. X
delayedTrigger integer (0 - 1) Defines whether delayed trigger event is ON (1) or OFF (0). Defaults to ON (1).
allowCancellation bool Defines whether delayed trigger will be cancelled if receiving non-delayed trigger event before delay timeout. Defaults to disabled.
{
  "type": "DELAY",
  "data": {
    "delayTimeMs": 1000,
    "delayedTrigger": 1, // Optional, defaults to 1
    "allowCancellation": false, // Optional, defaults to false
  }
}

6.17. AND & OR

The AND & OR modules allow creating logical gates for triggering. These modules will forward trigger with the value calculated from current state of system.

For instance, - AND module requires that all input trigger modules are active for AND module to send active event. - OR module requires that any of the input trigger modules is active for OR module to pass active event during state changes.

Field Type Description Required
timeoutMs integer (0 - 2147483647) How frequently triggering events should occur, i.e. max time difference between trigger events. Enabled when value greater than zero. Defaults to 0. Value in milliseconds.
{
  "type": "OR",
  "data": {}
}
{
  "type": "AND",
  "data": {
    "timeoutMs": 5000,
  }
}

6.18. MIN_READCOUNT (Pro License)

The MIN_READCOUNT module filters RFID tag data based on whether enough read events are seen within the time window.

Note: Specifying multiple readcounts/timewindows without antennaSeparation set to true will result in strange behavior (limits checked by last seen antenna).

Field Type Description Required
readCounts array of object readCount Array of entries defining read count and time limitations for antennas. Entry #0 defines ant 1, entry #1 defines Ant 2, … Undefined antennas will use limits defined in first entry. Note: Specifying multiple readcounts/timewindows without antennaSeparation set to true will result in strange behavior (limits checked by last seen antenna). X
antennaSeparation boolean Antenna separation for RFID tags. If this mode is used, then read are checked against based on antenna and tag records. If not used, then statistics are based only on tag level meaning all antennas accumulate shared statisctics. Default is False.
tidSeparation boolean TID separation mode will singulate tags based on TID instead of EPC. Tag read is dropped if TID is missing, so check that RFID reader(s) are configured to send TID information of tags. Default is False.

readCount | Field | Type | Description | Required | | :— | :— | :———————– | :—- | | timewindowMs | integer (1 - 2147483647) | Time window, or period, to include reads. Value in milliseconds. | X | | readCount | integer (1 - 2147483647) | Limit of how many reads are at least required within the time window to let tags pass filtering. | X |

6.19. TAGDATA (Pro License)

The TAGDATA module adds extra info fields to RFID tag data.

Field Type Description Required
assetExtraInfo1 string Data to be put into TAGDATA’s assetExtraInfo1 field. Max length is 20 characters. The empty string value is ignored.
assetExtraInfo2 string Data to be put into TAGDATA’s assetExtraInfo2 field. Max length is 20 characters. The empty string value is ignored.
assetExtraInfo3 string Data to be put into TAGDATA’s assetExtraInfo3 field. Max length is 20 characters. The empty string value is ignored.
eventExtraInfo1 string Data to be put into TAGDATA’s eventExtraInfo1 field. Max length is 20 characters. The empty string value is ignored.
eventExtraInfo2 string Data to be put into TAGDATA’s eventExtraInfo2 field. Max length is 20 characters. The empty string value is ignored.
eventExtraInfo3 string Data to be put into TAGDATA’s eventExtraInfo3 field. Max length is 20 characters. The empty string value is ignored.

7. Connections

Connections are also defined in MODULES.MODULES_JSON. See Section 6.

Field Type Description Required
type string Connection type. Either “TRIGGER” or “DATA”. X
source string ID of the source module X
target string ID of the target module X
fail boolean Defines if source is connected to data parsing success or fail output.

7.1. TRIGGER

{
  "type": "TRIGGER",
  "source": "module1",
  "target": "module2"
}

7.2. DATA

{
  "type": "DATA",
  "source": "module1",
  "target": "module2"
}

7.3 FAILED TRIGGER

{
  "type": "TRIGGER",
  "source": "module1",
  "target": "module2",
  "fail": true
}

7.4 FAILED DATA

{
  "type": "DATA",
  "source": "module1",
  "target": "module2",
  "fail": true
}

8. Module program examples:

8.1. Reader sending SGTIN-96 tags to HTTP REST server

{
  "modules": {
    "rdr": {
      "type": "RFID_READER",
      "data": {
        "reader": 0,
        "antennas": [
          1,
          2,
          3,
          4
        ]
      }
    },
    "epchex": {
      "type": "EPCHEX",
      "data": {
        "pattern": "^30",
        "epcLength": 96
      }
    },
    "duplicate": {
      "type": "DUPLICATE",
      "data": {
        "timeWindowMs": 60000
      }
    },
    "sender": {
      "type": "EVENTSENDER",
      "data": {
        "endpoint": "https://example.server.com/path/to/endpoint",
        "username": "username",
        "password": "password",
      }
    }
  },
  "connections": [
    {
      "type": "TRIGGER",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "DATA",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "TRIGGER",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "DATA",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "TRIGGER",
      "source": "duplicate",
      "target": "sender"
    },
    {
      "type": "DATA",
      "source": "duplicate",
      "target": "sender"
    }
  ]
}

8.2. Reader collecting reads of SSCC-96 tags for HTTP client

{
  "modules": {
    "rdr": {
      "type": "RFID_READER",
      "data": {
        "reader": 0,
        "antennas": [
          1,
          2,
          3,
          4
        ]
      }
    },
    "epchex": {
      "type": "EPCHEX",
      "data": {
        "pattern": "^31",
        "epcLength": 96
      }
    },
    "duplicate": {
      "type": "DUPLICATE",
      "data": {
        "timeWindowMs": 60000
      }
    },
    "store": {
      "type": "STORE",
      "data": {
        "timeWindowMs": 60000
      }
    }
  },
  "connections": [
    {
      "type": "TRIGGER",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "DATA",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "TRIGGER",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "DATA",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "TRIGGER",
      "source": "duplicate",
      "target": "store"
    },
    {
      "type": "DATA",
      "source": "duplicate",
      "target": "store"
    }
  ]
}

8.3. GPI triggered reading SGTIN-96 tags to HTTP REST server

{
  "modules": {
    "input": {
      "type": "INPUT",
      "data": {
        "device": 0,
        "port": 1
      }
    },
    "rdr": {
      "type": "RFID_READER",
      "data": {
        "reader": 0,
        "antennas": [
          1,
          2,
          3,
          4
        ]
      }
    },
    "epchex": {
      "type": "EPCHEX",
      "data": {
        "pattern": "^30",
        "epcLength": 96
      }
    },
    "duplicate": {
      "type": "DUPLICATE",
      "data": {
        "timeWindowMs": 60000
      }
    },
    "sender": {
      "type": "EVENTSENDER",
      "data": {
        "endpoint": "https://example.server.com/path/to/endpoint",
        "username": "username",
        "password": "password",
      }
    }
  },
  "connections": [
    {
      "type": "TRIGGER",
      "source": "input",
      "target": "rdr"
    },
    {
      "type": "DATA",
      "source": "input",
      "target": "rdr"
    },
    {
      "type": "TRIGGER",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "DATA",
      "source": "rdr",
      "target": "epchex"
    },
    {
      "type": "TRIGGER",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "DATA",
      "source": "epchex",
      "target": "duplicate"
    },
    {
      "type": "TRIGGER",
      "source": "duplicate",
      "target": "sender"
    },
    {
      "type": "DATA",
      "source": "duplicate",
      "target": "sender"
    }
  ]
}

9. IO Devices

App has generic support for extra GPIO devices. Actual usage depends on the custom logic but the devices itself are configured in common configuration.

[
  {
    "id": 0,
    "type": "LLRP",
    "initialState": "0110",
    "gpoInverted": [1,2,3,4],
    "gpiDebounce": {
      "1": 1000,
      "4": 2000
    }
  },
  {
    "id": 1,
    "type":"TBEN-S2-8DXP",
    "hostAddress": "10.7.0.120",
    "initialState":"001100111011",
    "dxpPinDirection": "00110011",
    "gpiDebounce": {
      "1": 1000,
      "4": 2000
    }
  }
]
Field Type Description Required Values
id integer Device identifier. Only RFID reader (internal) can have index 0 X
type string Device type X LLRP, DEBUS, TBEN-S2-8DXP, TBEN-L5-16DXP
hostAddress string IPv4 or host address for IO device Required by external IO devices
intialState string Initial state for GPOs. One character represents one entry. If pins are bidirectional, include GPIs also in string. 1 = ON, 0 = OFF, example 1100 means 1st & 2nd GPO are active and 3rd & 4th off.
dxpPinDirection string Pin direction setting for Turck devices (Q180/Q300 & TBENs). Required by Turck devices. 1 = OUTPUT, 0 = INPUT, example 1100 means 1st & 2nd port are outputs and 3rd & 4th inputs.
gpiDebounce object with key-value pairs Defines GPI debouncing used for ports. Port number must be string integer e.g., “1”. Debounce values are given in milliseconds.
gpoInverted array of integers Defines which GPOs are used as inverted values. For instance, some wirings require that activation is done with sink wiring. LLRP readers will most likely need outputs inverted.

Config parameter contains comma separated list of GPIO-devices. Each device entry contains IP=TYPE config.

Supported IO devices:

9.1. TBEN External IO modules

IO ports are same as in the device Modbus interface. M12 connector pin 4 is the first IO and pin 2 is the second. For example, reader port 0 is C0 pin 4, reader port 1 is C0 pin 2, reader pin 2 is C1 pin 4 and so on. More details can be seen in the device manual.

TBEN Vaux port control is supported only with TBEN-S2-8DXP device.

If you want to set initial state of outputs, you can use the following config:

IO_INITIAL_OUTPUT=0=000011

This means for device 0 the outputs 0-3 are turned off and 4-5 are turned on. This corresponds to Q180/Q300 having Vaux pins on and other DXP pins off. In this scenario sensors get their power from Vaux and sensor outputs can be seen in input pins.

If you connect traffic lights to reader then you probably want to turn everything off, e.g. IO_INITIAL_OUTPUT=0=000000

Vilant Node will use GPI signals only for triggering reading. If the expected logic requires signal inversion, Vilant Node can monitor inversed GPI signal values from all IO devices when a global config GPI_INVERTED is enabled.

9.2. Q180/300 IO

Q180 and Q300 have two DXP ports for IO. They are like DXP ports in other Turck devices.

NOTE: If PoE power input is used then output will work only from DXP aux pins. Other output pins will toggle just fine but no actual voltage is output. Inputs work just fine.

8.2.1. Pin mapping

Devices: - Turck Q180 - Turck Q300 - TBEN-S2-8DXP - TBEN-L5-16DXP

Reader port Connector M12 Pin Description
0 DXP 0/1 4 DXP 0
1 DXP 0/1 2 DXP 1
2 DXP 2/3 4 DXP 2
3 DXP 2/3 2 DXP 3
4 DXP 0/1 1 Vaux
5 DXP 2/3 1 Vaux

Physical connector DXP 0/1 is mapped to reader pin numbers 0, 1 and 4

Similarly, for DXP 2/3 is mapped to reader pins 2, 3 and 5. Turck documentation DXP Channels match port numbers. Vaux pins are added after DXP pins in reader port numbering.

Since DXP pins are both input and output the inputs will “see” output as well. Output turning on is masked but when output is turned off it will generate input low event on reader IO.

The reader will default to use port direction as input type. Reader will change port direction on DXP ports after any of these events: 1) DXP port output is activated with IO_INITIAL_OUTPUT configuration. 2) DXP port output is inverted with READER_GPO_INVERTED. (Only reader GPO are supported) 3) HTTP REST call on /gpo endpoint. See more information in REST documentation above.

DXP ports work simultaneously as input and output ports whereas Vaux ports are output-only.

App will also log “DXP chan %d Aux power overcurrent” diagnostic messages from Q180/Q300 ports. In case aux power is turned off this event is generated by hardware and can be ignored.

9.3. Impinj R700 IO

Impinj R700 has documented ports 0-based whereas we have decided to use 1-based indexing in Vilant applications.

Vilant Impinj
1 IN 0
2 IN 1
1 OUT 0
2 OUT 1
3 OUT 2

9.4. GPIO WebSocket Interface

Some GPIO events can be received and sent over a WebSocket. WebSocket path is /gpio (e.g. ws://reader:8080/gpio).

NOTE! DXP directions need to be configured in Node application in GPIO configuration. Trying to change output for the GPIO port defined as input port will fail due to invalid direction.

Example output messages:

{ "type":"output", "dev":0, "pin":4, "state":0, "timestamp":"2021-01-13T14:35:46.484Z" }
{ "type":"output", "dev":0, "pin":5, "state":1, "timestamp":"2021-01-13T14:36:51.170Z" }
{ "type":"input", "dev":0, "pin":2, "state":1, "timestamp":"2021-01-13T14:36:56.760Z" }
{ "type":"input", "dev":0, "pin":2, "state":0, "timestamp":"2021-01-13T14:36:57.815Z" }
{ "type":"input", "dev":0, "pin":2, "state":1, "timestamp":"2021-01-13T14:36:59.862Z" }
{ "type":"input", "dev":0, "pin":2, "state":0, "timestamp":"2021-01-13T14:37:00.227Z" }

Sending similarly formatted messages (without timestamp) will cause outputs to be set and fake input messages generated.

Dev refers to IO_DEVICE number. 0 means IO ports integrated to the device.

10. DEBUS passthru mode

This feature is supported only by Turck Q180 & Q300 readers. In this mode the application will forward communication from external client application to RF chip on the reader.

Configuration parameters: - GENERAL.DEBUS_PASSTHRU_ENABLED will disable or enable the feature. - GENERAL.DEBUS_PASSTHRU_PORT will set the port for accepting new connections for TCP communication between external client and Node application. - RFID.DEBUS_PASSHTRU.DEBUS_PASSTHRU_CONNECTION_ERROR_ALERT will disable or enable passthru connection alerts feature on Node. See more feature specific configurations in configuration page. To see parameters, passthru mode needs to be enabled.

10.1. DEBUS passthru mode GPIO

GPIO of the reader can be controlled with websocket interface defined in Section 9.4.

NOTE! DXP directions need to be configured in Node application in GPIO configuration. Trying to change output for the GPIO port defined as input port will fail due to invalid direction.