Salta ai contenuti

LoRaWAN Cheat Sheet

Overview

LoRaWAN (Long Range Wide Area Network) is a media access control (MAC) protocol built on top of the LoRa physical layer modulation, designed for low-power wide-area networks (LPWAN). It enables battery-powered IoT devices to communicate over distances of 2-15 km in urban areas and up to 40+ km in rural line-of-sight conditions, while consuming minimal power (devices can run for years on a single battery). LoRaWAN operates in unlicensed ISM bands (868 MHz in Europe, 915 MHz in US, 923 MHz in Asia) and uses a star-of-stars topology where end devices communicate through gateways to a central network server.

The LoRaWAN architecture consists of four key components: end devices (sensors/actuators), gateways (LoRa to IP bridges), a network server (manages routing, deduplication, and MAC commands), and application servers (process device data). The protocol supports three device classes: Class A (lowest power, uplink-initiated), Class B (scheduled receive windows), and Class C (always listening, highest power). Security is built-in with AES-128 encryption using separate network and application session keys (OTAA or ABP activation). Open-source network servers like ChirpStack and The Things Network (TTN) make it accessible for community and enterprise deployments.

Installation

ChirpStack (Network Server)

# Docker-based deployment
git clone https://github.com/chirpstack/chirpstack-docker.git
cd chirpstack-docker

# Configure region
# Edit docker-compose.yml and configuration/chirpstack.toml

# Start all services
docker compose up -d

# Access web UI at http://localhost:8080
# Default: admin / admin

The Things Network (TTN)

# TTN is a public community network
# Register at https://www.thethingsnetwork.org/
# Create application and register devices via web console

# Install TTN CLI
brew install TheThingsNetwork/lorawan-stack/ttn-lw-cli
# or
snap install ttn-lw-stack

# Login
ttn-lw-cli login

LoRa Gateway Setup (RAK/SX1301)

# Install packet forwarder
git clone https://github.com/Lora-net/lora_gateway.git
cd lora_gateway
make

git clone https://github.com/Lora-net/packet_forwarder.git
cd packet_forwarder
make

# Configure gateway
# Edit global_conf.json with your frequency plan and server address

Arduino/PlatformIO (End Device)

// platformio.ini
// [env:lora_sensor]
// platform = espressif32
// board = heltec_wifi_lora_32_V3
// framework = arduino
// lib_deps =
//   mcci-catena/MCCI LoRaWAN LMIC library@^4.1.1

#include <lmic.h>
#include <hal/hal.h>

// OTAA keys (from TTN/ChirpStack console)
static const u1_t PROGMEM APPEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const u1_t PROGMEM DEVEUI[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
static const u1_t PROGMEM APPKEY[16] = { /* 16 bytes */ };

void os_getArtEui(u1_t* buf) { memcpy_P(buf, APPEUI, 8); }
void os_getDevEui(u1_t* buf) { memcpy_P(buf, DEVEUI, 8); }
void os_getDevKey(u1_t* buf) { memcpy_P(buf, APPKEY, 16); }

LoRaWAN Concepts

Device Classes

ClassDescriptionPowerLatency
AUplink-initiated, 2 short RX windowsLowestHigh
BScheduled receive windows (beacons)MediumMedium
CContinuous receive windowHighestLowest

Activation Methods

MethodDescription
OTAAOver-The-Air Activation (recommended)
Device negotiates session keys dynamically
Keys: AppEUI + DevEUI + AppKey
ABPActivation By Personalization
Pre-provisioned session keys (less secure)
Keys: DevAddr + NwkSKey + AppSKey

Frequency Plans

RegionFrequencyChannelsMax Payload
EU868868 MHz8+222 bytes
US915915 MHz72222 bytes
AU915915 MHz72222 bytes
AS923923 MHz8+222 bytes
CN470470 MHz96222 bytes
IN865865 MHz3+222 bytes
KR920920 MHz8+222 bytes

Spreading Factors

SFData RateRangeTime on AirMax Payload
SF75.47 kbpsShort36 ms222 bytes
SF83.12 kbpsMedium72 ms222 bytes
SF91.76 kbpsMedium144 ms115 bytes
SF10980 bpsLong288 ms51 bytes
SF11440 bpsLonger577 ms51 bytes
SF12250 bpsLongest1155 ms51 bytes

ChirpStack Configuration

Application Setup

# Create application via API
curl -X POST http://localhost:8080/api/applications \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "application": {
      "name": "my-sensors",
      "description": "Environmental sensors",
      "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242"
    }
  }'

Device Profile

{
  "deviceProfile": {
    "name": "class-a-otaa",
    "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242",
    "region": "EU868",
    "macVersion": "LORAWAN_1_0_3",
    "regParamsRevision": "A",
    "supportsOtaa": true,
    "supportsClassB": false,
    "supportsClassC": false,
    "adrAlgorithmId": "default"
  }
}

Register Device

# Register device via API
curl -X POST http://localhost:8080/api/devices \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "device": {
      "devEui": "0102030405060708",
      "name": "temp-sensor-01",
      "applicationId": "app-uuid",
      "deviceProfileId": "profile-uuid",
      "description": "Office temperature sensor"
    }
  }'

# Set OTAA keys
curl -X POST http://localhost:8080/api/devices/0102030405060708/keys \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY" \
  -d '{
    "deviceKeys": {
      "devEui": "0102030405060708",
      "nwkKey": "00112233445566778899aabbccddeeff"
    }
  }'

