Ir al contenido

Cómo trabajar con LoRaWAN y un controlador PLC

OTAA o ABP, registro en The Things Network, mapa de pines LMIC, límites de tiempo en el aire y decodificación del payload, con código de ejemplo para la familia M-Duino LoRa
12 de febrero de 2024 por
Cómo trabajar con LoRaWAN y un controlador PLC
Boot & Work Corp. S.L, Queralt del Águila Munté

Para trabajar con LoRaWAN en un PLC necesitas cuatro cosas: una radio LoRa en el PLC, una pila LoRaWAN (en el programa del PLC o dentro del módulo de radio), un gateway con cobertura y un servidor de red, como The Things Network, que conoce las claves del dispositivo y envía los datos a tu aplicación. El PLC envía paquetes pequeños, el gateway los recibe y el servidor de red los comprueba, los descifra y los entrega.

Esta guía recorre el camino completo con un M-Duino de la familia LoRa (Arduino PLC) y la librería MCCI LoRaWAN LMIC: la elección entre OTAA y ABP, el registro del dispositivo en The Things Network, el mapa de pines, el ciclo de envío, los límites de tiempo en el aire y la decodificación del payload. Las secciones sobre activación, tiempo en el aire y payloads sirven para cualquier nodo LoRaWAN, también si no programas en Arduino.

¿Cómo envía datos un PLC por LoRaWAN?

LoRa es la modulación de radio. LoRaWAN es el protocolo de red que funciona sobre ella: define la capa MAC (Media Access Control), la activación de los dispositivos, la seguridad y el papel de los gateways y los servidores. Una red LoRaWAN es una estrella de estrellas: los dispositivos finales hablan con los gateways por radio, y los gateways hablan con un servidor de red por IP.

  1. 1 · PLCM-Duino LoRaLee sus entradas, prepara un payload pequeño y lo envía por radio. Aquí la librería LMIC ejecuta la pila LoRaWAN en el propio PLC.
  2. 2 · RadioGatewayRecibe todos los paquetes LoRa a su alcance en sus canales y los reenvía por Ethernet, Wi-Fi o 4G.
  3. 3 · RedServidor de redThe Things Network comprueba el código de integridad del mensaje y el contador de tramas, y elimina los duplicados de varios gateways.
  4. 4 · AplicaciónServidor de aplicaciónDescifra el payload y ejecuta tu payload formatter para convertir los bytes en valores.
  5. 5 · Tu sistemaSCADA, base de datosRecibe los datos decodificados mediante una integración, por ejemplo MQTT o un webhook.

Ejemplo Un camino posible. Los uplinks van de izquierda a derecha. Un downlink solo puede llegar a un dispositivo de Clase A en las dos ventanas cortas de recepción que siguen a cada uplink.

Cada sesión LoRaWAN 1.0.x usa dos claves AES-128. La clave de sesión de red (NwkSKey) protege la integridad del mensaje entre el dispositivo y el servidor de red. La clave de sesión de aplicación (AppSKey) cifra el payload desde el dispositivo hasta el servidor de aplicación, así que los gateways y el servidor de red solo ven un payload cifrado.

El alcance depende de la altura de la antena, el terreno, los obstáculos y el spreading factor. Va de cientos de metros dentro de edificios a varios kilómetros en campo abierto. Un spreading factor más alto llega más lejos, pero cada paquete pasa más tiempo en el aire.

OTAA o ABP: elige la activación antes de escribir el código

Un dispositivo LoRaWAN necesita claves de sesión antes de poder enviar. Las obtiene de una de estas dos formas. Over-The-Air Activation (OTAA) crea claves de sesión nuevas en un procedimiento de join. Activation By Personalization (ABP) guarda claves de sesión fijas en el dispositivo. Las claves que copias en el sketch, y el propio sketch (ttn-otaa o ttn-abp), dependen de esta elección.

