martes, 29 de agosto de 2017

Dispositivos SonOff en HomeAssistant

En este artículo veremos cómo podemos integrar algunos dispositivos SonOff en nuestro HomeAssistant.

Dispositivos SonOff

SonOff es una serie de dispositivos inteligentes fabricados por el fabricante chino Itead que nos permiten de una manera fácil y económica montar un sistema de domótica en nuestra casa.
Casi todos los componentes de esta serie están basados en el chip ESP8266(EX) y algunos de ellos en el más evolucionado ESP8265, y aunque Itead proporciona una aplicación móvil y una nube propia para la gestión de sus dispositivos, se da la circunstancia de que son dispositivos fácilmente modificables para adaptarlos a nuestras necesidades particulares.
¡ATENCIÓN! El simple hecho de cortar el sello de papel que traen los dispositivos para abrir su carcasa hace que invalidemos su garantía. Lógicamente, si cambiamos su firmware ya no podremos obtener ningún soporte del fabricante ni del vendedor.
Los componentes más conocidos y más baratos son los siguientes:

SonOff Basic

Este dispositivo es el más básico y barato de la serie. Se trata de un interruptor Wifi, que en principio sólo nos permite eso: encender y apagar un aparato conectado a él.
SonOff Basic
Está basado en el SoC ESP8266EX, es de reducido tamaño y dispone de un par de terminales de entrada (vivo y neutro) y otro par de salida, con un relé intermedio que se activa y desactiva por medio del ESP8266 mediante su conexión Wifi. Además tiene un pequeño pulsador que permite activar y desactivar manualmente la conexión del relé y un led que se activa cuando el relé está cerrado.
Tamaño SonOff Basic
Detalle ESP8266EX
Vista general SonOff Basic
Pero sin duda, lo más interesante de este dispositivo es que dispone de unos pequeños agujeritos donde, soldando una regleta de 5 pines, podemos acceder a ciertas patillas del SoC pudiendo reprogramarlo a nuestro gusto y conectarle algunos periféricos extra. En la siguiente imagen vemos la disposición de estas conexiones:
GPIOS SonOff Basic
Podemos observar las conexiones que tenemos disponibles:
  • 4 conexiones (GND - TX - RX - VCC) que constituyen un puerto serie mediante el cual podemos programar el dispositivo.
  • 1 puerto en la misma línea (GPIO14) en el que podríamos conectar cualquier dispositivo controlable por medio de un solo pin: sensores de temperatura, 1wire, etc.
  • El pulsador está conectado entre tierra y otro puerto (GPIO0). Si soldamos unos pequeños cables a estos terminales también podríamos hacer uso de este puerto

SonOff POW

Este otro dispositivo es algo más avanzado, permitiéndonos además de encender y apagar, controlar la potencia y el voltaje de la corriente que circula en todo momento por el dispositivo.
SonOff POW
El empaquetado es bastante más robusto y es algo más grande. Existen además dos versiones: una de 10 Amperios que nos permite encender y apagar y controlar el consumo de aparatos de hasta 2200W (lámparas, estufas, etc) y otra de 16 Amperios que nos permite hacer lo mismo con aparatos de hasta 3500W (aires acondicionados, lavavajillas, lavadoras, etc).
Al igual que su hermano menor, dispone de una línea con agujeritos para soldar en este caso 4 pines que nos permitirán programar el dispositivo. En este modelo es muy peligroso conectar otros periféricos a las líneas de control porque la señal GND va conectada a uno de los terminales de la red (que puede ser el neutro pero también el vivo), con lo cual podríamos freir el sensor que le conectemos. Por tanto en principio no trae ningún GPIO fácilmente accesible.
Las conexiones serían las que se muestran en las siguientes imágenes:
GPIOs SonOff POW
CA SonOff POW

Programación

Para reprogramar un dispositivo SonOff según nuestras necesidades necesitamos los siguientes elementos:
  • Un programador de Arduinos y otros controladores, como el FTDI FT232 o similar. Siempre nos tenemos que asegurar de que el programador funcione a 3.3V y no a 5V, si no queremos freír el controlador del SonOff.
  • Un entorno de programación preparado para ESP8266. A continuación veremos cómo preparar el entorno utilizando el IDE de Arduino, aunque se puede utilizar cualquier otra herramienta.
  • Un software (o firmware) para meterle al SonOff, que nos proporcione las funcionalidades que necesitamos.
A continuación veremos los pasos que necesitamos para configurar cada uno de los elementos:

Paso 1: Conectar el programador