TTN CLI Commands

# Login
ttn-lw-cli login

# Create application
ttn-lw-cli applications create my-app --user-id my-user

# Register device (OTAA)
ttn-lw-cli end-devices create my-app my-device \
  --dev-eui 0102030405060708 \
  --app-eui 0000000000000000 \
  --app-key 00112233445566778899AABBCCDDEEFF \
  --lorawan-version 1.0.3 \
  --lorawan-phy-version 1.0.3-a \
  --frequency-plan-id EU_863_870

# List devices
ttn-lw-cli end-devices list my-app

# Get device info
ttn-lw-cli end-devices get my-app my-device

# Subscribe to uplinks
ttn-lw-cli events subscribe --application-id my-app

# Send downlink
ttn-lw-cli end-devices downlink push my-app my-device \
  --frm-payload "AQID" --f-port 1

Payload Encoding/Decoding

CayenneLPP Format

// Decoder function (TTN/ChirpStack)
function decodeUplink(input) {
  var data = {};
  var i = 0;
  while (i < input.bytes.length) {
    var channel = input.bytes[i++];
    var type = input.bytes[i++];
    switch (type) {
      case 0x67: // Temperature
        data['temperature_' + channel] =
          ((input.bytes[i++] << 8) | input.bytes[i++]) / 10;
        break;
      case 0x68: // Humidity
        data['humidity_' + channel] = input.bytes[i++] / 2;
        break;
      case 0x73: // Barometer
        data['pressure_' + channel] =
          ((input.bytes[i++] << 8) | input.bytes[i++]) / 10;
        break;
      case 0x88: // GPS
        data['latitude_' + channel] =
          ((input.bytes[i++] << 16) | (input.bytes[i++] << 8) | input.bytes[i++]) / 10000;
        data['longitude_' + channel] =
          ((input.bytes[i++] << 16) | (input.bytes[i++] << 8) | input.bytes[i++]) / 10000;
        data['altitude_' + channel] =
          ((input.bytes[i++] << 16) | (input.bytes[i++] << 8) | input.bytes[i++]) / 100;
        break;
    }
  }
  return { data: data };
}

Custom Binary Encoding

// Efficient binary encoder (device side - C)
// Pack temperature (int16, 0.01 resolution) + humidity (uint8, 0.5 resolution)
// bytes[0-1] = temperature * 100
// bytes[2] = humidity * 2

// Decoder (server side - JS)
function decodeUplink(input) {
  var temp = (input.bytes[0] << 8 | input.bytes[1]);
  if (temp > 32767) temp -= 65536;
  return {
    data: {
      temperature: temp / 100,
      humidity: input.bytes[2] / 2,
      battery: input.bytes[3]
    }
  };
}

Advanced Usage

Adaptive Data Rate (ADR)

ADR automatically optimizes:
- Spreading factor (lower = faster, less range)
- Transmit power (lower = less battery usage)
- Channel selection

ADR is recommended for stationary devices.
Disable for mobile devices:
  LMIC_setAdrMode(0);  // Disable ADR

Multicast

# Create multicast group (ChirpStack)
curl -X POST http://localhost:8080/api/multicast-groups \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY" \
  -d '{
    "multicastGroup": {
      "name": "firmware-update-group",
      "applicationId": "app-uuid",
      "region": "EU868",
      "mcAddr": "01020304",
      "mcNwkSKey": "00112233445566778899aabbccddeeff",
      "mcAppSKey": "ffeeddccbbaa99887766554433221100",
      "groupType": "CLASS_C"
    }
  }'

FUOTA (Firmware Update Over The Air)

# LoRaWAN FUOTA uses multicast + fragmented data transport
# Requires Class B or C capable devices
# ChirpStack supports FUOTA via the FUOTA server component

Troubleshooting

IssueSolution
Device won’t join (OTAA)Verify DevEUI/AppKey match, check frequency plan
No uplinks receivedCheck gateway connectivity, antenna connection
Downlinks not deliveredClass A: wait for next uplink; check RX windows
Poor rangeCheck antenna, raise gateway, reduce SF
High packet lossCheck duty cycle limits, reduce payload size
ADR not optimizingNeed 20+ uplinks for ADR to activate
Duplicate messagesNormal with multiple gateways; server deduplicates
Join accept timeoutCheck RX1/RX2 delay settings match server

Diagnostic Tools

# Monitor gateway traffic
# ChirpStack: Gateway > Frames tab shows all LoRa frames

# Check gateway status
curl http://localhost:8080/api/gateways/$GATEWAY_ID \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY"

# View device events
curl "http://localhost:8080/api/devices/$DEV_EUI/events?limit=20" \
  -H "Grpc-Metadata-Authorization: Bearer $API_KEY"

# TTN: check gateway status
ttn-lw-cli gateways get my-gateway --connection-stats

# Airtime calculator
# https://www.thethingsnetwork.org/airtime-calculator
# Input: SF, bandwidth, payload size, coding rate

Duty Cycle Limits

EU868 duty cycle restrictions:
- Band 1 (868.0-868.6 MHz): 1% duty cycle
- Band 2 (868.7-869.2 MHz): 0.1% duty cycle
- Band 3 (869.4-869.65 MHz): 10% duty cycle

US915: No duty cycle (FCC dwell time limits instead)
- Max 400ms dwell time per channel