LoRaWan – Interruptor temporizado. Gateway y Broker

Gateway ChirpStack

La función Encode se encarga de preparar los valores para ser enviados por la red LoRaWan. El ejemplo muestra el uso de las variables estado y duración:

// Encode encodes the given object into an array of bytes.
//  - fPort contains the LoRaWAN fPort number
//  - obj is an object, e.g. {"temperature": 22.5}
//  - variables contains the device variables e.g. {"calibration": "3.5"} (both the key / value are of type string)
// The function must return an array of bytes, e.g. [225, 230, 255, 0]
function Encode(fPort, obj, variables) {
  var estado = obj["estado"];
  var duracion = obj["duracion"];
  var mensaje = [50,20];
  if (estado == "ON") {
  	mensaje = [49,duracion];
  }
  if (estado == "OFF") {
  	mensaje = [50,20];
  }
  return mensaje;
}

Mensaje MQTT

El mensaje Mqtt que activa la instrucción tiene un formato que requiere para el tema o tópico: identificador de aplicación del ChirpStack, deviceEUI,

el mensaje contiene si se confirma la trama, el puerto de la aplicación y un diccionario «object» con las variables  sus valores correspondientes en texto o numérico.

El ejemplo de la instruccion para el dispositivo es:

mosquitto_pub -h "localhost" -u "usuarioprueba" -P "usuarioclave" -t "application/3/device/bc55318912bfd090/command/down" -m '{"confirmed":true,"fPort":3,"object":{"estado":"ON","duracion":46}}'

Para observar el estado en mqtt para probar el envio del mensase se  usa la instrucción de suscripción:

mosquitto_sub -h "localhost" -u "usuarioprueba" -P "usuarioclave" -t "application/3/device/bc55318912bfd090/command/down"

Home Assitant – configuración

Se realiza combinando una variable numérica (input_number) para la duración en segundos, y se envía como una instrucción de control usando una plantilla de foco dim (light).

light:
  - platform: mqtt
    schema: template
    name: 'RegarPlanta'
    state_topic: "application/3/device/bc55318912bfd090/estado"
    command_topic: "application/3/device/bc55318912bfd090/command/down"
    command_on_template:  '{"confirmed":true,"fPort":3,"object":{"estado":"ON","duracion":{{states("input_number.regar_seg") | int}}}}'
    command_off_template: '{"confirmed":true,"fPort":3,"object":{"estado":"OFF","duracion":0}}'

input_number:
  regar_seg:
    name: regar_segundos
    initial: 10
    min: 0
    max: 254
    step: 1
    mode: box
    icon: mdi:timer-outline
    unit_of_measurement: "s"

Referencia: Send JSON command with MQTT?, https://community.home-assistant.io/t/send-json-command-with-mqtt/37663

Input Number, https://www.home-assistant.io/integrations/input_number

MQTT Light, https://www.home-assistant.io/integrations/light.mqtt/

LoRaWan – Interruptor temporizado. Archivo.ino

Un dispositivo como actuador requiere recibir un mensaje de control desde el broker para establecer el estado del artefacto o cosa. En el caso de LoRaWan el mensaje debe gestionarse por el gateway para transformar un mensaje Mqtt enviado al formato de bytes usado en la red LoRa, es decir un Encoder.

En el dispositivo LoRa se requiere al menos una función para recibir los mensajes, interpretación de los bytes recibidos y con éstos actualizar el estado de los pines de salidas usados como control.

Datos de control

Lo primordial en el dispositivo es el pin de control. Para el ejemplo inicial se usa el LED incorporado en el módulo de desarrollo Pin25.

// actuador
#define LEDPin 25  //LED light
boolean estado = false;  //estado dispositivo

Un dato complementario es la duración del actuador en estado encendido, es un factor necesario en el sistema de riego, pues se debería usar solo por periodos cortos de tiempo. Se aprovecha el modo de ahorro de energía del dispositivo en contraste con los largos periodos sin riego.

