Arduino,  Hardware

Haciendo inteligente mi aire acondicionado – Parte 1

¿Qué hay mejor que tener en manos un proyecto de mejora de un aparato electrónico (véase Haciendo Inteligente mi lavadora)? ¡Tener en mano dos proyectos de mejora de aparatos electrónicos!

Tengo un aire acondicionado en mi habitación marca Daitsu. Es un modelo antiguo, el DSG-07HRN2. Mis planes son: quiero manejarlo desde mi móvil, y poder programarlo según X criterios (temperatura, época del año, si voy a estar o no en casa…). La mayoría de estas funciones se pueden programar en OpenHAB, pero por supuesto, primero deberé conectar mi aire acondicionado a mi servidor OpenHAB, cosa que, sin modificaciones, no es posible.

Podría usar algún aparatito mágico (y caro) para manejar aparatos de infrarrojos desde el móvil (sé de su existencia), pero… ¿donde está la diversión? :P. Mi idea es meter otro microcontrolador ESP-01 en mi aire, que inyecte comandos en la placa receptora de infrarrojos, simulando que se ha recibido una señal infrarroja del mando. Y estoy seguro que va a ser una aventura interesante 🙂

Primero, para investigar el protocolo con el que el mando se comunica con la unidad split, he usado el sketch de ejemplo IRrecvDumpV2 de la librería Arduino-IRRemote, creada por Ken Shirriff y con varios contribuidores.

Cuando probé el mando de mi aire con mi arduino MEGA, me quedé extrañado de que decía que usaba protocolo de SAMSUNG. Curioso.

¿Samsung? ¿Daitsu? ¿Usan el mismo protocolo? ¿O es un error del sketch? ¿Lo averiguaremos?

