Usage
Device Lifecycle
OTAA Join Process
- Registration: Register device with DevEUI and AppKey
- Device Power On: Device sends JoinRequest
- Server Validation: Module verifies the MIC and checks DevNonce (replay protection)
- Session Creation: Server derives session keys and assigns a DevAddr
- JoinAccept: Server sends the encrypted JoinAccept via a gateway
- Ready: Device is joined and can send data
ABP Activation
- Registration: Register device with DevEUI, DevAddr, NwkSKey, AppSKey
- Device Power On: Device immediately sends data frames
- Ready: No join procedure needed
Uplink Data
When a device sends an uplink:
- Gateway receives the LoRa transmission
- Gateway forwards it to Ignition via UDP
- Module processes the frame (MIC verification, frame-counter validation, decryption, and payload decoding if a decoder is assigned)
- Tags are updated in real time under
Uplink/and, for decoded fields, at the device root - 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 (with a 3 dB advantage threshold), then SNR as a tiebreaker
- Duplicate receptions are otherwise dropped
Downlink Messages
Downlinks can be enqueued three ways: via the REST API, via tags, or internally by the module. The per-device queue holds up to 10 messages. Confirmed downlinks are retried, and the module falls back to RX2 when RX1 cannot be used.
Queue via Tags
Set the writable downlink tags on the device, then trigger the send:
- Write
[LoRaWAN]/Devices/{Name}/Downlink/Payload(hex) - Write
[LoRaWAN]/Devices/{Name}/Downlink/Port - Optionally write
[LoRaWAN]/Devices/{Name}/Downlink/Confirmed - Write
trueto[LoRaWAN]/Devices/{Name}/Downlink/Send(edge trigger)
The current queue is visible as a dataset at [LoRaWAN]/Devices/{Name}/Downlink/Queue.
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
}'
| Field | Type | Required | Description |
|---|---|---|---|
fPort | integer | Yes | LoRaWAN port |
payload | string | Yes | Hex-encoded payload |
confirmed | boolean | No | Request device acknowledgment |
Returns HTTP 503 when the module trial has expired.
View Queued Downlinks
curl "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/downlinks"
Cancel a Downlink
curl -X DELETE "http://localhost:8088/data/lorawan/devices/A358C4B192543F75/downlinks/{id}"
Transmission Timing
| Device Class | Timing |
|---|---|
| Class A | RX1 window after uplink, with RX2 fallback |
| Class C | Scheduled by the Class C scheduler; a prior uplink is required so the module knows how to route the frame |
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 before the entry is marked failed and leaves the queue.
Device Status
Automatic Status Requests
The module issues a DevStatusReq periodically. The interval is configurable and defaults to every 100 uplinks.
On-Demand Status Request
Via 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
| Field | Description |
|---|---|
| Battery | Battery level reported in DevStatusAns |
| Margin | SNR margin in dB |
| Timestamp | When 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
Two route families are exposed:
- OpenAPI resource routes at
/data/api/v1/resources/{moduleId}/...for managing device, gateway, and decoder resources. POST/PUT bodies are arrays; updates and deletes require a signature. - Custom routes at
/data/lorawan/...for runtime status, downlinks, status requests, and CSV bulk operations.
Custom Routes
| Method | Path | Description |
|---|---|---|
| GET | /data/lorawan/gateways/status | Status of all gateways |
| GET | /data/lorawan/devices/status | Status of all devices |
| POST | /data/lorawan/devices/:devEui/status-request | Trigger DevStatusReq |
| POST | /data/lorawan/devices/:devEui/downlink | Enqueue a downlink (503 if trial expired) |
| GET | /data/lorawan/devices/:devEui/downlinks | List queued downlinks |
| DELETE | /data/lorawan/devices/:devEui/downlinks/:id | Cancel a queued downlink |
| POST | /data/lorawan/gateways/:eui/block | Block a gateway (stop accepting its traffic) |
| POST | /data/lorawan/gateways/:eui/unblock | Unblock a previously blocked gateway |
| GET | /data/lorawan/edition | Report the running edition (used to apply the Edge device cap) |
| GET | /data/lorawan/devices/export | Download all devices as CSV |
| POST | /data/lorawan/devices/import | Upsert devices from a CSV body |
| GET | /data/lorawan/devices/template | Download an empty device CSV template |
| GET | /data/lorawan/gateways/export | Download all gateways as CSV |
| POST | /data/lorawan/gateways/import | Upsert gateways from a CSV body |
| GET | /data/lorawan/gateways/template | Download an empty gateway CSV template |
Device Status Example
curl "http://localhost:8088/data/lorawan/devices/status"
Gateway Status Example
curl "http://localhost:8088/data/lorawan/gateways/status"
Device Registration Example
# Register an OTAA device
curl -X POST "http://localhost:8088/data/api/v1/resources/com.operametrix.ignition.lorawan/device" \
-H "Content-Type: application/json" \
-d '[{
"name": "TempSensor_01",
"config": {
"name": "TempSensor_01",
"devEui": "A358C4B192543F75",
"appKey": "925742558798bfb46e938d7a5a1b161f",
"mode": "OTAA",
"deviceClass": "A"
}
}]'
Scripting API
The system.lorawan.* namespace provides 13 functions (all accept keyword arguments). Writes and mutations are scripted; reads, listings, and downlink queueing are intentionally not scripted — use the [LoRaWAN] tag provider for those.
| Function | Returns | Description |
|---|---|---|
system.lorawan.addDevice(devEui, appKey, name, mode, deviceClass, decoder, devAddr, nwkSKey, appSKey, fCntUp, fCntDown) | DevEUI | Register a device |
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) | EUI | Register a gateway |
system.lorawan.updateGateway(eui, name, description, location) | — | Update a gateway |
system.lorawan.removeGateway(eui) | — | Remove a gateway |
system.lorawan.addDecoder(name, file) | — | Add 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 count | Export devices to CSV |
system.lorawan.exportGateways(file) | row count | Export gateways to CSV |
system.lorawan.importDevices(file) | JSON report | Import devices from CSV |
system.lorawan.importGateways(file) | JSON report | Import gateways from CSV |
For decoder, import, and export functions, file is a path on the machine running the script.
# Register an OTAA device
system.lorawan.addDevice(
devEui="A358C4B192543F75",
appKey="925742558798bfb46e938d7a5a1b161f",
name="TempSensor_01",
mode="OTAA",
deviceClass="A",
decoder="EnvSensor")
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:
- Right-click a device tag and select Edit Tag
- Enable History
- Configure the sample mode (typically "On Change")
Alarming
Create alarms on LoRaWAN device data:
[LoRaWAN]/Devices/TempSensor_01/Temperature > 80
[LoRaWAN]/Devices/TempSensor_01/Status/Battery < 50
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
| Issue | Possible Cause | Solution |
|---|---|---|
| Gateway not connecting | UDP blocked | Check firewall for the configured port (default 1700) |
| Gateway not connecting | Wrong server address | Verify gateway configuration |
| Uplinks ignored | Gateway not registered | Register the gateway; only registered gateways are accepted |
| Device not joining | Wrong AppKey | Verify the 32-character hex key |
| Device not joining | DevNonce replay | Device may need a reset |
| Downlinks not received | Class A timing | Device must uplink first |
| Class C downlink not sent | No prior uplink | Class C needs a prior uplink to establish routing |
Tags report Bad_TrialExpired | Trial expired | Apply a license to restart the network layer |
Downlink returns 503 | Trial expired | Apply a license |
| "Class B is not supported" on CSV import or a scripting call | Class B is not implemented | Set the device to Class A or Class C (the web UI only offers these two) |
Best Practices
- Register gateways first - Only registered gateways are accepted; unregistered gateways are rejected
- Use OTAA - Provides better security than ABP
- Assign decoders - Convert raw payloads into meaningful tags
- Enable historian - Store time-series data for analysis
- Monitor battery - Set up alarms on device status
- Use Class A - Unless minimal-latency downlinks are required
- Check signal quality - Low RSSI/SNR indicates poor coverage
- Back up device keys - Store AppKeys securely outside Ignition
- Keep a license current - Trial expiry disables the network layer