Skip to main content
Version: 8.1

Usage

Ignition 8.1 build

This build exposes no OpenAPI resource CRUD routes — only the custom /data/lorawan/* REST routes documented below, alongside the system.lorawan.* scripting API. For the Ignition 8.3 build, see the current documentation.

Device Lifecycle

OTAA Join Process

  1. Registration: Register the device with its DevEUI and AppKey
  2. Device Power On: Device sends JoinRequest
  3. Server Validation: Module verifies the MIC and checks DevNonce (replay protection)
  4. Session Creation: Server derives session keys and assigns a DevAddr
  5. JoinAccept: Server sends the encrypted JoinAccept via a gateway
  6. Ready: Device is joined and can send data
system.lorawan.addDevice(
devEui="A358C4B192543F75",
appKey="925742558798BFB46E938D7A5A1B161F",
name="TempSensor_01",
deviceClass="A")

ABP Activation

  1. Registration: Register the device with DevEUI, DevAddr, NwkSKey, AppSKey
  2. Device Power On: Device immediately sends data frames
  3. Ready: No join procedure needed
system.lorawan.addDevice(
devEui="0102030405060708",
mode="ABP",
name="ABP_Sensor",
devAddr="26011234",
nwkSKey="2B7E151628AED2A6ABF7158809CF4F3C",
appSKey="3C4F9C09885817FBF6A2D2AE28161E7B")

When a device sends an uplink:

  1. Gateway receives the LoRa transmission
  2. Gateway forwards it to Ignition via UDP
  3. Module processes the frame (MIC verification, frame-counter validation, decryption, and payload decoding if a decoder is assigned)
  4. Tags are updated in real time under Uplink/ and, for decoded fields, at the device root
  5. Device state is persisted

Multi-Gateway Deduplication

When multiple gateways receive the same uplink (keyed by DevAddr:FCnt within a 5-second window):

  • The frame is processed once
  • The best gateway is selected for downlink routing by RSSI, then SNR as a tiebreaker
  • Duplicate receptions are otherwise dropped

Downlinks can be enqueued via tags or the REST API. The per-device queue holds up to 10 messages. Confirmed downlinks are retried (up to 8 attempts), and the module falls back to RX2 when RX1 cannot be used.

Trial Behavior

When the module trial has expired, the downlink endpoint returns HTTP 503 and no frames are transmitted. See Licensing.

Queue via Tags

Set the writable downlink tags on the device, then trigger the send:

  1. Write [LoRaWAN]/Devices/{Name}/Downlink/Payload (hex)
  2. Write [LoRaWAN]/Devices/{Name}/Downlink/Port
  3. Optionally write [LoRaWAN]/Devices/{Name}/Downlink/Confirmed
  4. Write true to [LoRaWAN]/Devices/{Name}/Downlink/Send (edge trigger)

The current queue is visible as a dataset at [LoRaWAN]/Devices/{Name}/Downlink/Queue.

system.tag.writeBlocking([
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Payload",
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Port",
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Send",
], ["48656C6C6F", 10, True])

Queue via REST API

Endpoint: POST /data/lorawan/devices/:devEui/downlink

curl -X POST "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/downlink" \
-H "Content-Type: application/json" \
-d '{
"fPort": 10,
"payload": "48656C6C6F",
"confirmed": false
}'
FieldTypeRequiredDescription
fPortintegerYesLoRaWAN port
payloadstringYesHex-encoded payload
confirmedbooleanNoRequest device acknowledgment

Returns HTTP 503 when the module trial has expired.

# List queued downlinks
curl "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/downlinks"

# Cancel a queued downlink (only if still QUEUED)
curl -X DELETE "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/downlinks/{id}"

Transmission Timing

Device ClassTiming
Class ARX1 window after uplink, with RX2 fallback
Class CScheduled by the Class C scheduler; a prior uplink is required so the module knows how to route the frame
Class C Requires a Prior Uplink

Class C downlinks are routed through the last gateway that heard the device, so the device must have sent at least one uplink before a downlink can be scheduled.

RX2 Fallback

When RX1 cannot be used, the module falls back to the region's RX2 default frequency and data rate. Confirmed downlinks are retried up to 8 times.


Device Status

Automatic Status Requests

The module issues a DevStatusReq periodically — by default every 100 uplinks. (This interval is a fixed internal default on the 8.1 build and is not exposed on the Settings page.)

On-Demand Status Request

Via REST API:

curl -X POST "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/status-request"

Via Tag: Write true to [LoRaWAN]/Devices/{Name}/Status/Request

Status Information

FieldDescription
BatteryRaw battery byte reported in DevStatusAns (per TS001-1.0.4 §5.3)
MarginLink margin in dB
TimestampWhen the status was received

Adaptive Data Rate (ADR)

The module implements ADR to optimize device transmission parameters by sending LinkADRReq MAC commands, adjusting the device's spreading factor and TX power based on observed link margin.


REST API Reference

Only custom routes under /data/lorawan/* are exposed on the 8.1 build (there are no OpenAPI resource CRUD routes — those are an 8.3-only feature). Device, gateway, and decoder management is done via the scripting API or CSV import/export.

MethodPathDescription
GET/data/lorawan/gateways/statusRuntime status of all gateways (connected, address, lastSeen)
POST/data/lorawan/gateways/:eui/blockBlock a gateway (ignore its traffic)
POST/data/lorawan/gateways/:eui/unblockUnblock a gateway
GET/data/lorawan/devices/statusRuntime status of all devices (joined, devAddr, fCntUp, fCntDown, lastSeen, rssi, snr, battery, margin)
POST/data/lorawan/devices/:devEui/status-requestQueue a DevStatusReq for the next downlink opportunity
POST/data/lorawan/devices/:devEui/downlinkQueue a downlink (503 if trial expired)
GET/data/lorawan/devices/:devEui/downlinksList queued downlinks for a device
DELETE/data/lorawan/devices/:devEui/downlinks/:idCancel a queued downlink (only if QUEUED)
GET/data/lorawan/editionReport the Ignition edition and any device-count cap
GET/data/lorawan/devices/exportDownload all devices as CSV
GET/data/lorawan/devices/templateDownload a device CSV template (example OTAA + ABP rows)
POST/data/lorawan/devices/importUpsert devices from a CSV body; returns a JSON import report
GET/data/lorawan/gateways/exportDownload all gateways as CSV
GET/data/lorawan/gateways/templateDownload a gateway CSV template
POST/data/lorawan/gateways/importUpsert gateways from a CSV body; returns a JSON import report

Status Examples

curl "http://localhost:8088/data/lorawan/devices/status"
curl "http://localhost:8088/data/lorawan/gateways/status"

Edition / Device Cap

curl "http://localhost:8088/data/lorawan/edition"

On Ignition Edge this reports {"edition": "EDGE", "deviceLimit": 50}; on other editions deviceLimit is null.


Scripting API

The system.lorawan.* namespace provides 13 functions (all accept keyword arguments). This is the primary way to manage resources on the 8.1 build. It works from Perspective gateway-scope scripts and the Designer script console. Reads, listings, and downlink queueing are intentionally not scripted — use the [LoRaWAN] tag provider for those.

FunctionReturnsDescription
system.lorawan.addDevice(devEui, appKey, name, mode, deviceClass, decoder, devAddr, nwkSKey, appSKey, fCntUp, fCntDown)DevEUIRegister a device (OTAA by default; mode='ABP' uses the session kwargs)
system.lorawan.updateDevice(devEui, name, appKey, deviceClass, decoder)Update a device
system.lorawan.removeDevice(devEui)Remove a device
system.lorawan.addGateway(eui, name, description, location)EUIRegister a gateway
system.lorawan.updateGateway(eui, name, description, location)Update a gateway
system.lorawan.removeGateway(eui)Remove a gateway
system.lorawan.addDecoder(name, file)decoder nameAdd a decoder from a JS file
system.lorawan.updateDecoder(name, file)Update a decoder from a JS file
system.lorawan.removeDecoder(name)Remove a decoder
system.lorawan.exportDevices(file)row countExport devices to CSV
system.lorawan.importDevices(file)JSON reportImport devices from CSV (upsert, best-effort)
system.lorawan.exportGateways(file)row countExport gateways to CSV
system.lorawan.importGateways(file)JSON reportImport gateways from CSV (upsert, best-effort)

CRUD functions raise IllegalArgumentException on bad input or an unknown id.

File Arguments

For decoder, import, and export functions, file is a path on the machine running the script — the gateway filesystem for a Perspective gateway-scope script, or the Designer machine for the script console (the contents are round-tripped to the gateway over RPC).

# Register an OTAA device with a decoder
system.lorawan.addDevice(
devEui="A358C4B192543F75",
appKey="925742558798BFB46E938D7A5A1B161F",
name="TempSensor_01",
mode="OTAA",
deviceClass="A",
decoder="EnvSensor")

# Promote a device to Class C
system.lorawan.updateDevice(devEui="A358C4B192543F75", deviceClass="C")

# Bulk export/import
count = system.lorawan.exportDevices(file="/tmp/devices.csv")
report = system.lorawan.importDevices(file="/tmp/devices.csv") # JSON summary string

Ignition Integration

Tag Bindings

Bind device data to components using the [LoRaWAN] tag provider:

[LoRaWAN]/Devices/TempSensor_01/Temperature
[LoRaWAN]/Devices/TempSensor_01/Uplink/RSSI
[LoRaWAN]/Devices/TempSensor_01/Status/Battery

Historian

Enable history on LoRaWAN tags to store time-series data:

  1. Right-click a device tag and select Edit Tag
  2. Enable History
  3. Configure the sample mode (typically "On Change")

Alarming

Create alarms on LoRaWAN device data, for example a high-temperature alarm on a decoded field or a low-battery alarm on Status/Battery.

Scripting with Tags

# Read a decoded value
temp = system.tag.readBlocking(["[LoRaWAN]/Devices/TempSensor_01/Temperature"])[0].value

# Request device status
system.tag.writeBlocking(["[LoRaWAN]/Devices/TempSensor_01/Status/Request"], [True])

# Enqueue a downlink via tags
system.tag.writeBlocking([
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Payload",
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Port",
"[LoRaWAN]/Devices/TempSensor_01/Downlink/Send",
], ["48656C6C6F", 10, True])

Troubleshooting

IssuePossible CauseSolution
Gateway not connectingUDP blockedCheck firewall for the configured port (default 1700)
Gateway not connectingWrong server addressVerify gateway configuration
Device not joiningWrong AppKeyVerify the 32-character hex key
Device not joiningDevNonce replayDevice may need a reset
Downlinks not receivedClass A timingDevice must uplink first
Class C downlink not sentNo prior uplinkClass C needs a prior uplink to establish routing
Registering a device fails on Edge50-device cap reachedIgnition Edge is capped at 50 devices
Class B is not supported errorClass B not implementedUse Class A or Class C
Tags report Bad_TrialExpiredTrial expiredApply a license to restart the network layer
Downlink returns 503Trial expiredApply a license

Best Practices

  1. Use OTAA - Provides better security than ABP through dynamic session keys
  2. Assign decoders - Convert raw payloads into meaningful tags
  3. Enable historian - Store time-series data for analysis
  4. Monitor battery - Set up alarms on device status
  5. Use Class A - Unless minimal-latency downlinks are required
  6. Check signal quality - Low RSSI/SNR indicates poor coverage
  7. Back up device keys - Store AppKeys securely outside Ignition; exported CSVs contain keys in plaintext
  8. Keep a license current - Trial expiry disables the network layer