El prototipo para un gateway simple se inicia con la función de recepción de mensajes que hay que procesar para enviarlos aun servidor MQTT. A partir de donde se gestionan los datos de los sensores.
Para el prototipo se usa placa de desarrollo que contiene: un módulo LoRa y un SoC ESP32. Si se reutiliza algunos componentes de algoritmos usados para los dispositivos con WiFi, la versión inicial se conecta un router IP via WiFi de donde se envia el mensaje MQTT.
El mensaje MQTT require la descripción de un tópico, por lo que en la conformación del tópico se usa la dirección de envío.
Los valores usados en el mensaje MQTT, son el estado del sensor u otro valor que se requiera. Para facilitar el seguimiento inicial de datos, se publica el identificador de mensaje, que es un contador ascendente que permite observar la secuencia del número de mensaje.
Gateway Multipunto – Recepción Lora y envío MQTT
Para el ejercicio se habilitan dos dispositivos «D1» y «D2», comprobando los mensajes recibidos de varias formas:
– mensajes por puerto serial
– mensajes MQTT en servidor
Instrucciones en Arduino
Los bloques se crean a partir del ejemplo de LoRa Mutipunto para dispositivos, aprovechando que el módulo Heltec LoRa-ESP32 tiene incorporado WiFi. Aunque no se encuentran todos los canales disponibles al mismo tiempo para recibir todas las señales, se aprovecha que si el canal está libre se puede realizar la transmisión del estado del sensor desde LoRa hacia WiFi para llegar hasta el broker MQTT.
El ejercicio es una prueba de concepto, pues un gateway completo debe estar atento a todos los canales de transmisión y tener la capacida de atención a cada uno de ellos simultaneamente.
Bloque principal
En el bloque principal se adjuntan los parámetros de conexión por WiFi, asi como las instrucciones de inicialización. Desde luego será necesario añadir los procedimientos de WiFi y MQTT.
/* Dispositivo Gateway: LoRa/WiFi/MQTT/Home-Assistant * http://blog.espol.edu.ec/girni/lora-multipunto-esquema/ * Referencia: Aaron.Lee www.heltec.cn, * https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series */ #include "heltec.h" #include <WiFi.h> #include <PubSubClient.h> // LoRa Banda ISM en Región 915Mhz #define BAND 915E6 //433E6,868E6,915E6 byte spread_factor = 8; // rango 6-12,default 7 // Mensaje LoRa a enviar por direcciones byte dir_local = 0xC1; // Dispositivo 1 byte dir_destino = 0xD1; // Dispositivo 2 byte id_msjLoRa = 0; // cuenta mensaje String paqueteEnv = ""; // mensaje // Mensaje LoRa recibido byte dir_envio = 0xC1; // receptor byte dir_remite = 0xD1; // emisor String paqueteRcb = "OFF"; // mensaje LoRa byte paqRcb_ID = 0; byte paqRcb_Estado = 0; // 0:vacio, 1: nuevo, 2:incompleto // 3:otro destinatario, 4:Broadcast // LED interno, ESP01-pin=1, ESP07-pin=2 int LED_pin = LED; //LED interno Heltec // Mensajes por Puerto Serial boolean serial_msj = true; // tiempo entre lecturas long tiempo_antes = 0; int tiempo_intervalo = 6000; long tiempo_espera = tiempo_intervalo + random(3000); // WIFI: conexión a Router char* ssid = "miRouter"; char* password = "miRouterclave"; // MQTT: Servidor char* MQTT_IP = "192.168.10.50"; uint16_t MQTT_puerto = 1883; char* MQTT_usuario = "usuarioprueba"; char* MQTT_contrasena = "usuarioclave"; // MQTT: Dispositivo Sensor char* MQTT_ID = "LoraGatewayC1"; String topico_base = "invernadero/lora"; String topico_valor = "/valor"; char MQTT_TOPIC[50] = ""; // construido en algoritmo char MQTT_SensorEstado[10] = "OFF"; boolean mqtt_desconectado = true; // MQTT: Dispositivo Actuador String topico_accion = "/cambia"; char MQTT_COMMAND[50] = ""; char MQTT_ActuadorEstado[10] = "OFF"; boolean actuador_estado = false; boolean actuador_bandera = false; char* sensor_ON = "ON"; char* sensor_OFF = "OFF"; // Clientes WiFi y MQTT WiFiClient wificlient; PubSubClient mqttclient(wificlient); void setup(){ Heltec.begin(false /*DisplayEnable Enable*/, true /*Heltec.LoRa Disable*/, serial_msj /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/); LoRa.setSpreadingFactor(spread_factor); //LoRa.onReceive(cbk); LoRa.receive(); inicia_wifi(); // conexión WIFI y MQTT if (WiFi.status() == WL_CONNECTED){ inicia_mqtt(); } } void loop(){ // LoRa Revisa mensajes de dispositivos int msjRcbLoRa = LoRa.parsePacket(); if (msjRcbLoRa !=0){ recibe_lora(msjRcbLoRa); int rssi_lora = LoRa.packetRssi(); int snr_lora = LoRa.packetSnr(); if (serial_msj==true){ if (paqRcb_Estado == 1){ Serial.print("Recibido: "); Serial.print(String(dir_remite,HEX));Serial.print(","); Serial.print(String(dir_envio,HEX)); Serial.print(","); Serial.print(paqRcb_ID); Serial.print(","); Serial.print(paqueteRcb); Serial.print(","); Serial.print(rssi_lora); Serial.print(","); Serial.print(snr_lora); Serial.print(","); Serial.println(); }else{ Serial.print("Paquete recibido Estado: "); Serial.println(paqRcb_Estado); } } // LED parpadea. Rebibido LoRa digitalWrite(LED, HIGH); delay(50); digitalWrite(LED, LOW ); delay(50); digitalWrite(LED, HIGH); delay(50); digitalWrite(LED, LOW ); } // MQTT publica estado recibido desde LoRa if (msjRcbLoRa !=0 && paqRcb_Estado == 1){ if (mqttclient.connected()==true){ publica_mqtt(); } } // LoRa Reenvia a dispositivo if (actuador_bandera == true){ id_msjLoRa = id_msjLoRa +1; envia_lora(dir_destino, dir_local, id_msjLoRa, paqueteEnv); actuador_bandera = false; } // Revisa estado WiFi y MQTT if (WiFi.status() != WL_CONNECTED){ inicia_wifi(); }else{ if (mqttclient.connected () == false){ mqtt_desconectado = true; inicia_mqtt(); // reintento } if (mqttclient.connected() == true){ if (mqtt_desconectado==true){ publica_mqtt(); mqtt_desconectado=false; } mqttclient.loop(); } } delay(100); }
LoRa – envío de mensajes
void envia_lora(byte destino, byte remite, byte paqueteID, String paquete){ // espera radio para enviar un paquete while(LoRa.beginPacket() == 0){ if (serial_msj==true){ Serial.println("Esperando radio disponible..."); } delay(100); } // envio del mensaje LoRa LoRa.beginPacket(); LoRa.write(destino); LoRa.write(remite); LoRa.write(paqueteID); LoRa.write(paquete.length()); LoRa.print(paquete); LoRa.endPacket(); }
LoRa – Recibe mensajes
void recibe_lora(int tamano){ if (tamano == 0){ paqRcb_Estado = 0; //vacio return; } // lectura de paquete paqueteRcb = ""; dir_envio = LoRa.read(); dir_remite = LoRa.read(); paqRcb_ID = LoRa.read(); byte paqrcbTamano = LoRa.read(); while(LoRa.available()){ paqueteRcb += (char)LoRa.read(); } if (paqrcbTamano != paqueteRcb.length()){ paqRcb_Estado = 2; // tamaño incompleto return; } if (dir_envio != dir_local){ paqRcb_Estado = 3; // otro destino return; } if (dir_envio == 0xFF) { paqRcb_Estado = 4; // Broadcast, difusion return; } paqRcb_Estado = 1; // mensaje Nuevo }
MQTT- inicia
void inicia_mqtt(void){ int esperamqtt = 5; int cuentamqtt = 0; if (serial_msj){ Serial.print(" MQTT Conectando a "); Serial.println(MQTT_IP); } mqttclient.setServer(MQTT_IP, MQTT_puerto); mqttclient.connect(MQTT_ID, MQTT_usuario, MQTT_contrasena); mqttclient.setCallback(recibe_mqtt); while (!mqttclient.connected() && (cuentamqtt<=esperamqtt)) { cuentamqtt = cuentamqtt + 1; if (serial_msj){ Serial.print("."); } // LED Monitor parpadeo MQTT digitalWrite(LED_pin, HIGH); delay(200); digitalWrite(LED_pin, LOW); delay(200); } if (mqttclient.connected()){ // publica_mqtt(); // suscribe a todos los topicos base } if (serial_msj){ //Fin de "...." Serial.print("\n MQTT Conectado: "); Serial.print(mqttclient.connected()); Serial.print("\t Estado: "); Serial.println(mqttclient.state()); } }
MQTT- publica mensaje
void publica_mqtt() { paqueteRcb.toCharArray(MQTT_SensorEstado, paqueteRcb.length()+1); // MQTT Construye tópico por remitente String remite = String(dir_remite, HEX); remite.toUpperCase(); String topico = topico_base + remite + topico_valor; topico.toCharArray(MQTT_TOPIC,topico.length()+1); if (serial_msj==true){ Serial.print(" "); Serial.print(topico); Serial.print("/"); Serial.println(MQTT_SensorEstado); } // MQTT Construye tópico por remitente String topico_cmd = ""; topico_cmd += topico_base + remite +"/cambia"; topico_cmd.toCharArray(MQTT_COMMAND,topico_cmd.length()+1); if (serial_msj==true){ Serial.print(" sucrito: "); Serial.println(MQTT_COMMAND); } if (mqttclient.connected()==true){ mqttclient.publish(MQTT_TOPIC,MQTT_SensorEstado,true); mqttclient.subscribe(MQTT_COMMAND); }else{ mqtt_desconectado = true; } }
MQTT- Recibe mensaje
// llega mensaje MQTT, callback mqtt sin confirmación void recibe_mqtt(char* p_topic, byte* p_payload, unsigned int p_length) { if (serial_msj){ Serial.println("Recibe mensaje MQTT"); Serial.println(p_topic); } // convierte a texto String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } paqueteEnv = payload; // direccion destino char dispositivo[3] = "D0"; int desde = topico_base.length(); dispositivo[0] = p_topic[desde]; dispositivo[1] = p_topic[desde+1]; dir_destino = (int) strtol(dispositivo,NULL,16); actuador_bandera = true; if (mqttclient.connected()==true){ mqttclient.subscribe(MQTT_COMMAND); }else{ mqtt_desconectado = true; } }
WiFi – inicio
void inicia_wifi(void) { int esperawifi = 10; // >=10 para conectar int cuentawifi = 0; if (serial_msj){ Serial.print(" WiFi Conectando a "); Serial.println(ssid); } WiFi.disconnect(true); delay(1000); WiFi.mode(WIFI_STA); WiFi.setAutoConnect(true); WiFi.begin(ssid,password); delay(100); while(WiFi.status() != WL_CONNECTED && (cuentawifi < esperawifi)){ cuentawifi = cuentawifi + 1; if (serial_msj){ Serial.print("."); } // Parpadeo de Monitor Wifi digitalWrite(LED_pin, HIGH); delay(300); digitalWrite(LED_pin, LOW); delay(200); } if (serial_msj){ // mensaje a serial, Fin de "..." Serial.println(); if (WiFi.status() == WL_CONNECTED){ Serial.print(" Estado: "); Serial.println(WiFi.status()); Serial.print(" MAC: "); Serial.println(WiFi.macAddress()); Serial.print(" IP: "); Serial.println(WiFi.localIP()); Serial.print(" RSSI: "); Serial.println(WiFi.RSSI()); Serial.println(); } if (WiFi.status() != WL_CONNECTED){ WiFi.printDiag(Serial); Serial.println(); } } }
.