ttn_fetch.py
En el lado servidor, este script Python consulta la API de The Things Network v3 para recuperar los últimos uplinks de una MKR WAN 1310: marca de tiempo, RSSI visto por la pasarela y carga base64. El ladrillo base de un panel o una alerta por correo.
Hardware
Una Arduino MKR WAN 1310 (microcontrolador SAMD21, módem LoRa pilotado por un ESP32 integrado que también expone Wi-Fi, antena LoRa y antena Wi-Fi). Una cuenta TTN, una aplicación con dispositivo unido, un token API (CLI ttn-lvc o clave de integración).
Cableado
Ninguno; la placa ni siquiera hace falta para probar: usa uplinks ya recibidos.
Funcionamiento
GET /as/applications/{app}/devices/{dev}/uplinks devuelve tramas descifradas por el servidor. El campo frm_payload va en base64: decodifícalo con base64.b64decode para recuperar tus bytes.
Ejecución
Instala Arduino SAMD Boards y la librería MKRWAN 2.x (MKRWAN1310), selecciona la placa y carga. La 1310 se controla con comandos AT a través de su ESP32 interno; para LoRa, prevé una segunda placa LoRa al otro lado.
Variantes
Pásate a MQTT (mqtt.ttn.cloud) para tiempo real sin sondeo o programa el script con cron para un archivo diario.
ttn_fetch.py
#!/usr/bin/env python3
"""Uplinks TTN v3 d'une application via l'API HTTP.
Usage : TTN_TOKEN=xxx python ttn_fetch.py [app] [device]
Le token doit etre une cle d'integration "Link API".
"""
import os
import sys
import requests
BASE = "https://eu1.cloud.thethings.network/api/v3"
APP = sys.argv[1] if len(sys.argv) > 1 else "mon-app"
DEV = sys.argv[2] if len(sys.argv) > 2 else "mon-device"
HEADERS = {"Authorization": f"Bearer {os.environ['TTN_TOKEN']}"}
def principal() -> None:
url = f"{BASE}/as/applications/{APP}/devices/{DEV}/uplinks"
r = requests.get(url, params={"limit": 10, "order": "DESC"},
headers=HEADERS, timeout=15)
r.raise_for_status()
for up in reversed(r.json().get("uplinks", [])):
t = up.get("received_at", "?")[:19]
meta = up.get("received_at_metadata", {})
rssi = meta.get("lorawan_metadata", {}).get(
"received_signal_strength", "?")
print(f"{t} rssi={rssi} b64={up.get('frm_payload', '')}")
if __name__ == "__main__":
principal()