Intrucciones Principales

A las instrucciones principales se añaden las líneas para la declaración de las variables. El resto de operaciones se las añade como funciones.

/* HelTec Automation(TM) LoRaWAN 1.0.2 OTAA example use OTAA, CLASS A
 * Solo ESP32+LoRa series boards con licencia http://www.heltec.cn/search/);
 *https://github.com/HelTecAutomation/ESP32_LoRaWAN
*/
#include <ESP32_LoRaWAN.h>
#include "Arduino.h"

/*licencia Heltec ESP32 LoRaWan http://resource.heltec.cn/search */
uint32_t  license[4] = { 0xC254CA22, 0xFB5646A9, 0xA23B184F, 0x8F613844};

/* OTAA parametros*/
uint8_t DevEui[] = { 0xbc, 0x55, 0x31, 0x89, 0x12, 0xbf, 0xd0, 0x90 };
uint8_t AppEui<[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t AppKey[] = { 0x99, 0x42, 0xd4, 0x4b, 0xfc, 0x59, 0x85, 0x0a, 0xad, 0x76, 0x15, 0x3b, 0x7a, 0xdb, 0x50, 0x32 };

/* ABP parametros*/
uint8_t NwkSKey[] = { 0xd6, 0x28, 0x15, 0x0c, 0x99, 0x79, 0xbc, 0xec, 0xed, 0x11, 0x67, 0x75, 0x0b, 0x37, 0xb4, 0xe0 };
uint8_t AppSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint32_t DevAddr  = ( uint32_t )0x00052815;

//LoraWan channelsmask, default channels 0-7/
uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };

DeviceClass_t  loraWanClass = CLASS_A;  /*Soporte de A and C*/
uint32_t appTxDutyCycle = 30000;        /*15000; en [ms]*/
bool overTheAirActivation = true;     /*OTAA or ABP*/
bool loraWanAdr = true;               /*ADR enable*/
bool isTxConfirmed = true;            /*confirmed or unconfirmed messages */
uint8_t appPort = 3;                  /* Application port */
  
/* reintentos de transmisión, en caso de no recibir ack */
uint8_t confirmedNbTrials = 8;

/* Seleccionado de Arduino IDE tools */
uint8_t debugLevel = LoRaWAN_DEBUG_LEVEL; 
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;

// Mensajes por Puerto Serial
volatile boolean serial_msg = true;

// actuador
#define LEDPin 25  //LED light
boolean estado = false;  //estado dispositivo

// Sensor de estado de bateria, revisar modelo de módulo
// PowerDetection 13 en Wireless Stick lite, otros 37
#define Vext 21
#define PowerDetection 13

void setup(){
  Serial.begin(115200);
  while (!Serial);
  SPI.begin(SCK,MISO,MOSI,SS);
  Mcu.init(SS,RST_LoRa,DIO0,DIO1,license);
  deviceState = DEVICE_STATE_INIT;
}
void loop(){
  switch( deviceState )  {
    case DEVICE_STATE_INIT:    {
      LoRaWAN.init(loraWanClass,loraWanRegion);
      break;
    }
    case DEVICE_STATE_JOIN:    {
      LoRaWAN.join();
      break;
    }
    case DEVICE_STATE_SEND:    {
      prepareTxFrame( appPort );
      LoRaWAN.send(loraWanClass);
      deviceState = DEVICE_STATE_CYCLE;
      if (serial_msg){
        Serial.print("  estado: ");
        Serial.println(estado);
      }
      break;
    }
    case DEVICE_STATE_CYCLE:    {
      // Schedule next packet transmission
      txDutyCycleTime = appTxDutyCycle + randr( -APP_TX_DUTYCYCLE_RND,
                                                APP_TX_DUTYCYCLE_RND );
      LoRaWAN.cycle(txDutyCycleTime);
      deviceState = DEVICE_STATE_SLEEP;
      break;
    }
    case DEVICE_STATE_SLEEP:    {
      LoRaWAN.sleep(loraWanClass,debugLevel);
      break;
    }
    default:    {
      deviceState = DEVICE_STATE_INIT;
      break;
    }
  }
}

