Guide complet pour planifier, déployer et exploiter un réseau LoRaWAN privé ou hybride avec focus sur la couverture, la capacité et la gestion du spectre.
radio
lorawan
lpwan
L200
L300
1. Architecture LoRaWAN : Concepts Fondamentaux
1.1 Topologie Étoile de Étoiles
┌──────────────────────────────────────────────────────┐
│ End Devices │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ ED1 │ │ ED2 │ │ ED3 │ │ ED4 │ │ ED5 │ ... │ Class A/B/C
│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │
│ │ │ │ │ │ │
│ └────────┴────────┴────────┴────────┘ │
│ │ │
│ LoRa Radio │
│ (868/915 MHz) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Gateways (Concentrators) │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ GW1 │ │ GW2 │ │ GW3 │ │ GW4 │ ... │ Multi-channel RX
│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │ Packet forwarding
│ │ │ │ │ │
│ └────────┴────────┴────────┘ │
│ │ │
│ Backhaul Network │
│ (Ethernet, 4G, Fiber) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Network Server (LNS) │
│ │
│ • Packet routing & deduplication │
│ • MAC layer management │
│ • ADR (Adaptive Data Rate) │
│ • Device management │
│ • Downlink scheduling │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Application Server (AS) │
│ │
│ • Payload decoding │
│ • Business logic │
│ • Integration APIs │
│ • Data storage │
└──────────────────────────────────────────────────────┘
1.2 Classes de Devices : Trade-offs
| Classe | Mode RX | Latence Downlink | Consommation | Use Cases |
|---|---|---|---|---|
| Class A | RX après TX uniquement | Haute (jusqu’à prochain uplink) | Très faible (~10 µA sleep) | Capteurs autonomie max, télémétrie |
| Class B | RX périodiques (beacons) | Moyenne (slots prédictifs) | Moyenne (~100 µA avg) | Commandes programmées, smart meters |
| Class C | RX continu (sauf TX) | Très faible (< 1s) | Élevée (alimentation secteur) | Actionneurs temps réel, éclairage |
⚠️ Piège Fréquent : Class A Downlink
En Class A, le device n’ouvre ses fenêtres RX (RX1 à T+1s, RX2 à T+2s) qu’après un uplink. Pour envoyer une commande, il faut soit attendre le prochain uplink naturel, soit utiliser une « confirmed uplink » pour forcer une réponse.
2. Planification RF : Link Budget & Couverture
2.1 Calcul du Link Budget
# Link Budget LoRaWAN (SF12, BW125, EU868)
## Émetteur (End Device)
TX Power (ED): +14 dBm (typique pour batterie)
TX Antenna Gain: +2 dBi (omnidirectionnelle)
EIRP: 16 dBm
## Propagation
Path Loss (free space): -L dB = 32.45 + 20log₁₀(f_MHz) + 20log₁₀(d_km)
Exemple: d=5km, f=868MHz → L ≈ 114 dB
Margin (obstacles, fading): -10 dB (urbain dense)
-5 dB (suburbain)
-2 dB (rural)
## Récepteur (Gateway)
RX Antenna Gain: +8 dBi (directionnel ou colinéaire)
RX Sensitivity (SF12): -137 dBm (théorique SX1301/2/3)
Implementation Loss: -2 dB (câbles, connecteurs)
Effective Sensitivity: -135 dBm
## Bilan
Received Signal (5km urban): 16 - 114 - 10 + 8 = -100 dBm
Link Margin: -100 - (-135) = 35 dB ✓ EXCELLENT
# Portée estimée selon SF et environnement
SF7 (sensib -123 dBm): ~2 km urbain, ~5 km rural
SF12 (sensib -137 dBm): ~8 km urbain, ~20 km rural
2.2 Spreading Factors : Compromis Débit vs Portée
| SF | Bitrate (BW125) | ToA (51 bytes) | Sensibilité | Portée Relative |
|---|---|---|---|---|
| SF7 | 5470 bps | ~56 ms | -123 dBm | 1x (baseline) |
| SF8 | 3125 bps | ~103 ms | -126 dBm | 1.4x |
| SF9 | 1760 bps | ~205 ms | -129 dBm | 2x |
| SF10 | 980 bps | ~371 ms | -132 dBm | 2.8x |
| SF11 | 440 bps | ~741 ms | -135 dBm | 4x |
| SF12 | 250 bps | ~1482 ms | -137 dBm | 5.6x |
💡 ADR (Adaptive Data Rate)
Le Network Server ajuste automatiquement le SF et la TX power en fonction du SNR mesuré. Un device proche de la gateway utilisera SF7 pour libérer le spectre, tandis qu’un device distant passera en SF12 pour garantir la réception.
3. Dimensionnement Réseau : Capacité
3.1 Duty Cycle & Fair Access Policy
En Europe (bande ISM 868 MHz), la régulation ETSI EN300.220 impose des duty cycles :
| Sous-bande | Fréquences | Duty Cycle | Max TX Power |
|---|---|---|---|
| g (868.0-868.6) | 868.0 – 868.6 MHz | 1% (36s/h) | +14 dBm |
| g1 (868.7-869.2) | 868.7 – 869.2 MHz | 0.1% (3.6s/h) | +14 dBm |
| g3 (869.4-869.65) | 869.4 – 869.65 MHz | 10% (6min/h) | +27 dBm (gateways) |
3.2 Calcul de Capacité Théorique
# Calcul capacité LoRaWAN (simulation simplifiée)
def calculate_lorawan_capacity(
num_channels=8,
num_sf=6, # SF7 to SF12
packet_size_bytes=51,
uplink_interval_sec=600, # 10 minutes
duty_cycle=0.01 # 1%
):
"""
Calcule le nombre max de devices supportés par gateway
"""
# Time on Air pour chaque SF (BW125, 51 bytes payload)
toa_ms = {
7: 56, 8: 103, 9: 205,
10: 371, 11: 741, 12: 1482
}
# Distribution SF (ADR équilibré, estimation)
sf_distribution = {
7: 0.3, 8: 0.25, 9: 0.2,
10: 0.15, 11: 0.07, 12: 0.03
}
# Temps total disponible par heure (duty cycle 1%)
available_time_ms_per_hour = 3600 * 1000 * duty_cycle
# Pour chaque canal
available_per_channel = available_time_ms_per_hour / num_channels
# Calcul du nombre de packets possibles (moyenne pondérée SF)
avg_toa = sum(toa_ms[sf] * prob for sf, prob in sf_distribution.items())
packets_per_hour_per_channel = available_per_channel / avg_toa
# Total pour tous les canaux
total_packets_per_hour = packets_per_hour_per_channel * num_channels
# Devices supportés (basé sur intervalle uplink)
devices_supported = total_packets_per_hour * (uplink_interval_sec / 3600)
return {
"max_devices": int(devices_supported),
"packets_per_hour": int(total_packets_per_hour),
"avg_toa_ms": round(avg_toa, 1)
}
# Exemple: gateway 8 canaux, devices transmettant toutes les 10 min
result = calculate_lorawan_capacity()
print(f"Capacité max: {result['max_devices']} devices")
print(f"Throughput: {result['packets_per_hour']} packets/h")
# Output (approximatif):
# Capacité max: ~5000-8000 devices par gateway
# (en pratique: 2000-3000 pour tenir compte collisions, downlinks, marge)
⚠️ Collisions & Scalabilité
La capacité réelle est inférieure à la théorie en raison des collisions (même SF/canal/temps). L’effet « capture » de LoRa atténue partiellement ce problème, mais au-delà de 2000-3000 devices actifs par gateway, envisager le déploiement de gateways supplémentaires.
4. Déploiement de Gateways
4.1 Sélection Hardware
| Modèle | Concentrateur | Canaux RX | Backhaul | Prix Indicatif |
|---|---|---|---|---|
| RAK7248 | SX1302 | 8 | ETH, WiFi, LTE | ~300€ |
| Kerlink iBTS | SX1301 | 8 | ETH, 4G | ~800€ |
| Cisco IXM | SX1301 | 8 + GPS | ETH, PoE | ~1200€ |
| Multitech MTCDT | SX1301/1308 | 8 | ETH, WiFi | ~500€ |
4.2 Installation &
client.on_message = on_message
client.connect(« mqtt.example.com », 8883, 60)
5. Patterns de Résilience
5.1 Quality of Service (QoS) : Stratégies
💡 Choix du QoS selon le contexte
- QoS 0 (At most once) : Télémétrie haute fréquence, pertes acceptables (températures, RSSI)
- QoS 1 (At least once) : Commandes importantes, alertes (duplication acceptable)
- QoS 2 (Exactly once) : Transactions critiques, facturation (coût en latence/bande passante)
5.2 Last Will & Testament (LWT) : Détection de Panne
# Configuration LWT pour détection device offline
import paho.mqtt.client as mqtt
import json
import time
def create_lwt_message(device_id):
return json.dumps({
"device_id": device_id,
"status": "offline",
"timestamp": int(time.time()),
"reason": "unexpected_disconnect"
})
client = mqtt.Client(client_id="device-sensor-123")
# Configure LWT
lwt_topic = "devices/sensor-123/status"
lwt_payload = create_lwt_message("sensor-123")
client.will_set(lwt_topic, payload=lwt_payload, qos=1, retain=True)
# TLS setup
client.tls_set(ca_certs="/etc/pki/ca.crt",
certfile="/etc/pki/sensor-123.crt",
keyfile="/etc/pki/sensor-123.key")
# Connect with clean session = False for persistent session
client.connect("mqtt.example.com", 8883, keepalive=60)
# On successful connect, publish online status
def on_connect(client, userdata, flags, rc):
if rc == 0:
online_msg = json.dumps({
"device_id": "sensor-123",
"status": "online",
"timestamp": int(time.time()),
"firmware": "1.2.3"
})
client.publish(lwt_topic, payload=online_msg, qos=1, retain=True)
client.on_connect = on_connect
