Create a LoRaWAN network
LoRaWAN (Long Range Wide Area Network) enables sensor communication spanning kilometers with minimal power usage.
To collect LoRaWAN sensor data with Viam, use the lorawan module.
Hardware requirements
Architecture
You can use Viam to create LoRaWAN networks containing one gateway and multiple nodes. In a LoRaWAN network, information flows in two directions:
- uplinks transmit from nodes to gateways
- downlinks transmit from gateways to nodes

Nodes
Nodes pair LoRaWAN transmitters and receivers with a sensor. Instead of physically connecting to a machine over GPIO pins or USB, nodes communicate with your machine using the wireless LoRaWAN protocol. LoRaWAN supports communication over distances of up to 10 kilometers. Nodes typically run on battery power; a single coin cell battery can power a node for weeks or months.
Gateway
A gateway collects data generated by LoRaWAN nodes and acts as a network server. The gateway is the device that communicates with nodes over LoRaWAN and that connects to Viam. Viam can sync the data from the gateway and the LoRaWAN nodes to Viam, where you can aggregate and visualize your data.
Add a gateway
To start your network, you need a gateway.
The lorawan module supports the following varieties of gateway hardware:
- Peripherals built on the SX1302 or SX1303 chips connected to an SBC, such as the Waveshare SX1302 Gateway HAT
- Dedicated machines such as the Raspberry Pi CM4-based RAK7391 WisGate Connect
If you choose the RAK7391:
- Complete the RAKPiOS quickstart to flash your RAK7391 with an operating system and connect it to the Internet.
- Install
viam-server.
If you choose a peripheral:
Follow our guide to install
viam-serveron your SBC.Enable SPI on your machine:
sudo raspi-config nonint do_spi 0Follow the instructions provided by your peripheral manufacturer (for example, the Waveshare SX1302 LoRaWAN Gateway HAT instructions) to physically connect your peripheral to your SBC. For supported models, there is no need to configure drivers and software;
viam-serverhandles software configuration for you.
After setting up your gateway hardware, complete the following steps to configure your gateway:
Open your machine’s page in Viam and navigate to the CONFIGURE tab.
First, add a board component:
- In the left-hand menu, click the + icon next to your machine part and select Component or service.
- Select the
boardtype, then select the model that matches your machine. For instance, if you connected a peripheral gateway to a Raspberry Pi 5, chooseraspberry-pi:rpi5. If you have a RAK7391, chooseraspberry-pi:rpito represent the internal Raspberry Pi Compute Module 4. - Click Add module, and enter a name for your board.
- Click Create to add the module and board component to your machine.
Next, add a lorawan gateway component:
- Click the + icon again to add another component.
- Select the
sensortype, then select thelorawanmodel that matches the name of your gateway. If your SX1302- or SX1303-based peripheral lacks a dedicated model, chooselorawan:sx1302-hat-generic. - Click Add module, and enter a name for your gateway.
- Click Create.
- Click Save in the top right to apply your changes.
In the components section of your machine configuration, add the following objects:
{
"name": "<lorawan-gateway-board>",
"api": "rdk:component:board",
"model": "viam:raspberry-pi:<your-pi-model>",
"attributes": {}
},
{
"name": "<your-gateway-name>",
"api": "rdk:component:sensor",
"model": "viam:lorawan:<gateway-sensor-name>",
"attributes": {
"board": "<lorawan-gateway-board>",
"spi_bus": "<spi-bus-number>",
"region_code": "<region-code>"
}
}
Choose an appropriate board model from the board components registry.
Choose an appropriate gateway model from the following options:
viam:lorawan:sx1302-waveshare-hat: Waveshare LoRaWAN SX1302 Gateway HATviam:lorawan:sx1302-hat-generic: generic model for all other peripherals built using the SX1302 or SX1303 chipsviam:lorawan:rak7391: RAK7391 WisGate Connect
Configure attributes based on the descriptions below:
You must configure the following attributes for SX1302- and SX1303-based LoRaWAN gateways:
board: The name of the board component that the peripheral is connected to. Used for GPIO pin control.reset_pin: GPIO pin used for peripheral reset. Not configurable forsx1302-waveshare-hat.
For generic peripherals, you must also configure spi_bus, power_en_pin, and path. For a full list of attributes, see the module README.
You must configure the following attributes for RAK7391 gateways:
board: The name of the board component that represents the Raspberry Pi Compute Module inside the RAK7391. Used for GPIO pin control.
Add a node
Complete the following steps to configure your node:
- Navigate to the CONFIGURE tab of your machine’s page in the Viam app.
- Click the + icon next to your machine part in the left-hand menu and select Component or service.
- Select the
sensortype, then typelorawanand select thelorawanmodel that matches the name of your node. If the name of your node does not appear in the list, choose the genericlorawan:nodeoption. - Click Add module, and enter a name for your node.
- Click Create to add the model and module to your machine.
- Configure the model attributes as described below, according to the node type.
- Click Save in the top right to apply your changes.
In the components section of your machine configuration, add the following object, depending on your preferred activation protocol:
{
"name": "<your-node-name>",
"api": "rdk:component:sensor",
"model": "viam:lorawan:<node-name>",
"attributes": {
"dev_eui": <device-eui>,
"app_key": <application-key>,
"gateways": [<gateway-name>]
}
}
{
"name": "<your-node-name>",
"api": "rdk:component:sensor",
"model": "viam:lorawan:<node-name>",
"attributes": {
"join_type": "ABP",
"dev_addr": <device-address>,
"app_s_key": <application-session-key>,
"network_s_key": <network-session-key>,
"gateways": [<gateway-name>]
}
}
Choose an appropriate node model from the following options:
viam:lorawan:dragino-LHT65N: Dragino LHT65N temperature and humidity sensor.viam:lorawan:dragino-WQSLB: Dragino WQS-LB water quality sensorviam:lorawan:milesight-ct101: Milesight CT101 current sensorviam:lorawan:milesight-em310-tilt: Milesight EM310-TILT sensorviam:lorawan:node: Any LoRaWAN sensor that is:- Class A
- supports either the
US915orEU868frequency bands - uses LoRaWAN MAC specification version 1.0.3
Configure attributes based on the descriptions below:
Configure the following attributes for OTAA nodes:
join_type: (Optional) The activation protocol used to secure this network. Default: “OTAA”. Options: “OTAA”, “ABP”.dev_eui: The device EUI (Extended Unique Identifier), a unique 64-bit identifier for the LoRaWAN device in hexadecimal format (16 characters). Found on your device or in device packaging.app_key: The 128-bit hexadecimal AES application key used for device authentication and session key derivation. Found in the device datasheet.gateways: An array containing the names of the gateway component in your Viam configuration.
You must configure the following attributes for ABP nodes:
dev_addr: The 32-bit hexadecimal device address used to identify this device in uplink messages. Found in the device datasheet or in device packaging.app_s_key: The 128-bit hexadecimal application session key used to decrypt uplink messages. Found in the device datasheet or in device packaging.network_s_key: The 128-bit hexadecimal network session key used to decrypt uplink messages. Found in the device datasheet or in device packaging.gateways: An array containing the names of the gateway component in your Viam configuration.
For the generic viam:lorawan:node model, you must also configure decoder_path for the decoder script.
Tip
Device-specific models for Milesight nodes provide default values for app_key (for OTAA), network_s_key and app_s_key (for ABP), fport, decoder_path, and uplink_interval_mins.
If you use a Milesight node, omit these fields from your configuration.
For a full list of attributes, see the module README.
Activation protocols
LoRaWAN networks can use any of the following protocols for communication:
| Name | Value | Key | Session Key | Configuration |
|---|---|---|---|---|
| Over-The-Air Activation | OTAA | Dynamic, generated at join time | Automatically rotated |
|
| Activation By Personalization | ABP | Static | Static unless manually rotated |
|
To specify an activation protocol for your network, use the join_type field.
Decoder script
When a LoRaWAN device transmits data, it sends compressed binary payloads to minimize power consumption and airtime. Decoder scripts convert this binary data into structured, human-readable formats like JSON.
Each manufacturer uses different encoding schemes depending on the data transmitted by a device. For example:
- A temperature sensor might encode 23.5°C as a hexadecimal value like
0x00EB. - A GPS tracker might pack latitude and longitude into 8 bytes.
Without a decoder script, your application receives meaningless byte sequences like 0x00EB1337 instead of useful data like {"temperature": 23.5, "humidity": 42.0 }.
Device manufacturers typically provide scripts for each device. For examples, see the following GitHub repositories:
Frame port
Info
Only configure an fport value for your node if the documentation for your device instructs you to do so.
Frame port (fport) is an 8-bit field in the LoRaWAN MAC payload structure that defines the data type contained within the frame payload.
Frame ports identify the kind of data passed in an uplink or downlink message so it can be routed to the correct handler, which helps keep LoRaWAN traffic fast, simple, and efficient.
Use the fport field of a node configuration to specify the frame port value that the node expects to use for downlink communication.
When you specify an fport value for a node, gateways use that value as the frame port for all downlink communication with the node.
Control nodes
You can use DoCommand to configure, control, and calibrate your LoRaWAN nodes.
The lorawan module supports the following commands:
Restart node
To restart a node from the Viam web UI, send the following DoCommand input in the CONTROL tab of your machine page:
{
"restart_sensor": ""
}
The following example shows how to restart a node from an SDK:
node = await robot.get_component(Sensor.get_resource_name("<your_node_name>"))
# restart node
await node.do_command({"restart_sensor": {}})
final node = Sensor.fromRobot(robot, '<your_node_name>');
// restart node
await node.doCommand({ 'restart_sensor': {} });
const node = new SensorClient(client, "<your_node_name>");
// restart node
await node.doCommand({ restart_sensor: {} });
Send a downlink message to a node
To send a downlink message to a node from the Viam web UI, specify a hexadecimal string in the following DoCommand input in the CONTROL tab of your machine page:
{
"send_downlink": "48656C6C6F"
}
The following example shows how to send downlink messages to a node from an SDK:
node = await robot.get_component(Sensor.get_resource_name("<your_node_name>"))
# send downlink message in hexadecimal
await node.do_command({"send_downlink": "48656C6C6F"})
final node = Sensor.fromRobot(robot, '<your_node_name>');
// send downlink message in hexadecimal
await node.doCommand({ 'send_downlink': '48656C6C6F' });
const node = new SensorClient(client, "<your_node_name>");
// send downlink message in hexadecimal
await node.doCommand({ send_downlink: "48656C6C6F" });
Configure the transmission interval
The transmission interval controls how often a node communicates with the gateway.
To change the transmission interval of a node from the Viam web UI, send the following DoCommand input in the CONTROL tab of your machine page:
{
"set_interval": 300.0
}
The following example shows how to change the transmission interval of a node from an SDK:
node = await robot.get_component(Sensor.get_resource_name("<your_node_name>"))
# set data transmission interval in seconds
await node.do_command({"set_interval": 300.0})
final node = Sensor.fromRobot(robot, '<your_node_name>');
// set data transmission interval in seconds
await node.doCommand({ 'set_interval': 300.0 });
const node = new SensorClient(client, "<your_node_name>");
// set data transmission interval in seconds
await node.doCommand({ set_interval: 300.0 });
Calibrate the Dragino WQS-LB water quality sensor
To use a Dragino WQS-LB, you must calibrate the sensor. For other sensors, see the manufacturer’s instructions.
Info
For more information about calibration, consult the user manual.
To send calibration commands from the Viam web app, send the following DoCommand input in the CONTROL tab of your machine page:
{
"<command>": value
}
The following example shows how to send calibration commands to the Dragino WQS-LB sensor:
node = await robot.get_component(Sensor.get_resource_name("<your_node_name>"))
await node.do_command({"<command>": "<value>"})
final node = Sensor.fromRobot(robot, '<your_node_name>');
await node.doCommand({'<command>': '<value>' });
const node = new SensorClient(client, "<your_node_name>");
await node.doCommand({ <command>: <value> });
The Dragino WQS-LB supports the following calibration commands:
calibrate_phcalibrate_eccalibrate_tcalibrate_orp
Use the calibration processes below to calibrate your sensor with these commands:
Calibrate the pH probe
The pH probe uses a three-point calibration process:
Wash the electrode with distilled water
Place the electrode in a 9.18 standard buffer solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink to the node:
{ "calibrate_ph": 9 }Wash the electrode with distilled water
Place the electrode in a 6.86 standard buffer solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink to the node:
{ "calibrate_ph": 6 }Wash the electrode with distilled water.
Place the electrode in a 4.01 standard buffer solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink to the node:
{ "calibrate_ph": 4 }
Calibrate the electrical conductivity probe
The EC probe uses a one-point calibration process. You can configure the EC probe in the following modes:
For K=1, to measure conductivity from 0-2000 μS/cm at a resolution of 1 μS/cm:
Wash the electrode with distilled water.
Place the electrode in a 1413 μS/cm solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink:
{ "calibrate_ec": 1 }
For K=10, to measure conductivity from 10-20000 μS/cm at a resolution of 10 μS/cm:
Wash the electrode with distilled water.
Place the electrode in a 12.88 mS/cm solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink:
{ "calibrate_ec": 10 }
Calibrate the turbidity probe
The turbidity probe uses a one-point calibration process:
Prepare a 0 NTU, 200 NTU, 400 NTU, 600 NTU, 800 NTU, or 1000 NTU solution.
Place the probe in the solution.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send a downlink containing the NTU value to your node:
{ "calibrate_t": <NTU value> }
Calibrate the ORP probe
The Oxidation-Reduction Potential (ORP) probe uses a two-point calibration process:
Wash the electrode with distilled water and place the probe in a 86mV standard buffer.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink to the node:
{ "calibrate_orp": 86 }Wash the electrode with distilled water and place the probe in a 256mV standard buffer.
Wait at least one transmission interval. View the sensor output in the TEST panel. Once the data stabilizes, send the following downlink to the node:
{ "calibrate_orp": 256 }
View captured data
You can query captured data in the Viam app from the DATA page. To build a dashboard to monitor your captured data using charts and graphs, configure widgets on the TELEOP page.
For more information, see Visualize data.
Troubleshooting
Gateway fails to start
Check hardware connections:
- If using a HAT, ensure your HAT is properly seated on the SBC’s GPIO header.
- Check that the antenna is securely connected to your gateway.
- On the Waveshare SX1302 LoRaWAN Gateway HAT, check the status LEDs for activity:
- Power LED (red, solid): gateway receiving power
- TX LED (red, blinking): gateway transmitting data
- RX LED (red, blinking): gateway receiving data
Verify pin configuration:
- If using a HAT, confirm that
reset_pinandpower_en_pinin your gateway configuration match the manufacturer’s guidelines for your HAT. - Verify that SPI is enabled on your machine.
- Check that
spi_busis configured for the correct SPI bus.
- If using a HAT, confirm that
Sensor fails to join network
Wait 10-15 minutes:
- Nodes can take up to 10-15 minutes to join the network.
- If a node doesn’t join the network within 30 minutes, try restarting the node.
Verify device identifiers:
- Check that
dev_euiin your node configuration matches the value of your device. - If you use the OTAA activation protocol, confirm that
app_keymatches the application key defined in the device datasheet. - If you use the ABP activation protocol, verify that
app_s_keyandnetwork_s_keymatch the values defined in the device datasheet or device packaging.
- Check that
Check network configuration:
- Ensure gateway is running with no error logs.
- Verify that the
gatewaysfield of the node contains an array that contains only a string that exactly matches the name of your gateway in your machine configuration (for example,"gateways": [ "example-gateway" ]). - Verify that your gateway and node frequency regions match.
Adjust positioning:
- Move node closer to gateway for initial setup.
- Check for physical obstructions.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!