OTAAABP
Valores guardados en el PLCDevEUI, JoinEUI (también llamado AppEUI) y la clave raíz AppKeyDevAddr, NwkSKey y AppSKey
Procedimiento de joinSí. El dispositivo envía un join request y la red responde con un join acceptNo. El dispositivo puede enviar enseguida
Claves de sesiónNuevas en cada join, derivadas de la AppKeyLas mismas durante toda la vida del dispositivo
Parámetros de redEl join accept puede llevar la lista de canales y los parámetros de recepciónLos canales y los parámetros de recepción se fijan en el código
Contadores de tramas tras reiniciar el PLCUn join nuevo abre una sesión nueva con contadores nuevosLos contadores vuelven a 0 si no los guardas, y la red descarta las tramas que ya ha visto
Adecuado paraDispositivos instalados y producciónPruebas cortas de laboratorio

Para un PLC en campo, usa OTAA. La AppKey nunca viaja por el aire, y el dispositivo puede volver a hacer join tras un corte de alimentación o un cambio de red. Si usas ABP, cada dispositivo necesita su propio DevAddr y sus propias claves, y debes guardar los contadores de tramas en memoria no volátil.

Hardware y software para el ejemplo con M-Duino LoRa

  • Un PLC de la familia M-Duino LoRa con una antena para tu banda. Nuestro ejemplo usa un M-Duino 21+ LoRa. La familia se basa en Arduino Mega 2560 (ATmega2560, 256 KB de flash, 8 KB de SRAM), consulta el datasheet de la familia M-Duino LoRa.
  • Arduino IDE con el paquete de placas de Industrial Shields. En Herramientas, elige la placa M-Duino LoRa family y tu modelo. Sigue la guía de primeros pasos si todavía no tienes las placas instaladas.
  • La librería MCCI LoRaWAN LMIC, versión de Industrial Shields, desde GitHub (versión 6.0.1-is1 en el momento de escribir este artículo). Es compatible con LoRaWAN 1.0.2 y 1.0.3. La región y la radio se configuran en project_config/lmic_project_config.h: por defecto es CFG_eu868. Para Norteamérica, cámbialo a CFG_us915.
  • Una cuenta en The Things Network y un gateway con cobertura, propio o de la comunidad.

Registra el PLC como dispositivo final en The Things Network

El servidor de red debe conocer tu dispositivo antes del primer join. En la consola de The Things Network:

  1. Crea una aplicación. El nombre es libre.
  2. Haz clic en "+ Register end device" y elige "Enter end device specifics manually".
  3. Frequency plan: "Europe 863-870 MHz (SF9 for RX2 - recommended)" para EU868. Si estás fuera de Europa, elige el plan de tu región.
  4. LoRaWAN version: "LoRaWAN Specification 1.0.2". Regional Parameters version: "RP001 Regional Parameters 1.0.2".
  5. JoinEUI (AppEUI): usa el valor del proveedor de tu módulo si lo tienes. Si no, se acepta un valor de todo ceros.
  6. Genera el DevEUI y la AppKey, y registra el dispositivo.

Después puedes indicar la ubicación del dispositivo y un payload formatter (ver más abajo). La consola muestra cada clave con un selector de orden de bytes (msb o lsb) y una vista de array en estilo C. Lo necesitarás en el paso siguiente.

Cómo trabajar con LoRaWAN y un controlador PLC
Vista general del dispositivo final en la consola de The Things Network: plan de frecuencias, versión de LoRaWAN, parámetros regionales e información de activación.

Mapa de pines LMIC en M-Duino LoRa y Ardbox LoRa

Abre el ejemplo desde Archivo, Ejemplos, MCCI LoRaWAN LMIC library, y elige ttn-otaa o ttn-abp. Los dos sketches tienen la misma estructura. Cambian las claves del principio y el setup.

How to work with LoRaWAN and a PLC controller
Arduino IDE con la placa M-Duino LoRa family (M-Duino 21+ LoRa) y los ejemplos ttn-otaa y ttn-abp de la librería MCCI LoRaWAN LMIC.

La librería se comunica con la radio LoRa por SPI. Tienes que indicarle qué pines de Arduino controlan el chip select (NSS) y el reset de la radio:

  • Familia M-Duino LoRa: chip select en el pin 12, reset en el pin 13.
  • Familia Ardbox LoRa: chip select en el pin 23, reset en el pin 3. No uses el pin 3 para nada más en tu programa, por ejemplo como entrada de interrupción.