Para conectar el programador, nos aseguraremos de que realizamos las siguientes conexiones entre el mismo y el SonOff:
Conexion programador
Básicamente se trata de establecer un “null modem” entre ambos dispositivos, cruzando las líneas de recepción y transmisión y manteniendo las de alimentación y tierra. El resto de pines del programador se quedarán sin conectar.
De este modo, si conectamos el programador a nuestro ordenador, nos aparecerá un nuevo puerto serie (COMx) al que nos podemos conectar tanto para obtener la información que saque el dispositivo como para programarlo. Para esto último hay que entrar en modo programación como veremos más adelante.

Paso 2: Preparar el entorno

Para programar un dispositivo ESP8266 existe la posibilidad de utilizar el mismo lenguaje de Arduino (y su IDE), como vimos en este artículo, aunque existen otras posibilidades como LUA o MicroPython.
Nosotros vamos a utilizar Arduino como lenguaje de programación, para lo cual lo normal es utilizar su propio IDE (yo particularmente utilizo Eclipse que es mucho más potente, pero eso ya es otro cantar). Para ello tendremos que hacer lo siguiente:
  • Debemos disponer de una versión reciente del IDE (a partir de la 1.6.4). Se descarga en esta dirección. El IDE tal cual lo instalamos conoce muchas placas de hardware, como todos los modelos habidos y por haber de Arduino y otras muchas, pero en principio no conoce ningún dispositivo ESP8266.
  • Para ello, tendremos que instalar el soporte de ESP8266. Para ello, haremos lo siguiente:
    • Arrancamos el IDE.
    • Vamos a la opción preferencias dentro del menú Archivo.
    • En la opción “Gestor de URLs Adicionales de Tarjetas”, especificamos la siguiente dirección: http://arduino.esp8266.com/stable/package_esp8266com_index.json y pulsamos Ok.
      Url ESP8266
    • Vamos al menú Herramientas -> Placa -> Gestor de Tarjetas.
    • Seleccionamos ESP8266 Community y la instalamos.
      Gestor de tarjetas
    • A partir de ese momento, ya tenemos disponible toda la familia de productos basados en ESP8266 en el menú Herramientas -> Placa.
  • Para poder programar correctamente nuestro dispositivo, en el menú Herramientas debemos seleccionar exactamente las siguientes opciones. En caso contrario no nos va a funcionar:
    • Placa: Generic ESP8266 module.
    • Flash Mode: DOUT.
    • Flash Frequency: 40Mhz.
    • CPU Frequency: 80Mhz.
    • Flash Size: 1M (64K SPIFFS).
    • Debug Port: Disabled.
    • Debug Level: Ninguno.
    • Reset Method: CK.
    • Upload Speed: 115200.
    Opciones Esp8266
    En cuanto al puerto, lo podremos seleccionar cuando tengamos conectado el programador.

Paso 3: Elegir el software

A la hora de cambiar el funcionamiento de nuestros dispositivos SonOff, podemos elegir entre una gran cantidad de softwares, o firmwares, o como se les quiera llamar.
Unos tienen más características que otros, otros tienen un servidor web integrado, otros son más fácilmente configurables, etc. Yo voy a poner una lista de los más conocidos y el que yo he elegido concretamente. Todos ellos están programados en el lenguaje de Arduino y se pueden subir desde nuestro IDE ya preparado.
  • Tasmota.
    Este software soporta todos los dispositivos SonOff, incluye un servidor web y permite configurarlo de forma interactiva, se conecta a servidores MQTT y permite conectarles una gran cantidad de sensores, es actualizable por OTA, etc.
  • Espurna.
    Este software es español y tiene prácticamente las mismas características.
  • Homie.
    Homie es una librería que podemos incluir en nuestro Sketch de Arduino y que nos provee de una funcionalidad básica: conexión a Wifi, a MQTT, etc.
  • SonOff-HomeAssistant.
    Este software es muy básico: sólo nos permite encender y apagar los dispositivos y conectarles opcionalmente un sensor de temperatura y humedad. También es configurable mediante OTA, pero no tiene ninguna característica avanzada como configuración online, ni servidor web ni nada de eso.
En mi caso, me he quedado con esta última opción, ya que no necesito que un interruptor haga otra cosa: yo tengo de momento 3 SonOffs Basic funcionando: uno como interruptor simple, otro con un DHT22 que me mide la temperatura y la humedad (soportado directamente por el software) y un tercero con un sensor de temperatura DS18B20 mediante una modificación que he realizado en el código. Para mí es suficiente y es lo que voy a explicar.

Configuración

Para utilizar el software SonOff-HomeAssistant, nos iremos al repositorio y entraremos en el directorio arduino. Dentro de este directorio utilizaremos los siguientes programas:
  • ESPsonoff-v1.01pOTA, si vamos a utilizar un SonOff Basic sin ningún sensor conectado.
  • ESPsonoff-v1.01tOTA, si vamos a utilizar un SonOff Basic con un sensor de temperatura y humedad conectado (DHT11 ó DHT22).
  • ESPsonoff_POW-v1.0, si vamos a utilizar un SonOff POW.
