No, si realmente no tengo problemas ni de cobertura, ni de velocidad. Tengo una red mallada con 2 puntos de acceso y cobertura al 100% en toda la casa con 200 mbps simétricos de ancho de banda (con Wifi). Pero lo que no quería era que una vez instalado todo y funcionando, por lo que sea, se estropearan mis puntos de acceso, o el router, o cualquier elemento de red y que dejaran de funcionar las luces xD.
Quiero que los arduinos funcionen de manera autónoma sin que se vean afectados por una caída eventual de la red o del servidor MQTT.
De todas maneras he vuelto a la solución inicial. Prescindiré de los interrupts por el momento y verificaré los cambios en los interruptores dentro del loop. En caso de que se caiga la red tendré un delay de 1000ms en cada loop hasta que consiga conectar. De esta manera solo tendré un tiempo de respuesta como mucho de 1 segundo desde que pulso el interruptor hasta que responde la luz. Tampoco es mucho tiempo y de momento soluciono la papeleta.
#include "controlReles.h"
#include "controlInterruptor.h"
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>
// Definimos el SSID de la Wifi
char ssid[] = "XXXXXX";
char password[] = "XXXXXX";
// Vinculamos el cliente wifi con el cliente MQTT
WiFiClient clienteWifi;
PubSubClient client(clienteWifi);
// Control del led de la placa
unsigned int led = 0;
// Cada 30 segundos publicaremos un mensaje en el topic de estado confirmando que seguimos online
unsigned int estoyVivo = 0; // Tiempo de la última notificación
bool iniciado = false; // En el primer bucle también lo publicamos
// Zona de la casa que cubre este dispositivo (la usaremos para identificarlo y para construír los topics)
char* zona = "pasillo";
// Definimos las variables de los topics del RELE 1 para el servidor mqtt
char topicEstadoRele1[50];
char topicSetRele1[50];
// Definimos las variables de los topics del RELE 2 para el servidor mqtt
char topicEstadoRele2[50];
char topicSetRele2[50];
// Definimos las variables del topic de disponibilidad (alive)
char topicDisponible[50];
// Definimos la dirección IP del servidor MQTT, así como el usuario y contraseña
const char* mqtt_uri = "XXX.XXX.XXX.XXX";
const char* mqtt_user = "****";
const char* mqtt_pass = "****";
// Cuando se actua sobre un relé, se almacena su estado para posteriormente notificarlo al servidor MQTT
char* pendienteRele1 = NULL;
char* pendienteRele2 = NULL;
/**
* Función que conectará con la red Wifi
*/
void setup_wifi () {
// Definimos el modo Wifi a Station
WiFi.mode(WIFI_STA);
// Conectamos con la red Wifi
WiFi.begin(ssid, password);
// Esperaremos a conectar al Wifi un total de 5 iteraciones (1000ms cada una)
int iteraciones = 0;
Serial.print("Conectando a la red Wifi ");
while (WiFi.status() != WL_CONNECTED && iteraciones < 5) {
Serial.print(".");
delay(1000);
iteraciones = iteraciones + 1;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("No se ha podido conectar a la red Wifi, seguimos intentándolo en segundo plano");
} else {
Serial.println("Conexión a la red Wifi correcta");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
ArduinoOTA.begin();
}
// Entropía... xD
randomSeed(micros());
}
/**
* Función de callback para cuando recibimos algún mensaje en los topics suscritos
*/
void mensajeRecibido(char* topic, byte* payload, unsigned int length) {
String orden;
for (int i = 0; i < length; i++) {
orden += (char)payload[i];
}
int numeroRele = (strcmp(topic, topicSetRele1) == 0) ? 1 : 2;
if (orden == "ON") {
Serial.print("Recibida instrucción para encender el rele: ");
Serial.println(numeroRele);
encenderRele(numeroRele);
} else {
Serial.print("Recibida instrucción para apagar el rele: ");
Serial.println(numeroRele);
apagarRele(numeroRele);
}
}
/**
* Función que tratará de reconectar con el servidor mqtt
*/
void reconnect() {
// Aunque el bucle solo se ejecutará una vez, no quitamos la estructura del while por si
// en un futuro decidimos iterar más veces
unsigned int intentos = 0;
while (!client.connected() && intentos < 1) {
Serial.print("Conectando al servidor mqtt: ");
Serial.println(mqtt_uri);
// Creamos un ID aleatorio
String clientId = "ESP8266Client-";
clientId += String(zona);
clientId += "-";
clientId += String(random(0xffff), HEX);
intentos++;
// Intentamos conectar
if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass, topicDisponible, 0, 0, "offline", true)) {
Serial.println("Conectado al servidor MQTT");
// Nos suscribimos de nuevo a los topics
client.subscribe(topicSetRele1);
client.subscribe(topicSetRele2);
// Publicamos el estado "online"
client.publish(topicDisponible, "online");
estoyVivo = millis();
} else {
Serial.print("Ha fallado la conexión, rc=");
Serial.print(client.state());
// Esperando 1 segundo para seguir con el loop
delay(1000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.println("Iniciando el dispositivo");
pinMode(BUILTIN_LED, OUTPUT);
// Definimos los topics
sprintf(topicEstadoRele1, "home/%s/luz1/state", zona);
sprintf(topicSetRele1, "home/%s/luz1/set", zona);
sprintf(topicEstadoRele2, "home/%s/luz2/state", zona);
sprintf(topicSetRele2, "home/%s/luz2/set", zona);
sprintf(topicDisponible, "home/%s/status", zona);
// Setup de relés
iniciarReles();
// Setup de interruptores
setupInterruptores();
// Setup de wifi
setup_wifi();
// Setup de MQTT
client.setServer(mqtt_uri, 1883);
client.setCallback(mensajeRecibido);
// Definimos el callback al que hay que llamar cuando se modifica algún relé
onCambioRele([](int rele, char* valor) {
if (rele == 1) {
pendienteRele1 = valor;
} else if (rele == 2) {
pendienteRele2 = valor;
}
});
}
void loop() {
// Gestión de las actualizaciones OTA
ArduinoOTA.handle();
// Loop de interruptores
loopInterruptores();
// Si estamos conectados ejecutamos toda la lógica relacionada con la conectividad MQTT
if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) {
reconnect();
} else {
if (pendienteRele1) {
client.publish(topicEstadoRele1, pendienteRele1);
pendienteRele1 = NULL;
}
if (pendienteRele2) {
client.publish(topicEstadoRele2, pendienteRele2);
pendienteRele2 = NULL;
}
// Notificamos que estamos vivos cada 30 segundos
unsigned int ahora = millis();
// Pasados 47 días, millis() se reinicia, por lo que es necesario verificar si ha vuelto a 0 (segunda condición del IF)
if (ahora - estoyVivo > 30000 || ahora - estoyVivo < 0 || !iniciado) {
client.publish(topicDisponible, "online");
estoyVivo = millis();
iniciado = true;
}
client.loop();
}
}
// Parpadea el led de ESP8266 cada 500 ms
int ahora = millis();
if (ahora - led > 500 || ahora - led < 0) {
int estado = digitalRead(LED_BUILTIN);
digitalWrite(LED_BUILTIN, !estado);
led = ahora;
}
}