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

A LoRaWAN network consisting of a single gateway and multiple 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 off of 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:

If you choose the RAK7391:

  1. Complete the RAKPiOS quickstart to flash your RAK7391 with an operating system and connect it to the Internet.
  2. Install viam-server.

If you choose a peripheral:

  1. Follow our guide to set up an SBC.

  2. Enable SPI on your machine:

    sudo raspi-config nonint do_spi 0
    
  3. Follow 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-server handles 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:

  1. In the left-hand menu, click the + icon next to your machine part and select Component or service.
  2. Select the board type, then select the model that matches your machine. For instance, if you connected a peripheral gateway to a Raspberry Pi 5, choose raspberry-pi:rpi5. If you have a RAK7391, choose raspberry-pi:rpi to represent the internal Raspberry Pi Compute Module 4.
  3. Click Add module, and enter a name for your board.
  4. Click Create to add the module and board component to your machine.

Next, add a lorawan gateway component:

  1. Click the + icon again to add another component.
  2. Select the sensor type, then select the lorawan model that matches the name of your gateway. If your SX1302- or SX1303-based peripheral lacks a dedicated model, choose lorawan:sx1302-hat-generic.
  3. Click Add module, and enter a name for your gateway.
  4. Click Create.
  5. 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 HAT
  • viam:lorawan:sx1302-hat-generic: generic model for all other peripherals built using the SX1302 or SX1303 chips
  • viam:lorawan:rak7391: RAK7391 WisGate Connect

Configure attributes based on the tables 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 for sx1302-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:

  1. Navigate to the CONFIGURE tab of your machine’s page in the Viam app.
  2. Click the + icon next to your machine part in the left-hand menu and select Component or service.
  3. Select the sensor type, type lorawan, then select the lorawan model that matches the name of your node. If the name of your node does not appear in the list, choose the generic lorawan:node option.
  4. Click Add module, and enter a name for your node.
  5. Click Create to add the module to your machine.
  6. 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 sensor
  • viam:lorawan:milesight-ct101: Milesight CT101 current sensor
  • viam:lorawan:milesight-em310-tilt: Milesight EM310-TILT sensor
  • viam:lorawan:node: Any LoRaWAN sensor that is:
    • Class A
    • supports either the US915 or EU868 frequency band
    • uses LoRaWAN MAC specification version 1.0.3

Configure attributes based on the tables below:

You must configure the following attributes for OTAA nodes:

  • join_type: 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: Name 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: The name 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.

For a full list of attributes, see the module README.

Activation protocols

LoRaWAN networks can use any of the following protocols for communication:

NameValueKeySession KeyConfiguration
Over-The-Air ActivationOTAADynamic, generated at join timeAutomatically rotated
  • dev_eui
  • app_key
Activation By PersonalizationABPStaticStatic unless manually rotated
  • dev_addr
  • network_s_key
  • app_s_key

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

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: {} });

To restart 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({"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

Before using the Dragino WQS-LB, you must calibrate the sensor.

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_ph
  • calibrate_ec
  • calibrate_t
  • calibrate_orp

Use the calibration processes below to calibrate your sensor with these commands:

Calibrate the pH probe

The pH probe uses a thre-point calibration process:

  1. Wash the electrode with distilled water

  2. Place the electrode in a 9.18 standard buffer solution.

  3. 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
    }
    
  4. Wash the electrode with distilled water

  5. Place the electrode in a 6.86 standard buffer solution.

  6. 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
    }
    
  7. Wash the electrode with distilled water.

  8. Place the electrode in a 4.01 standard buffer solution.

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

    1. Wash the electrode with distilled water.

    2. Place the electrode in a 1413 μS/cm solution.

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

    1. Wash the electrode with distilled water.

    2. Place the electrode in a 12.88 mS/cm solution.

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

  1. Prepare a 0 NTU, 200 NTU, 400 NTU, 600 NTU, 800 NTU, or 1000 NTU solution.

  2. Place the probe in the solution.

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

  1. Wash the electrode with distilled water and place the probe in a 86mV standard buffer.

  2. 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
    }
    
  3. Wash the electrode with distilled water and place the probe in a 256mV standard buffer.

  4. 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_pin and power_en_pin in your gateway configuration match the manufacturer’s guidelines for your HAT.
    • Verify that SPI is enabled on your machine.
    • Check that spi_bus is configured for the correct SPI bus.

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_eui in your node configuration matches the value of your device.
    • If you use the OTAA activation protocol, confirm that app_key matches the application key defined in the device datasheet.
    • If you use the ABP activation protocol, verify that app_s_key and network_s_key match the values defined in the device datasheet or device packaging.
  • Check network configuration:

    • Ensure gateway is running with no error logs.
    • Verify that the gateway field 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, "gateway": [ "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.