Una vez descargado el software y abierto con el IDE de Arduino, deberemos ajustar los siguientes parámetros del código:
#define DHTTYPE         DHT22
// Aquí indicaremos DHT11 ó DHT22, según el modelo que tengamos

#define MQTT_CLIENT     "Sonoff_Living_Room_v1.01tOTA"
// Este parámetro indica el identificador que tiene este dispositivo de cara al MQTT.
// El identificador debe ser único para cada dispositivo

#define MQTT_SERVER     "192.168.0.100"                      
// Aquí indicamos la dirección IP del broker MQTT

#define MQTT_PORT       1883
// Puerto TCP del broker

#define MQTT_TOPIC      "home/sonoff/living_room/1"
// Tópico que utilizaremos para controlar este dispositivo, y en el que se 
// publicarán el estado del mismo y los valores de los sensores

#define MQTT_USER       "user"
// Usuario del broker MQTT

#define MQTT_PASS       "pass"
// Contraseña del mismo

#define WIFI_SSID       "homewifi"
// Nombre de la WIFI

#define WIFI_PASS "homepass"
// Contraseña de la WIFI

int kRetries = 10;
// Número de intentos a realizar para conectar a la Wifi.
// En mi caso tuve que aumentarlo a 50 porque tardaba en conectar.

Paso 4: Grabar el software

Una vez que tenemos el software descargado y configurado a nuestro gusto, tendremos que grabarlo en el dispositivo. Para ello tendremos que entrar en modo programación:
  1. Con el programador desconectado del puerto USB, dejar pulsado el pulsador del SonOff.
  2. Sin soltar el pulsador, conectar el programador a un puerto USB del ordenador.
  3. Dejar pulsado el pulsador unos 3 ó 4 segundos.
  4. Soltar el pulsador.
Desde este momento el controlador del SonOff, en lugar de ejecutar el software que tiene cargado, se queda a la espera de que le mandemos un nuevo software. Subiremos el software mediante la opción Programa -> Subir.
En caso de que nos dé un error de sincronización, podemos volver a intentarlo. En caso de que siga fallando, podríamos bajar la velocidad de flasheo de 115200 a 57600 o inferior. Si seguimos teniendo problemas, es posible que no hayamos entrado bien en el modo de programación: nos aseguraremos de que todos los parámetros están puestos tal y como se dice en el punto 2, desconectaremos y volveremos a entrar en modo programación.
Una vez que subamos el software, podemos desconectar el programador del ordenador y volver a conectarlo sin pulsar nada. Le daremos unos segundos y podemos comprobar si se conecta a la WIFI y que podamos encender y apagar el LED mediante el pulsador.

Paso 5: Modificaciones

En el caso del SonOff Basic, como ya se ha dicho, podemos conectarle cualquier sensor utilizando el GPIO 14 que tenemos disponible. Por ejemplo, siguiendo este esquema podríamos añadirle un sensor DHT11 o DHT22:
Esquema DHT
Si lo que tenemos es un sensor de temperatura Dallas DS18B20, utilizaríamos un esquema parecido, intercalando una resistencia de 4K7Ω entre la línea de datos y la de alimentación.
En lugar de utilizar la librería DHT.h, utilizaríamos:
  • OneWire.
  • DallasTemperature
Las modificaciones en el código serían muy simples:
Antes del método setup, añadiríamos:
OneWire one(PINDS18B20);
DallasTemperature sensorDallas(&one);
Dentro del método setup:
sensorDallas.begin();
Y finalmente, el método getTemp se quedaría así:
void getTemp() {
    Serial.print("DS18B20 read . . . . . . . . . . . . . . . . . ");
    char message_buff[60];

    // Lanzamos una petición para leer temperaturas...
    sensorDallas.requestTemperatures();

    // ... y obtenemos las del primero (y único, en principio)
    float temp = sensorDallas.getTempCByIndex(0);

    if (digitalRead(LED) == LOW) {
        blinkLED(LED, 100, 1);
    } else {
        blinkLED(LED, 100, 1);
        digitalWrite(LED, HIGH);
    }
    if (isnan(temp)) {
        mqttClient.publish(
                MQTT::Publish(MQTT_TOPIC"/debug", "\"DS18B20 Read Error\"").set_retain().set_qos(
                        1));
        Serial.println("ERROR");
        tempReport = false;
        return;
    }
    String pubString = "{\"Temp\": " + String(temp) + "}";
    pubString.toCharArray(message_buff, pubString.length() + 1);
    mqttClient.publish(
            MQTT::Publish(MQTT_TOPIC"/temp", message_buff).set_retain().set_qos(
                    1));
    Serial.println("OK");
    tempReport = false;
}

