lora_logger.py
Côté ordinateur, ce script pyserial consomme la passerelle LoRa, écrit chaque trame dans un CSV horodaté et affiche un histogramme de RSSI en caractères. On suit en direct la portée des stations et l'effet d'une antenne qu'on déplace, sans logiciel propriétaire.
Matériel requis
Une Arduino MKR WAN 1300 (microcontrôleur SAMD21, modem LoRa Semtech SX1272 point à point, pas de pile LoRaWAN embarquée, antenne 868/915 MHz). PC avec Python 3 + pyserial, la passerelle MKR WAN branchée en USB, stations émettant à proximité.
Câblage
Rien de plus que le câble USB de la passerelle ; le script saute l'en-tête CSV émis par le croquis.
Fonctionnement
La boucle for sur l'objet série itère ligne à ligne ; le découpage par point-virgule sépare métadonnées et charge utile. Le RSSI est mappé en barre # pour un suivi visuel immédiat.
Exécution
Installez Arduino SAMD Boards et la bibliothèque MKRWAN, sélectionnez la carte, téléversez. Il faut DEUX cartes LoRa (deux MKR WAN 1300, ou une 1300 et une 1310) pour voir les échanges ; réglez le même plan de fréquences dans le code.
Variantes
Tracez avec matplotlib pour les courbes de portée, ou faites tourner le script en systemd sur un Raspberry Pi pour un enregistrement permanent.
lora_logger.py
#!/usr/bin/env python3
"""Lit la passerelle LoRa (passerelle-usb.ino) et journalise en CSV.
Usage : python lora_logger.py [port]
"""
import csv
import sys
import time
import serial
PORT = sys.argv[1] if len(sys.argv) > 1 else "/dev/ttyACM0"
def principal() -> None:
with serial.Serial(PORT, 115200, timeout=1) as carte, \
open("lora.csv", "a", newline="") as fh:
ecrivain = csv.writer(fh)
for ligne in carte:
l = ligne.decode(errors="replace").strip()
if l.count(";") < 3 or l.startswith("millis"):
continue
t_carte, rssi, snr, *trame = l.split(";")
horloge = time.strftime("%H:%M:%S")
ecrivain.writerow([horloge, t_carte, rssi, snr,
";".join(trame)])
fh.flush()
barres = "#" * max(0, (int(float(rssi)) + 130) // 2)
print(f"{horloge} {rssi} dBm {barres} {';'.join(trame)}")
if __name__ == "__main__":
try:
principal()
except KeyboardInterrupt:
print("\nJournalise a l'arret.")