Recibe trama

La recepción de los datos en la trana, «downlink», se realiza en la variable Buffer que es un arreglo de Bytes. La instrucción de control requiere dos valores: estado y duración , por lo que se usa el primer y segundo Byte. Este detalle se debe ajustar en cada situación para la cantidad de bytes que se requieran.

Recibidos los datos, se invoca a la función para control del actuador «actuador_pin()».

void  downLinkDataHandle(McpsIndication_t *mcpsIndication){
  lora_printf("+REV DATA:%s,RXSIZE %d,PORT %d\r\n",
              mcpsIndication->RxSlot?"RXWIN2":"RXWIN1",
              mcpsIndication->BufferSize,
              mcpsIndication->Port);
  lora_printf("+REV DATA:");
  
  actuador_pin(mcpsIndication->Buffer[0],
                mcpsIndication->Buffer[1]);

  for(uint8_t i=0;i<mcpsIndication->BufferSize;i++)  {
    lora_printf("%02X",mcpsIndication->Buffer[i]);
  }
  lora_printf("\r\n");
}

Controla estado de actuador

Con los dato recibidos en la función anterior, se selecciona la acción para cada caso de valor de estado.

void actuador_pin(uint8_t estado, uint8_t duracion) {
  if (serial_msg){
    Serial.print("\n instruccion recibida: ");
    Serial.print(estado);
    Serial.print("  ");
    Serial.println(duracion);
  }
  switch(estado) {
    case 49: {
      pinMode(LEDPin,OUTPUT);
      digitalWrite(LEDPin, HIGH);
      estado = true;
      if (serial_msg){
        Serial.print("activador: ON ");
        Serial.print(" duración (s): ");
        Serial.print(duracion);
      }
      delay(duracion*1000); // en segundos
      digitalWrite(LEDPin, LOW);
      estado = false;
      break;
    }
    case 50: {
      pinMode(LEDPin,OUTPUT);
      digitalWrite(LEDPin, LOW);
      estado = false;
      if (serial_msg){
        Serial.println("activador: OFF");
      }
      delay(duracion*1000); // en segundos
      break;
    }
    default: {break;}
  }
}

Las siguientes funciones son las mismas usadas en el ejemplo de revisión de estado de batería.

Prepara trama

static void prepareTxFrame( uint8_t port ){
  uint16_t BateriaV = SensorBateria();
  
  appDataSize = 4 ;
  appData[3] = 0 ;
  appData[2] = 0 ;
  appData[1] = highByte(BateriaV);
  appData[0] = lowByte(BateriaV);
  
  if (serial_msg){
    Serial.print("trama datos Byte: ");
    Serial.print(appData[1]);
    Serial.print("  ");
    Serial.println(appData[0]);
  }
}

Lectura de Sensor

uint16_t SensorBateria(){
  // lectura ADC Voltaje de bateria 
  digitalWrite(Vext, LOW);
  delay(10);
  uint16_t ADC_voltage = analogRead(PowerDetection);
  digitalWrite(Vext, HIGH);
  
  if (serial_msg){
    Serial.print("Voltaje Batería (V): ");
    Serial.println(ADC_voltage);
  }
  return ADC_voltage;
}

LoRaWan – Interruptor temporizado con Relé

Un actuador con red LoRaWan puede usarse con varios elementos, por ejemplo para el control de una válvula de paso para riego.

El esquema básico del circuito empieza con tener un pin de salida o control. El primer paso sería controlar el LED del módulo de desarrollo que para el ejemplo es el pin 25. Con esto se puede realizar las pruebas de conectividad de LoRaWan, Mqtt y Home Assistant.

 

El pin de salida puede ser conectado aun circuito para usar un relé para adaptarlo a diferentes niveles de voltaje o controlar otros circuitos de mayor voltaje y corriente.