Integración en HomeAssistant

Finalmente, una vez que tenemos el software funcionando en nuestro dispositvo, tendremos que definirlo en nuestro HomeAssistant como interruptor o como luz. Para ello añadiremos a nuestro fichero configuration.yaml las siguientes líneas:
# Podríamos utilizarlo como interruptor (switch) o como luz (light)
switch:
  - platform: mqtt
    name: Interruptor 1
    state_topic: "micasa/sonoff/cocina/1/stat"
    command_topic: "micasa/sonoff/cocina/1"
    qos: 0
    payload_on: "on"
    payload_off: "off"
    retain: true
Además, en el caso de que le hayamos conectado un sensor de temperatura, habría que definir los sensores:
sensor:
- platform: mqtt
  name: "Temperatura cocina"
  state_topic: "micasa/sonoff/cocina/1/temp"
  unit_of_measurement: "°C"
  value_template: '{{ value_json.Temp | default(0) }}'

- platform: mqtt
  name: "Humedad cocina"
  state_topic: "micasa/sonoff/cocina/1/temp"
  unit_of_measurement: "%"
  value_template: '{{ value_json.Humidity | default(0) }}'

Montaje físico

Como ejemplo, en la siguiente imagen se muestra cómo integrar un SonOff Basic modificado con un sensor de temperatura dentro de un ventilador:
Montaje ventilador

domingo, 6 de agosto de 2017

Sensor de consumo eléctrico para HomeAssistant (y 3)

En este artículo pretendo dar por zanjado el tema del sensor de consumo eléctrico doméstico. Venimos de esta entrada.
En la entrada anterior planteamos el circuito a utilizar para nuestro sensor e hicimos una serie de pruebas en nuestro protoboard.
Esta vez vamos a ver el montaje definitivo y cómo mandar los datos a HomeAssistant.

Montaje definitivo

En la imagen podemos ver el montaje definitivo que he hecho en una placa:
Montaje definitivo
No sé si se ven bien los detalles, pero básicamente he montado el D1 mini en un zócalo para poder extraerlo fácilmente para reprogramarlo y le he conectado a la entrada de 5V una batería recargable de litio 18650 de 3.7V, con lo cual el regulador interno nos da los 3.3V estabilizados para el divisor de tensión.
He de decir que este es el segundo D1 mini que uso: el primero me lo cargué midiendo el consumo del dispositivo. Se ve que no le sentaron nada bien los microcortes de tensión que le metí cuando estaba probando a poner y quitar el amperímetro. Ahora le he puesto al circuito unos pines que me hacen de interruptor al estar conectados mediante un puente. No quiero cargarme más componentes con un pico al conectar y desconectar la batería (que no es nada fácil).
Aparte de esto, nada más que decir en cuanto al montaje físico.

Software

En cuanto al programa en sí para el lector de consumo, aparte de utilizar la librería EmonLib para lo que son las lecturas de corriente, necesitamos algo más para enviar los datos una vez que los tenemos.
Lo más lógico dado que vamos a utilizar HomeAssistant como centralizador es utilizar un cliente de MQTT.

MQTT

MQTT son las siglas de Message Queue Telemetry Transport, un protocolo ligero utilizado en dispositivos del Internet de las cosas que permite a múltples intermediarios intercambiar mensajes de la misma manera que funcionaría un chat: cada intermediario accede a lo que se denomina un tópico, que es el tema de la conversación, y cada uno puede publicar mensajes sobre ese tópico. Otros clientes pueden suscribirse a ese tópico y recibir una notificación cuando haya un mensaje nuevo.
Nos conectaremos a un servidor (o broker) de MQTT y publicaremos periódicamente en un tópico concreto los valores leídos de corriente y potencia efectivas. Para ello utilizaremos la librería PubSubClient.

El programa

A continuación, el código fuente del programa utilizado para el sensor de consumo:
/**
 * Lector de consumo energético con conexión Wifi a servidor MQTT
 * Isidoro Aguilar Baeza - Julio/Agosto 2017
 * Este script se ejecuta en dispositivos NodeMCU, no en Arduino
 *
 * Librerías utilizadas:
 *   - PubSubClient(MQTT): https://github.com/knolleary/pubsubclient
 *   - EmonLib: https://github.com/openenergymonitor/EmonLib
 */

#include "Arduino.h"
#include <ESP8266WiFi.h>
#include "lib/PubSubClient.h"
#include "lib/EmonLib.h"

// Configuración de pines
#define PIN_LED            2

// Configuración de WIFI
#define WIFI_SSID    "XXXXXX"               // Nombre del AP Wifi
#define WIFI_PWD    "XXXXXX"            // Contraseña para conectar
#define WIFI_BPS    9600                // Velocidad del interfaz WIFI

