Arduino

Haciendo inteligente mi lavadora – Parte 4

<< Continuación de la parte 3

Hora de tocar la parte de software de este proyecto. Si recordamos, el requerimiento es simple: Hacer que cuando la lavadora termine el lavado (desbloquee la puerta), se lance una notificación a mi teléfono móvil.

Para enviar notificaciones, podría elegir una de muchas rutas posibles: por e-mail, por alguno de los servicios que existen de comunicaciones, a través de una app que me diseñe yo mismo… Para ahorrar tiempo, decidí usar uno de los servicios que existen para notificaciones simples: pushbullet.

Pushbullet te permite, entre muchas otras opciones, enviar pequeñas notificaciones de texto a todos los dispositivos que tengas enlazados. Podría entonces, desde mi microcontrolador ESP8266, enviar una notificación y recibirla en el móvil. Pero decidí ir un poco más lejos: Tengo un mini-ordenador como centro domótico en mi casa: un orange PI PC con OpenHAB, que me permite controlar centralizadamente mis enchufes inteligentes. ¿Y si conecto mi lavadora al sistema y es mi centro domótico el encargado de enviar dichas notificaciones? Así, también podré controlar el estado de la lavadora y enviar órdenes de puesta en marcha de forma remota.

Empecemos con la forma de comunicación que va a tener el ESP-01 con mi centro domótico. En este caso, decidí elegir MQTT, por lo simple que es: Defines canales (topics) de comunicación, el dispositivo que vaya a escuchar esos canales se suscribe a ellos, y el dispositivo que vaya a enviar publica los mensajes a dicho topic. Para el uso de MQTT con los microcontroladores ESP8266, existe la librería PubSubClient, que nos facilitará el envío de mensajes entre un dispositivo y otro.

Así que comencemos nuestro proyecto en Arduino IDE. Lo primero, la conexión wifi a mi punto de acceso y la conexión al servidor MQTT (que tengo en mi centro domótico). En mi caso, he separado los datos de conexión en un fichero .h, para tenerlos «desconectados» del código principal:

//Connection.h:

const char* ssid         = "<YourSSID>";
const char* password     = "<YourSSIDPassword>";
const char* mqttServer   = "<YourMQTTServerIP>";
const int   mqttPort     = 1883;
const char* mqttUser     = "<YourMQTTServerUser>";
const char* mqttPassword = "<YourMQTTServerPassword>";

Creamos un par de subrutinas, para preparar la conexión y conectar al servidor, así como el armazón de setup() y loop(). Aquí, también hemos definido los dos topics mqttDoorTopic, que usaremos para transmitir el estado de bloqueo de la puerta de la lavadora, y mqttStartTopic, que usaremos para recibir la orden de comenzar el lavado:

#include <WiFiClient.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "Connection.h"

const char* mqttClientId   = "GF_WashingMachine";
const char* mqttDoorTopic  = "Home/GF_Patio/WashingMachine/Door";
const char* mqttStartTopic = "Home/GF_Patio/WashingMachine/Start";

WiFiClient espClient;
PubSubClient client(espClient);


void setup() {
  setup_wifi();
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

void setup_wifi() {
  delay(10);

  // We start by connecting to a WiFi network
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    // Attempt to connect
    if (client.connect(mqttClientId, mqttUser, mqttPassword)) {
      // Once connected, resubscribe
      client.subscribe(mqttStartTopic);
    } else {
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

Los pines GPIO0 y GPIO2 del ESP-01 se usan para elegir el modo de inicio al momento de alimentar la placa, lo que hacía incompatible el circuito que explicaba en la entrada anterior. Para solucionarlo, podemos usar los pines RX y TX como pines GPIO, usando el modo FUNCTION_3 en el setup(). También indicamos si van a ser de salida o entrada:

  //Disable Serial pins in order to use them as GPIO
  pinMode(1,FUNCTION_3); //TX
  pinMode(3,FUNCTION_3); //RX

  pinMode(startCyclePin, OUTPUT);   // For washing cycle start
  pinMode(doorLockPin, INPUT);      // For door lock detection

Creamos una función que nos devuelva el estado de bloqueo de puerta, leyendo el estado del pin 1:

bool isDoorLocked() {
  //Returns true if pin is high (door is locked); false otherwise
  return digitalRead(doorLockPin) == 1;
}

Creamos también una subrutina para el inicio de lavado, que active durante medio segundo el relé que puenteará el botón de inicio (pin 3).

void startWashingCycle() {
  if(!isDoorLocked()) {
    //Presses the start button for 0.5 seconds
    digitalWrite(startCyclePin, HIGH);
    delay(500);
    //Releases the start button and waits for the washing cycle to start
    //(the door will lock after some time)
    digitalWrite(startCyclePin, LOW);
    while(!isDoorLocked()) {
      delay(1000);
    }
  }
}

En el loop(), se comprueba cada 5 segundos el estado de bloqueo de la puerta, y se publica al topic de bloqueo de puerta. Si la puerta estaba bloqueada y se desbloquea, quedándose desbloqueada por 10 segundos, se entiende que el lavado ha finalizado, y se transmite el estado «OPEN», actualizando el estado de funcionamiento también a «OFF». Si la puerta estaba desbloqueada y se bloquea, se transmite el estado de bloqueo «CLOSED» y el estado de funcionamiento «ON»:

long now = millis();
  //Check status every 5 seconds
  if (now - lastCheck > 5000) {
    lastCheck = now;

    if (isDoorLocked()) {
      if (!wasDoorLocked) {
        wasDoorLocked = true;
        client.publish(mqttStartTopic, "ON");
      }
      
      client.publish(mqttDoorTopic, "CLOSED");

      if (doorUnlockedFor > 0) {
        doorUnlockedFor = 0;
      }
    } else {
      if (wasDoorLocked) {
        doorUnlockedFor++;
      }

      //Door needs to be unlocked for 10 seconds (2 checks). This is
      //done to avoid misunderstanding power fluctuations as
      //unlocking signals.
      if (doorUnlockedFor >= 2) {
        client.publish(mqttDoorTopic, "OPEN");

        client.publish(mqttStartTopic, "OFF");
        wasDoorLocked = false;
      }
    }
  }

Por último, se controla cuándo se recibe la orden externa de inicio de lavado en la subrutina callback(). Se convierte el contenido del mensaje a string, agregando un carácter «\0» delante, y se comprueba si es la orden de inicio «ON». Si es así, se llama a la subrutina de inicio de lavado:

  //Add \0 to payload, to convert it to string later
  byte* payloadNull = (byte*)malloc(length+1);
  memcpy(payloadNull, payload, length);
  payloadNull[length] = '\0';

  if(String(topic).equals(String(mqttStartTopic))) {
    if (String((char*)payloadNull).equals("ON")) {
      startWashingCycle();
    }
  }

Con esto es suficiente para la parte del microcontrolador. Os dejo el código en github por si lo queréis :). Lo que queda es entrar en mi servidor OpenHAB y configurar la lavadora para que esté disponible.

En el servidor, será necesario el broker MQTT (o sea, el servidor que maneje los mensajes y a quién van dirigidos) y el plugin (binding) para agregar dispositivos MQTT. También, en este caso, será necesario agregar el plugin para envío de notificaciones a través de PushBullet.

Agregamos el MQTT broker:

Agregamos el binding MQTT:

Y por último el conector con PushBullet:

Necesitaremos agregar el dispositivo (thing) de la lavadora, sus canales de comunicación y sus items, para poder controlarlos. Empezamos con el dispositivo:

El thing ID lo dejé como estaba, aunque puede ser buena idea personalizarlo, para que sea más fácil de recordar 🙂

Se agregan sus canales. Para el bloqueo de puerta, uno de tipo contacto, que tiene los valores posibles «OPEN» y «CLOSED». Para el inicio de lavado, uno de tipo switch, que tiene los valores posibles «ON» y «OFF». Es importante asignar los mismos topics que nos declaramos en el ESP-01, si no, no funcionará por supuesto:

Se mapean los canales a dos nuevos items, para dar control sobre ellos:

Con esto ya tenemos acceso al estado de la lavadora:

Ahora para lanzar las notificaciones cuando el lavado se complete, es necesario tener instalado el plugin de reglas:

La interacción con el plugin de PushBullet aún no se puede hacer de forma gráfica, por lo que para esto habrá que hacerlo de la forma antigua: accediendo por SSH al servidor OpenHAB. Será necesario agregar un nuevo fichero a /etc/openhab2/rules. En mi caso creé el archivo «lavadora.rules», con el siguiente contenido:

rule "lavadora"
when
    Item Lavadora_BloqueoPuerta changed from CLOSED to OPEN
then
    sendPushbulletNote("Lavadora", "Lavado completado")
end

Con eso, cuando el bloqueo de puerta de la lavadora se cambia de CLOSED a OPEN, se lanza la notificación.

Lo último que hacer, conectar las tripas a la lavadora 🙂

¡Nos vemos en la siguiente entrada!

Continúa en la parte 5 >>

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Ver
Privacidad