En ese momento me dediqué a pulsar cada botón, y seleccionar cada modo y velocidad (la mayoría de los mandos de aires acondicionados informan al split de todos los parámetros cada vez que se pulsa un botón, por lo que si guardo cada una de las combinaciones posibles, podría investigar cómo están formados los comandos. Abajo podéis ver la lista de los comandos que apunté.

¿Qué es tanto número? Pues parece ser el tiempo en microsegundos que cada pulso de la señal infrarroja está encendida (número positivo) o apagada (número negativo).

Si ordenamos todos los valores de todos los comandos en una hoja excel, vemos las diferencias entre cada uno de los comandos y coloreamos para mostrar fácilmente las diferencias, podemos empezar a ver qué valores llevan qué información.

Un poco de orden entre el caos

Fijándonos detenidamente, cada vez que cambia el valor de temperatura, cambian los valores nº 68, 70, 72 y 74, así que estos valores son los encargados de llevar la temperatura. También vemos que cambian los valores 84, 86, 88 y 90 de igual forma, aunque siempre con valores contrarios a los primeros (si el 68 es un -350 aproximadamente, el 84 es un -1400 aproximadamente, y viceversa). Interpretaremos cada uno de estos valores como verdadero (o «1») y falso (o «0»), lo que nos hará más fácil apuntar e interpretar los valores. En mi caso, llamo verdadero al valor más grande (-350 aproximadamente) y falso al valor más pequeño (-1500 aproximadamente). De esta forma podemos llegar a las siguientes conclusiones:

  • Las posiciones 36, 38 y 40 corresponden a la velocidad. Estas mismas posiciones, igual que comentábamos antes, están repetidas en las posiciones 52, 54 y 56, con valores opuestos a los primeros. Los valores posibles son: 010 (AUTO), 011 (LOW), 101 (MEDIUM), 110 (HIGH) y 100 (APAGADO). También existe otro valor, que sólo está presente en el modo AUTO (frío o calor, dependiendo de la temperatura programada), con valor 111.
  • La posición 46 (repetida con valor opuesto en la posición 62) corresponde al estado de funcionamiento (encendido o apagado). El valor falso corresponde a encendido, y el verdadero a apagado.
  • Las posiciones 68, 70, 72 y 74, como decíamos antes, con sus posiciones con valores opuestos 84, 86, 88 y 90, corresponden a la temperatura. En un principio sospeché (y aun sigo sospechando) que existía algún tipo de correlación entre la cantidad en grados y el valor (interpretado en binario) de estas cuatro posiciones del comando, pero no he conseguido ver el patrón. Los valores posibles son: 0001 (Apagado), 1111 (17ºC), 1110 (18ºC), 1100 (19ºC), 1101 (20ºC), 1001 (21ºC), 1000 (22ºC), 1010 (23ºC), 1011 (24ºC), 0011 (25ºC), 0010 (26ºC), 0110 (27ºC), 0111 (28ºC), 0101 (29ºC) y 0100 (30ºC).
  • Las posiciones 76 y 78 (con sus posiciones con valores opuestos 92 y 94) se corresponden con el modo de funcionamiento, siendo 01 el modo AUTO (frío o calor según temperatura actual y temperatura seleccionada), 11 el modo COOL (frío), 10 el modo FAN (ventilador) y 00 el modo HEAT (calor).
  • El comando completo (posiciones 1 a la 99) se repiten con el mismo valor en las posiciones 101 a 199 (siendo la posición que resta un valor de aproximadamente 4600). Entiendo que esto es así para asegurar que el comando se ha enviado correctamente y no se ha corrompido por el camino, debido a bloqueo del rayo infrarrojo.

Con toda esta información, me dispuse a montar un pequeño sketch de arduino, y así ir confirmando los datos que había descubierto.

Primero, me declaré un array el cual rellené con los valores fijos del comando, dejando a cero en un principio, los valores que iban a ser variables (velocidad, encendido/apagado, temperatura y modo).

uint16_t commandTemplate[] = {4660, 4240, 768,  1422, 768,  348,  768,  1422, 768,  1422, 768,  348,  768,  348,  768,  1422, //Unknown - Header?
                              768,  348,  768,  348,  768,  1422, 768,  348,  768,  348,  768,  1422, 768,  1422, 768,  348,  //Unknown - Filler? Other functions?
                              768,  1422, 768,  0,    768,  0,    768,  0,    768,  1422, 768,  1422, 768,  0,    768,  1422, //Speed and ON/OFF
                              768,  1422, 768,  0,    768,  0,    768,  0,    768,  348,  768,  348,  768,  0,    768,  348,  //Speed and ON/OFF - repetition (inverted)
                              768,  348,  768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  348,  //Temperature and mode
                              768,  348,  768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  1422, //Temperature and mode - repetition (inverted)
                              768,  1422, 768,                                                                                //Unknown - Trailer?
                              4660,                                                                                           //Separator - Command repetition ---------------------
                              4660, 4240, 768,  1422, 768,  348,  768,  1422, 768,  1422, 768,  348,  768,  348,  768,  1422, //Unknown - Header?
                              768,  348,  768,  348,  768,  1422, 768,  348,  768,  348,  768,  1422, 768,  1422, 768,  348,  //Unknown - Filler? Other functions?
                              768,  1422, 768,  0,    768,  0,    768,  0,    768,  1422, 768,  1422, 768,  0,    768,  1422, //Speed and ON/OFF
                              768,  1422, 768,  0,    768,  0,    768,  0,    768,  348,  768,  348,  768,  0,    768,  348,  //Speed and ON/OFF - repetition (inverted)
                              768,  348,  768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  348,  //Temperature and mode
                              768,  348,  768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  0,    768,  1422, //Temperature and mode - repetition (inverted)
                              768,  1422, 768};                                                                               //Unknown - Trailer?

Declaré también varios arrays, uno para cada control del aire acondicionado (temperatura, velocidad…), con las representaciones binarias de cada valor:

/**
 * I'm not exactly sure if there is correlation between the temperature in degrees and the binary values. If there is, I
 * have not been able to find it. Here are the specific codes for every temperature.
 */
const boolean temperatureCommandValues[][4] { {false, false, false, true},    //OFF  -> 0001
                                              {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},    //Filler positions
                                              {true,  true,  true,  true},    //17ºC -> 1111
                                              {true,  true,  true,  false},   //18ºC -> 1110
                                              {true,  true,  false, false},   //19ºC -> 1100
                                              {true,  true,  false, true},    //20ºC -> 1101
                                              {true,  false, false, true},    //21ºC -> 1001
                                              {true,  false, false, false},   //22ºC -> 1000
                                              {true,  false, true,  false},   //23ºC -> 1010
                                              {true,  false, true,  true},    //24ºC -> 1011
                                              {false, false, true,  true},    //25ºC -> 0011
                                              {false, false, true,  false},   //26ºC -> 0010
                                              {false, true,  true,  false},   //27ºC -> 0110
                                              {false, true,  true,  true},    //28ºC -> 0111
                                              {false, true,  false, true},    //29ºC -> 0101
                                              {false, true,  false, false} }; //30ºC -> 0100

/**
 * Weirdly enough, there is an special speed value for AUTO mode. Value AUTO for speed is not accepted by the machine,
 * so the special value is used.
 */
const boolean speedCommandValues[][3] { {false, true,  false},   //AUTO -> 010
                                        {false, true,  true},    //LOW  -> 011
                                        {true,  false, true},    //MED  -> 101
                                        {true,  true,  false},   //HIGH -> 110
                                        {true,  true,  true},    //SPECIAL -> 111
                                        {true,  false, false} }; //OFF -> 100

/**
 * Another weird point here is the value for COOL here is the same as the value for OFF mode. Oh well...
 */
const boolean modeCommandValues[][2] { {false, true},    //AUTO -> 01
                                       {true,  true},    //COOL -> 11, also OFF
                                       {true,  false},   //FAN  -> 10
                                       {false, false} }; //HEAT -> 00

Con algunas subrutinas para controlar cómo cargar cada valor, tuve funcionando el pequeño sketch, que usaré de base para mi ESP-01. Os lo dejo abajo, para que podáis descargarlo, así como en mi github. Lo siguiente es migrarlo al ESP-01, hacer que interprete las órdenes de mi OpenHAB y mande los comandos correctos, antes de empezar con las modificaciones de hardware 🙂

¡Nos vemos en la siguiente entrada!

Continúa en la parte 2 >>

Un comentario

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