// Configuración de MQTT
#define MQTT_SERVIDOR    "X.X.X.X"        // Dirección IP del broker
#define MQTT_PUERTO        1883
#define MQTT_USUARIO    "XXXXXXXXXXXXX"    // Usuario de acceso
#define MQTT_PWD        "XXXXXXXXXXXXX"    // Contraseña
#define MQTT_CLIENTE    "sensorConsumo"    // Identificador de este cliente
#define MQTT_TOPICO        "micasa/potencia"    // Tópico en el que publicaremos

// Configuración eléctrica
#define RatioTC            2000    // Ratio del transformador
#define ResCarga        91        // Resistencia de carga
#define correccion        1.145    // Factor de corrección adicional. Calculado empíricamente
#define VoltajeLinea    220        // Voltaje típico de la línea
#define T_PAUSA            10        // Intervalo entre medidas, en segundos

// Inicializamos el cliente Wifi y el MQTT
WiFiClient clienteESP;
PubSubClient clienteMQTT(clienteESP);

// Declaramos un lector de consumo de la librería EmonLib
EnergyMonitor emon1;

// Declaramos la cadena que se enviará al servidor MQTT
char payloadMQTT[30];

/**
 * Conecta con el servidor de MQTT (Mosquitto)
 */
void conectarMQTT() {
    if (!clienteMQTT.connected()) {
        Serial.print("Conectando con el servidor MQTT en ");
        Serial.print(MQTT_SERVIDOR);

        boolean conectado = false;
        while (!conectado) {
            Serial.print(".");
            if (clienteMQTT.connect(MQTT_SERVIDOR, MQTT_USUARIO, MQTT_PWD)) {
                conectado = true;
                Serial.println();
                Serial.println("Conectado");
                //clienteMQTT.subscribe(MQTT_TOPICO);
            } else {
                delay(1000);
            }
        }
    }
}

/**
 * Configuración.
 * Se ejecuta sólo una vez, al iniciar el Arduino
 */
void setup() {
    // 1.- Inicializamos el puerto serie USB
    Serial.begin(9600);
    delay(100);
    Serial.println("");
    Serial.println("");
    Serial.println("Lector de consumo eléctrico");
    Serial.println();
    Serial.print("Conectando con ");
    Serial.print(WIFI_SSID);

    // 2.- Inicializamos el módulo WIFI y nos conectamos al AP
    WiFi.persistent(false);
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PWD);

    // Establecemos un bucle de espera hasta que esté conectado...
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println();
    Serial.println("Conectado.");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());

    // 3.- Nos conectamos al servidor MQTT
    clienteMQTT.setServer(MQTT_SERVIDOR, MQTT_PUERTO);

    // 4.- Inicializamos el LED
    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);

    // 5.- Indicamos dónde está conectado el sensor de corriente (pin analógico A0) y el factor de calibración.
    emon1.current(A0, RatioTC / ResCarga * correccion);

    Serial.print("Voltaje de alimentación: ");
    Serial.print((double) (emon1.readVcc() / 1000));
    Serial.println("V");

    // Hacemos una primera lectura que desechamos. La primera nunca es fiable
    emon1.calcIrms(14800);
}

/**
 * Bucle de ejecución
 * Se ejecuta una y otra vez hasta que se apague el NodeMCU
 */