La versión de Industrial Shields de la librería no necesita las líneas DIO de la radio, así que se quedan como LMIC_UNUSED_PIN. Sin LMIC_USE_INTERRUPTS, la librería lee los flags de interrupción de la radio por SPI cada vez que se ejecuta os_runloop_once(). Por eso tu loop() debe llamarla a menudo y no bloquearse nunca.

// Pin mapping for the M-Duino LoRa family
const lmic_pinmap lmic_pins = {
    .nss = 12,                    // radio chip select
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 13,                    // radio reset
    .dio = {LMIC_UNUSED_PIN, LMIC_UNUSED_PIN, LMIC_UNUSED_PIN},
};

En el M-Duino LoRa, el pin 13 es el reset de la radio. Algunos ejemplos genéricos de Arduino hacen parpadear el pin 13 como LED de estado. No copies ese código aquí, porque reinicia la radio.

Claves del dispositivo en el sketch: OTAA y ABP

Claves OTAA

Sustituye FILLMEIN por los valores de la consola. En la librería LMIC, el DevEUI y el JoinEUI van en orden little-endian (primero el byte menos significativo, "lsb" en la consola). La AppKey va en orden big-endian ("msb" en la consola).

static const u1_t PROGMEM APPEUI[8]  = { FILLMEIN };   // JoinEUI, lsb
static const u1_t PROGMEM DEVEUI[8]  = { FILLMEIN };   // DevEUI, lsb
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };   // AppKey, msb

Con OTAA, la primera llamada a do_send() inicia el join. Tras el join accept, The Things Network envía al dispositivo los canales EU868 adicionales, así que no hace falta fijarlos en el código.

Claves ABP

Para ABP, registra el dispositivo con activación ABP en la consola y copia el DevAddr, la NwkSKey y la AppSKey. Las dos claves van en orden big-endian. El DevAddr es un número de 32 bits, por ejemplo 0x260B1234.

static const PROGMEM u1_t NWKSKEY[16] = { FILLMEIN };  // msb
static const u1_t PROGMEM APPSKEY[16] = { FILLMEIN };  // msb
static const u4_t DEVADDR = FILLMEIN;                  // unique for every device

Un dispositivo ABP arranca solo con los tres canales EU868 por defecto. El ejemplo ttn-abp añade los demás canales de The Things Network después de LMIC_setSession(), fija la velocidad de RX2 en SF9 y el uplink en SF7 a 14 dBm:

#if defined(CFG_eu868)
LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
#else
# error Region not supported
#endif

LMIC_setLinkCheckMode(0);
LMIC.dn2Dr = DR_SF9;          // The Things Network uses SF9 for RX2
LMIC_setDrTxpow(DR_SF7, 14);  // uplink data rate and power

El ciclo de envío de LMIC: do_send, onEvent y os_runloop_once

El ejemplo envía el texto "Hello, world!" (13 bytes) cada TX_INTERVAL segundos. Tres funciones hacen funcionar el ciclo.

do_send() pone en cola un uplink en el puerto 1, salvo que haya una transmisión pendiente:

static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

onEvent() no sirve solo para depurar. Cuando llega el evento EV_TXCOMPLETE, programa el siguiente do_send(). Sin esta función, el PLC envía un paquete y se para. Esta es una versión reducida del gestor de eventos del ejemplo de la librería:

void onEvent(ev_t ev) {
    switch (ev) {
        case EV_JOINED:
            Serial.println(F("EV_JOINED"));
            LMIC_setLinkCheckMode(0);
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if (LMIC.dataLen) {
                Serial.print(F("Received "));
                Serial.print(LMIC.dataLen);
                Serial.println(F(" bytes of payload"));
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        default:
            break;
    }
}

os_runloop_once() ejecuta el planificador de LMIC. Llámala en cada loop() y lee las entradas del PLC con temporizadores basados en millis(), no con delay():

void setup() {
    Serial.begin(9600);
    os_init();          // LMIC init
    LMIC_reset();       // reset the MAC state
    do_send(&sendjob);  // first send (starts the join with OTAA)
}

void loop() {
    os_runloop_once();
}

Tiempo en el aire, duty cycle y política de uso justo de The Things Network

Cada paquete LoRa ocupa el canal durante un tiempo que crece con el tamaño del payload y el spreading factor. Ese tiempo en el aire tiene dos límites:

  • Duty cycle normativo. En la banda EU863-870, las sub-bandas que usan los canales LoRaWAN tienen un duty cycle del 1% (ETSI EN 300 220-2). Tras 1 s de transmisión, el dispositivo debe quedarse en silencio 99 s en esa sub-banda.
  • Política de uso justo de The Things Network. En la red comunitaria, cada nodo dispone de 30 s de tiempo en el aire de uplink cada 24 horas y de 10 mensajes de downlink como máximo cada 24 horas.

Cálculo Tiempo en el aire del mensaje del ejemplo ("Hello, world!", 13 bytes de payload más 13 bytes de cabeceras LoRaWAN), ancho de banda de 125 kHz, coding rate 4/5, 8 símbolos de preámbulo, cabecera explícita y CRC:

Spreading factorTiempo en el aire por uplinkTiempo en el aire al día, 1 uplink cada 60 sUplinks al día dentro de 30 s
SF761,7 ms88,8 s486 (uno cada 178 s)
SF9205,8 ms296,4 s145 (uno cada 596 s)
SF10411,6 ms592,8 s72 (uno cada 1200 s)
SF121646,6 ms2371,1 s18 (uno cada 4800 s)

Por tanto, el intervalo de 60 s del ejemplo sirve para una prueba en banco, pero en The Things Network es demasiado frecuente para un dispositivo que funciona cada día. Para un PLC en campo, envía menos y más pequeño: un payload binario de pocos bytes en lugar de texto, y un intervalo adecuado a tu spreading factor. LoRaWAN transporta datos de proceso y alarmas. No es un enlace para lazos de control rápidos.

Payload formatter para The Things Network

La red entrega bytes en bruto. Un payload formatter en la consola los convierte en campos. Para este ejemplo de texto, elige el formatter JavaScript personalizado para uplinks y pega este código. Convierte cada byte en un carácter:

function decodeUplink(input) {
  if (input.bytes && input.bytes.length > 0) {
    const decodedChars = [];

    for (let i = 0; i < input.bytes.length; i++) {
      const charValue = String.fromCharCode(input.bytes[i]);
      decodedChars.push(charValue);
    }

    return {
      data: {
        decodedChars: decodedChars.join('')
      },
      warnings: [],
      errors: []
    };
  } else {
    return {
      data: {},
      warnings: ["No bytes in the input"],
      errors: []
    };
  }
}

Para datos reales del PLC, empaqueta los valores en bits y bytes en el sketch y decodifícalos con desplazamientos y máscaras en el formatter. El ejemplo de empaquetado de bits en un payload LoRaWAN mete 11 entradas digitales, 2 flags de fallo y un valor analógico de 10 bits en 4 bytes.

Primer uplink: qué comprobar y errores habituales

Abre el Monitor Serie. Con OTAA deberías ver EV_JOINING, después EV_JOINED, y luego Packet queued y EV_TXCOMPLETE una vez por intervalo. En la consola, la pestaña Live data del dispositivo muestra el join accept y cada uplink. Con el formatter anterior, el campo decodificado muestra "Hello, world!".

SíntomaCausa habitualQué hacer
Join requests en la consola, pero sin EV_JOINEDOrden de bytes incorrecto en DevEUI o JoinEUI, AppKey incorrectaCopia DevEUI y JoinEUI como lsb y la AppKey como msb
Nada en la consolaNingún gateway con cobertura, o un plan de frecuencias que no coincide con lmic_project_config.hComprueba la cobertura del gateway y usa la misma región en la librería y en la consola
Join aceptado en la consola, pero el PLC no lo recibeLa ventana de recepción se abre demasiado tarde o demasiado prontoAñade LMIC_setClockError() después de LMIC_reset() para ampliar las ventanas de recepción. Consulta la sección Timing de la documentación de la librería
Dispositivo ABP: uplinks ignorados tras un reinicioEl contador de tramas ha vuelto a 0Guarda el contador o, solo para una prueba de laboratorio, activa "Resets frame counters" en los parámetros MAC del dispositivo
Menos uplinks de los esperadosLímites de duty cycle en la libreríaUsa un intervalo más largo o un spreading factor más bajo

LoRaWAN en ESP32 PLC y Raspberry PLC

La parte de red es la misma para cualquier PLC: la misma activación, las mismas claves, planes de frecuencias, límites de tiempo en el aire y payload formatters. Lo que cambia es el dispositivo. En la familia M-Duino LoRa, la librería LMIC ejecuta la pila LoRaWAN en el microcontrolador del PLC. En la familia ESP32 PLC, la placa de expansión LoRa usa un módulo Microchip RN2483 (UE y Asia) o RN2903 (EE. UU. y Australia), que lleva la pila LoRaWAN dentro y recibe comandos ASCII por UART, según el datasheet de la familia ESP32 PLC. Tu programa envía comandos como "join" y "send", y el módulo hace el resto.

Los modelos ESP32 PLC y Raspberry PLC ofrecen una placa de comunicación LoRa en versión EU868 o US902. Consulta la familia ESP32 PLC y la familia Raspberry PLC, y estas guías:

Mira la telemetría LoRaWAN a escala de red. El simulador de telemetría de sitios remotos reproduce la red de agua de un municipio con RTU ESP32 PLC sobre 4G y LoRaWAN. Corta un enlace y observa el store and forward, las alarmas y la detección de fugas por caudal nocturno.

Abre el simulador de telemetría

Enlaces LoRa en casos de estudio con PLC

Estos casos de estudio usan LoRa para traer datos de PLC desde lugares sin cable:

  • Planta de hormigón: un M-Duino dosifica cada material y envía los registros de cada amasada a un servidor por LoRa, para prever producción, facturación y averías.
  • Bomba de calor geotérmica: un M-Duino vigila presión y temperatura, y LoRa es uno de los enlaces con sitios remotos.
  • Paneles solares autónomos: un PLC por grupo de paneles informa a un PLC maestro por LoRa.

Preguntas frecuentes sobre LoRaWAN y PLC

¿Qué diferencia hay entre LoRa y LoRaWAN?

LoRa es la modulación de radio que transporta los bits. LoRaWAN es el protocolo de red sobre LoRa: activación de dispositivos, seguridad AES-128, contadores de tramas, gateways y servidores de red. Dos PLC pueden hablar punto a punto solo con LoRa. Para usar The Things Network u otra red LoRaWAN, el PLC necesita una pila LoRaWAN.

¿OTAA o ABP para un PLC industrial?

Usa OTAA. Crea claves de sesión nuevas en cada join, no envía la clave raíz por el aire y se recupera tras un reinicio. ABP sirve para pruebas cortas de laboratorio, pero necesita claves fijas y únicas por dispositivo y contadores de tramas guardados en memoria no volátil.

¿Por qué mi PLC no hace join en The Things Network?

Revisa primero el orden de bytes: en la librería LMIC, DevEUI y JoinEUI van en lsb y la AppKey en msb. Después comprueba que la región de lmic_project_config.h coincide con el plan de frecuencias de la consola y que hay un gateway con cobertura.

¿Cada cuánto puede enviar datos un PLC por LoRaWAN en The Things Network?

La política de uso justo da a cada nodo 30 s de tiempo en el aire de uplink al día. Un payload de 13 bytes en SF7 tarda 61,7 ms, así que caben unos 486 uplinks al día. En SF12 tarda 1646,6 ms, así que caben unos 18 al día.

¿Puede un PLC LoRaWAN recibir órdenes?

Sí, como downlinks. Un dispositivo de Clase A solo escucha en dos ventanas cortas de recepción después de cada uplink, así que una orden espera al siguiente uplink. The Things Network permite 10 downlinks por nodo y día. Usa los downlinks para consignas y configuración, no para control rápido.

¿Qué plan de frecuencias necesito?

El del país donde se instala el PLC: por ejemplo, EU868 en Europa y US915 en Norteamérica. El hardware de radio, la región de la librería y el plan de frecuencias de la consola deben coincidir. Las placas LoRa de Industrial Shields existen en versiones para distintas bandas, por ejemplo EU868 y US902.

Buscar en nuestro blog

Cómo trabajar con LoRaWAN y un controlador PLC
Boot & Work Corp. S.L, Queralt del Águila Munté 12 de febrero de 2024
Compartir esta publicación
Etiquetas

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs