Banner
Arduino,  Hardware

Haciendo inteligente mi aire acondicionado – Parte 2

<< Continuación de la parte 1

Vuelvo desde el inframundo para continuar con mi «aire acondicionado inteligente» 😄

En la anterior entrada, conseguimos echar a andar un sketch de prueba que permitía controlar el aire acondicionado mediante un arduino mega. Ahora toca portar el sketch a un ESP-01 para hacer uso de su conectividad WiFi y tamaño compacto, ya que no parece haber demasiado hueco dentro de la unidad split del aire acondicionado.

Al igual que con el proyecto de mi lavadora inteligente (que sigue funcionando genial un año después 😊), la idea sería usar MQTT para la comunicación con mi servidor central openHAB, por lo que necesitaremos la librería PubSubClient.

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

.
.
.

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

Para la gestión de conexiones WiFi, he encontrado una librería que nos permite configurar de nuevo la conexión WiFi en caso de que cambiemos de red, sin tener que reprogramar la placa, lo que nos viene genial. Se llama WiFiManager. En este caso, le indicamos que, en caso de no poder conectarse por WiFi a nuestro router, abra una conexión para realizar la configuración, y reintente la conexión pasados 3 minutos (180 segundos).

#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

.
.
.

void setup_wifi() {
  WiFiManager wifiManager;
  wifiManager.setTimeout(180); //3 minutes

  if(!wifiManager.autoConnect(wifiSsid, wifiPassword)) {
    //Retry after 3 minutes with no WiFi connection
    ESP.reset();
    delay(5000);
  }
}

Para el envío de comandos por infrarrojos, al igual que con el sketch de prueba que montamos en la entrada anterior, necesitaremos la librería IRRemote. En este caso, la versión para ESP8266, IRremoteESP8266.

#include <IRremoteESP8266.h>
#include <IRsend.h>

Una vez usadas las librerías equivalentes para el microcontrolador, el resto de la lógica implementada en el sketch de prueba es compatible, por lo que no es necesario mayor cambio, aparte de encapsularlo de forma que podamos llamarlo mediante subrutinas de una forma fácil. En este caso, he trasladado toda esa lógica a un fichero .h, para dejar el fichero .ino principal para las subrutinas de conexión, gestión de topics MQTT, etc.

#include "Configuration.h"
#include "Commands.h"

Usaremos en este caso dos topic MQTT para cada función a controlar del aire acondicionado (un topic para recibir comandos desde el servidor MQTT y otro topic para comunicar al servidor el estado actual de cada función).

const char* mqttPowerStateTopic   = "Home/FF_MasterBedroom/AirConditioner/PowerS";
const char* mqttPowerCommandTopic = "Home/FF_MasterBedroom/AirConditioner/PowerC";
const char* mqttModeStateTopic    = "Home/FF_MasterBedroom/AirConditioner/ModeS";
const char* mqttModeCommandTopic  = "Home/FF_MasterBedroom/AirConditioner/ModeC";
const char* mqttTempStateTopic    = "Home/FF_MasterBedroom/AirConditioner/TemperatureS";
const char* mqttTempCommandTopic  = "Home/FF_MasterBedroom/AirConditioner/TemperatureC";
const char* mqttSpeedStateTopic   = "Home/FF_MasterBedroom/AirConditioner/SpeedS";
const char* mqttSpeedCommandTopic = "Home/FF_MasterBedroom/AirConditioner/SpeedC";

Las funciones que vamos a controlar son:

  • Estado de funcionamiento: ON/OFF
  • Modo: AUTO, COOL (frío), FAN (aire) y HEAT (calor)
  • Velocidad: AUTO, LOW (baja), MED (media) y HIGH (alta)
  • Temperatura: AUTO o un valor entre 17 y 30 grados

El resto del funcionamiento es sencillo: Al recibir comandos por los topic de comando MQTT, actuamos sobre el aire mandando los comandos correspondientes (cambiar de modo, cambiar la temperatura…). En el caso del comando de encendido/apagado, se esperan los comandos ON o OFF:

void callback(char* topic, byte* payload, unsigned int length) {
  String topicString = String(topic);
  byte* payloadZeroTerm = (byte*)malloc(length+1);
  String payloadString;
  byte payloadByte;

// Conversion of payload to String
  memcpy(payloadZeroTerm, payload, length);
  payloadZeroTerm[length] = '\0';
  payloadString = String((char*)payloadZeroTerm);

// Conversion of payload to Byte
  payloadByte = (byte)payloadString.toInt();

// Power Topic: Payload will be "ON" or "OFF"
  if(topicString.equals(String(mqttPowerCommandTopic))) {
    if (payloadString.equals("ON")) {
      sendPowerCommand(true);
    } else if (payloadString.equals("OFF")) {
      sendPowerCommand(false);
    }

.
.
.

En el caso de la velocidad, se esperan los valores AUTO (que corresponde a la velocidad 0 según nuestro sketch de pruebas), LOW (correspondiente a la velocidad 1), MED (velocidad 2) o HIGH (velocidad 3).

.
.
.
// Speed Topic: Payload will be "AUTO", "LOW", "MED", or "HIGH"
  } else if(topicString.equals(String(mqttSpeedCommandTopic))) {
    if (payloadString.equals("AUTO")) {
      sendSpeedCommand(0);
    } else if (payloadString.equals("LOW")) {
      sendSpeedCommand(1);
    } else if (payloadString.equals("MED")) {
      sendSpeedCommand(2);
    } else if (payloadString.equals("HIGH")) {
      sendSpeedCommand(3);
    }
.
.
.

Para el modo, el funcionamiento es prácticamente igual que el caso de la velocidad, solo que con los valores AUTO (modo 0), COOL (modo 1), FAN (modo 2) y HEAT (modo 3).

.
.
.
// Mode Topic: Payload will be "AUTO", "COOL", "FAN" or "HEAT"
  } else if(topicString.equals(String(mqttModeCommandTopic))) {
    if (payloadString.equals("AUTO")) {
      sendModeCommand(0);
    } else if (payloadString.equals("COOL")) {
      sendModeCommand(1);
    } else if (payloadString.equals("FAN")) {
      sendModeCommand(2);
    } else if (payloadString.equals("HEAT")) {
      sendModeCommand(3);
    }
.
.
.

La temperatura puede tener el valor AUTO (valor de temperatura 0 segun nuestro sketch de pruebas) o una cantidad de grados entre 17 y 30.

.
.
.
// Temperature Topic: Payload will be "AUTO" or a number corresponding to the desired temperature, between 17 and 30
  } else if(topicString.equals(String(mqttTempCommandTopic))) {
    if (payloadString.equals("AUTO")) {
      sendTemperatureCommand(0);
    } else {
      if (payloadByte >= 17 && payloadByte <= 30) {
        sendTemperatureCommand(payloadByte);
      }
    }
  }

  publishStates();
}

Aparte de enviar el status de cada función del aire usando los topics de status tras recibir comandos, para comunicar al servidor MQTT que el comando se ha ejecutado, también se envía una actualización de los mismos cada 5 segundos, para casos en los que el status en el servidor se corrompa o restablezca, por ejemplo, debido a un reinicio del servidor.

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

  long now = millis();
  if (now - lastCheck > 5000) {
    lastCheck = now;

    publishStates();
  }
}

void publishStates() {
//  Publish power state
  if (powerState) {
    client.publish(mqttPowerStateTopic, "ON");
  } else {
    client.publish(mqttPowerStateTopic, "OFF");
  }

//  Publish mode
  if (mode == 0) {
    client.publish(mqttModeStateTopic, "AUTO");
  } else if (mode == 1) {
    client.publish(mqttModeStateTopic, "COOL");
  } else if (mode == 2) {
    client.publish(mqttModeStateTopic, "FAN");
  } else if (mode == 3) {
    client.publish(mqttModeStateTopic, "HEAT");
  }

//  Publish temperature
  if (temperature == 0) {
    client.publish(mqttTempStateTopic, "AUTO");
  } else {
    client.publish(mqttTempStateTopic, String(temperature).c_str());
  }

//  Publish speed
  if (fanSpeed == 0) {
    client.publish(mqttSpeedStateTopic, "AUTO");
  } else if (fanSpeed == 1) {
    client.publish(mqttSpeedStateTopic, "LOW");
  } else if (fanSpeed == 2) {
    client.publish(mqttSpeedStateTopic, "MED");
  } else if (fanSpeed == 3) {
    client.publish(mqttSpeedStateTopic, "HIGH");
  } else if (fanSpeed == 4) {
    client.publish(mqttSpeedStateTopic, "SPECIAL");
  } else if (fanSpeed == 5) {
    client.publish(mqttSpeedStateTopic, "OFF");
  }
}

Una vez hecho esto, tenemos la parte de software lista. ¡Hora de coger el destornillador! Veamos qué hay debajo de la carcasa de mi aire, a ver de cuánto sitio disponemos.

Nuestra víctima

Primero, quitemos esa tapa superior y veámos cuantos kilos de polvo hay acumulados 😂

Sí… Me lo esperaba…

Parece que todo se mantiene en su sitio con cuatro tornillos: uno claramente visible al quitar la tapa y otros tres dentro de la ranura de salida de aire, tras unas piezas de plástico que los ocultan. Tras quitarlos, la carcasa completa sale sin problemas.

Eh… Sí… Habrá que darle un buen limpiado…

Así a primeras, la modificación parece fácil: la pequeña placa abajo a la derecha contiene los LEDs de estado y el receptor de infrarrojos, y está conectada con un conector a la placa principal. La idea es pegar un led infrarrojo al lado del receptor para que los comandos que se envíen desde la nueva placa sean visibles al aire, dejando vía libre para si en un futuro (o en caso de todo el tinglado se nos estropee) queremos manejar el aire con el mando a distancia de serie.

La pequeña placa: nuestro punto de actuación

El siguiente paso será crear una pequeña plaquita para conectar el ESP-01 al LED infrarrojos y la instalación de la misma dentro del aire acondicionado. Nos vemos en la siguiente entrada 😊

Continúa en la parte 3 >>

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