void loop() {
    // 1.- Encendemos el LED antes de leer (extrañamente parece que los valores HIGH y LOW están invertidos en el esp8266)...
    digitalWrite(PIN_LED, LOW);

    if (!clienteMQTT.connected())
        conectarMQTT();

    clienteMQTT.loop();

    // 2.- Leemos la corriente efectiva y calculamos la potencia.
    double corriente = emon1.calcIrms(14800);
    double potencia = corriente * VoltajeLinea;

    Serial.print("Corriente: ");
    Serial.print(corriente);
    Serial.println(" A");

    Serial.print("Potencia: ");
    Serial.print(potencia);
    Serial.println(" W");

    // 3.- Lo mandamos al servidor de MQTT
    sprintf(payloadMQTT, "{\"corriente\" : %d.%03d,\"potencia\" : %d.%03d}",
            (int) corriente, (int) (corriente * 1000) % 1000, (int) potencia,
            (int) (potencia * 1000) % 1000);
    Serial.print("Payload: ");
    Serial.println(payloadMQTT);
    clienteMQTT.publish(MQTT_TOPICO, payloadMQTT);
    clienteMQTT.disconnect();

    // 4.- Volvemos a apagar el led hasta la próxima
    digitalWrite(PIN_LED, HIGH);

    // 5.- Esperamos un ratito
    delay(T_PAUSA * 1000);
}
Varias cosas que destacar en este código:
  1. Los ejemplos de EmonLib para Arduino siempre utilizan un muestreo de 1480. Como yo estoy utilizando un ESP8266 que va a 80 Mhz (en lugar de los 8Mhz del Arduino Pro Mini), he multiplicado por 10 el tamaño de la muestra. Tarda más en obtener la corriente pero es bastante más estable que en el ejemplo que vimos en la entrada anterior.
  2. Además, en mis experimentos he observado que cuando se comienzan a hacer lecturas mediante llamadas al método calcIrms, la primera vez da un valor extrañamente alto, aún sin carga. Por tanto, he hecho una primera llamada en la función setup que simplemente descarto.
  3. Para el parámetro de calibración, además del ratio del transformador y el valor de la resistencia de carga, he utilizado un factor de corrección basándome en la comparación de las lecturas obtenidas al medir el consumo de una lámpara de 300W. Lo he corregido para que las lecturas se acerquen en lo posible a dicho valor, aunque quizá no sea buena idea. Lo ideal es medir con una pinza amperimétrica profesional y comparar con nuestras lecturas, aplicando entonces la corrección necesaria.
  4. Otro parámetro a tener en cuenta es el voltaje de la línea. Debemos utilizar un valor medio, aún cuando sabemos que el voltaje no es algo estable en una instalación. En mi casa oscila entre los 208V y los 234V, con lo cual he elegido 220V como el valor medio. Lo ideal sería leer el voltaje instantáneo, pero para eso necesitaríamos otro tipo de sensor.
  5. La conexión con la Wifi y con el servidor de MQTT entrarán en un bucle infinito en caso de no poder conectarse, con lo cual quizá hubiera que optimizar algo aquí.

Optimizaciones

Este es el código estándar que se utilizaría en un Arduino: una función setup que se encarga de las inicializaciones, y otra función loop que se repite de forma infinita hasta que el dispositivo se apaga. Las lecturas en este caso se hacen en dicha función y se espacian unos 10 segundos (más el tiempo de lectura). En esta configuración, el código funciona bastante bien, pero el consumo es algo alto si vamos a funcionar con una batería.
Siendo optimista, una batería 18650 de las que he comprado (en Amazon) podría tener unos 2700 mAh de capacidad, aunque estén marcados como de 3000.
He medido el consumo del circuito (aquí es donde me cargué el primer D1 mini) y, mientras está muestreando, el consumo es de unos 70-74 mA. Cuando el programa pasa a la espera, el consumo baja a unos 18-20 mA. Si hacemos la media, nos sale un consumo medio de unos 38 mA. Esto quiere decir que una batería nos duraría:
2700 mAh / 38 mA = 71 h = 2,9 días
Esto está muy bien para una prueba, pero no me veo cambiando la batería del lector cada 3 días. Así que habrá que hacer algo: algo que se llama deep sleep (sueño profundo).
El ESP8266 tiene este modo de funcionamiento que es prácticamente “catatónico“: toda la electrónica se apaga excepto un pequeño temporizador establecido por el programa. El truco consiste en que, al iniciar el temporizador, se le indica al ESP que active un puerto concreto, normalmente el D0. Si el D0 lo conectamos al pin de RESET, el resultado es que al terminar el temporizador, el ESP se reinicia y ejecuta de nuevo la función setup.
Por tanto, habría que modificar el programa para que toda la lógica transcurra en el setup y no en el loop. Una vez que tengamos una lectura válida y la hayamos enviado al servidor MQTT, nos echaremos a dormir un ratito en modo deep sleep.
En este modo el consumo baja drásticamente. En teoría, en un dispositivo puro sin electrónica añadida debería bajar al orden de microAmperios en vez de miliAmperios. Lógicamente, la unidad que estamos usando (D1 mini) tiene electrónica para el puerto USB, para el regulador de tensión, etc. Aún así, modifiquemos el código y veamos qué tal el consumo:
/**
 * Lector de consumo energético con conexión Wifi a servidor MQTT
 * Isidoro Aguilar Baeza - Julio/Agosto 2017
 * Este script se ejecuta en dispositivos NodeMCU, no en Arduino
 *
 * Librerías utilizadas:
 *   - PubSubClient(MQTT): https://github.com/knolleary/pubsubclient
 *   - EmonLib: https://github.com/openenergymonitor/EmonLib
 */

#include "Arduino.h"
#include <ESP8266WiFi.h>
#include "lib/PubSubClient.h"
#include "lib/EmonLib.h"

// Configuración de pines
#define PIN_LED            2

// Configuración de WIFI
#define WIFI_SSID    "XXXXXX"               // Nombre del AP Wifi
#define WIFI_PWD    "XXXXXX"            // Contraseña para conectar
#define WIFI_BPS    9600                // Velocidad del interfaz WIFI

// Configuración de MQTT
#define MQTT_SERVIDOR    "X.X.X.X"        // Dirección IP del broker
#define MQTT_PUERTO        1883
#define MQTT_USUARIO    "XXXXXXXXXXXXX"    // Usuario de acceso
#define MQTT_PWD        "XXXXXXXXXXXXX"    // Contraseña
#define MQTT_CLIENTE    "sensorConsumo"    // Identificador de este cliente
#define MQTT_TOPICO        "micasa/potencia"    // Tópico en el que publicaremos

// Configuración eléctrica
#define RatioTC            2000    // Ratio del transformador
#define ResCarga        91        // Resistencia de carga
#define correccion        1.145    // Factor de corrección adicional. Calculado empíricamente
#define VoltajeLinea    220        // Voltaje típico de la línea
#define T_PAUSA            55        // Intervalo entre medidas, en segundos

// Inicializamos el cliente Wifi y el MQTT
WiFiClient clienteESP;
PubSubClient clienteMQTT(clienteESP);

// Declaramos un lector de consumo de la librería EmonLib
EnergyMonitor emon1;

// Declaramos la cadena que se enviará al servidor MQTT
char payloadMQTT[30];

/**
 * Conecta con el servidor de MQTT (Mosquitto)
 */
void conectarMQTT() {
    if (!clienteMQTT.connected()) {
        Serial.print("Conectando con el servidor MQTT en ");
        Serial.print(MQTT_SERVIDOR);

        boolean conectado = false;
        while (!conectado) {
            Serial.print(".");
            if (clienteMQTT.connect(MQTT_SERVIDOR, MQTT_USUARIO, MQTT_PWD)) {
                conectado = true;
                Serial.println();
                Serial.println("Conectado");
                //clienteMQTT.subscribe(MQTT_TOPICO);
            } else {
                delay(1000);
            }
        }
    }
}

/**
 * Configuración.
 * Se ejecuta sólo una vez, al iniciar el Arduino
 */
void setup() {
    // 1.- Inicializamos el puerto serie USB
    Serial.begin(9600);
    delay(100);
    Serial.println("");
    Serial.println("");
    Serial.println("Lector de consumo eléctrico");
    Serial.println();
    Serial.print("Conectando con ");
    Serial.print(WIFI_SSID);

    // 2.- Inicializamos el módulo WIFI y nos conectamos al AP
    WiFi.persistent(false);
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PWD);

    // Establecemos un bucle de espera hasta que esté conectado...
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println();
    Serial.println("Conectado.");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());

    // 3.- Nos conectamos al servidor MQTT
    clienteMQTT.setServer(MQTT_SERVIDOR, MQTT_PUERTO);

    // 4.- Inicializamos el LED
    pinMode(PIN_LED, OUTPUT);
    digitalWrite(PIN_LED, LOW);

    // 5.- Indicamos dónde está conectado el sensor de corriente (pin analógico A0) y el factor de calibración.
    emon1.current(A0, RatioTC / ResCarga * correccion);

    Serial.print("Voltaje de alimentación: ");
    Serial.print((double) (emon1.readVcc() / 1000));
    Serial.println("V");

    // Hacemos una primera lectura que desechamos. La primera nunca es fiable
    emon1.calcIrms(14800);

    // 6.- Encendemos el LED antes de leer (extrañamente parece que los valores HIGH y LOW están invertidos en el esp8266)...
    digitalWrite(PIN_LED, LOW);

    if (!clienteMQTT.connected())
        conectarMQTT();

    clienteMQTT.loop();

    // 7.- Leemos la corriente efectiva y calculamos la potencia.
    double corriente = emon1.calcIrms(14800);
    double potencia = corriente * VoltajeLinea;

    Serial.print("Corriente: ");
    Serial.print(corriente);
    Serial.println(" A");

    Serial.print("Potencia: ");
    Serial.print(potencia);
    Serial.println(" W");

    // 8.- Lo mandamos al servidor de MQTT
    sprintf(payloadMQTT, "{\"corriente\" : %d.%03d,\"potencia\" : %d.%03d}",
            (int) corriente, (int) (corriente * 1000) % 1000, (int) potencia,
            (int) (potencia * 1000) % 1000);
    Serial.print("Payload: ");
    Serial.println(payloadMQTT);
    clienteMQTT.publish(MQTT_TOPICO, payloadMQTT);
    clienteMQTT.disconnect();

    // 9.- Volvemos a apagar el led hasta la próxima
    digitalWrite(PIN_LED, HIGH);

    // 10.- Finalmente, nos vamos a dormir
    pinMode(D0, WAKEUP_PULLUP);        // D0 conectada a RST
    ESP.deepSleep(T_PAUSA * 1000000);    // Establecemos el temporizador
    delay(100);        // Normalmente no se ejecutará, pero a veces es necesario.
}

/**
 * Bucle de ejecución
 * Se ejecuta una y otra vez hasta que se apague el NodeMCU
 */
void loop() {
}
Una vez modificado el código, las lecturas de consumo son las siguientes:
  • Unos 75 mA durante la lectura (5s).
  • 0.46 mA en deep Sleep (55s).
Haciendo la media:
((5 x 75 mA) + (55 x 0,46 mA)) / 60 = 6,67 mA
Luego, la duración de la batería ahora sería de:
2700 mAh / 6,67 mA = 404,80 h = 16.86 días
No está nada mal, la verdad. Pero a lo mejor no necesitamos tener lecturas cada minuto. Veamos qué pasa si las hacemos cada 3 minutos:
((5 x 75 mA) + (175 x 0,46 mA)) / 180 = 2,53 mA
2700 mAh / 2,53 mA = 1067,19 h = 44,46 días
Pues decidido: vamos a hacer las lecturas con un intervalo de unos 3 minutos, con lo cual la batería nos debería durar sobre mes y medio. No pasa nada porque es recargable, así nos entretenemos.
Por tanto, sólo habría que cambiar el parámetro T_PAUSA por el valor 175 y volver a subir el programa al dispositivo, conectarlo todo y probar.

Montaje físico

El montaje físico habría que realizarlo lo más cerca posible de la toma general de la casa, colocando el sensor CT013 alrededor de un sólo cable: o el vivo o el neutro (nunca la tierra). Un buen lugar para coloclarlo sería en la caja para el ICP, aunque ello nos obligaría a abrirla cada vez que queramos cambiar la batería.
En mi caso, lo he colocado de esta manera en una caja de conexiones que tenía cerca del cuadro general:
Montaje en pared

Conexión con HomeAssistant

Bueno, pues ya tenemos nuestro dispositivo leyendo la corriente y la potencia de nuestra casa y enviando los datos cada 3 minutos al servidor MQTT.
Ahora lo que nos queda es configurar un sensor en HomeAssistant para poder leer y mostrar los valores.
La configuración es muy fácil. En nuestro fichero configuration.yaml deberíamos activar el protocolo mqtt y deberíamos definir (en el mismo configuration.yaml o en sensors.yaml) un sensor para el tópico en el que estamos publicando los datos.
La configuración debería quedar parecida a esta:
mqtt:
  discovery: true
  broker: X.X.X.X
  port: 1883

sensor:
  - platform: mqtt
    name: Consumo Global
    state_topic: "micasa/potencia"
    unit_of_measurement: "kW"
    value_template: '{{ "%0.2f" | format((value_json.potencia | float) / 1000 ) }}'
Yo lo he configurado para que muestre la potencia en kilovatios, redondeándolo a 2 decimales. Si alguien quiere mostrar el valor en vatios, tal como viene del lector, tan sólo bastaría con cambiar la última línea por:
    value_template: '{{ value_json.potencia  }}'
Finalmente, el aspecto que tiene el sensor cuando lo vemos en HomeAssistant es el siguiente:
HA Consumo

Conclusión

A lo largo de estas tres entradas hemos construido un sensor de potencia efectiva que nos permite consultar en HomeAssistant la potencia aproximada que se está consumiendo actualmente en toda la casa. Además del circuito eléctrico, hemos creado un pequeño programa que se ejecuta en un dispositivo inalámbrico basado en ESP8266 cada tres minutos y el resto del tiempo permanece dormido. Hemos conectado los datos del dispositivo con un servidor de mensajería MQTT y hemos conseguido visualizar los datos en HomeAssistant.
Para el montaje, hemos utilizado los siguientes componentes:
  • 1 transformador de corriente SCT013-000. Comprado en Aliexpress por unos 5€.
  • 1 dispositivo inalámbrico D1 mini. Comprado en Aliexpress por unos 2,50€.
  • 1 condensador de 10 uF y 25V. Comprado en la tienda de electróncia de la esquina.
  • 2 resistencias de 1/4W y 4,7KΩ.
  • 2 resistencias de 1/4W y 22Ω.
  • 1 resistencia de 1/4W y 47Ω.
  • Placa de montaje y zócalos.
Entre el condensador y las resistencias no llegan al euro. Lo que sí es importante es comprar resistencias buenas, con una precisión alta. Para el primer montaje que hice utilicé unas resistencias que compré en Aliexpress baratísimas pero que resultaron ser desastrosas. Los voltajes del divisor de tensión no cuadraban y los valores del lector no tenían sentido.