Controlando las puertas de un garaje con un ESP32 - Parte 3

  • October 6, 2018
  • tuxotron
  • ESP32 - Relay - Sensor wiring

    *ESP32 - Relay - Sensor wiring*

    Ahora que ya tenemos nuestro ESP32 listo para el combate, es hora de instalarlo. Lo primero que debemos hacer es usar un servidor MQTT con algún tipo de autentificación. Hasta ahora todas las pruebas que hemos hecho han sido usando el servidor de pruebas de io.eclipse.org sin ningún tipo de autentificación. Esto significa, que si alguien averigua los canales que usas, podría abrir y cerrar las puertas de tu garaje. No bueno!

    Hay muchas opciones disponibles, pero personalmente, para este tipo de proyectos, prefiero manejar mi propio servidor. Así que voy a explicar como lo tengo montado. Uso Moquitto broker, un proyecto de la fundación Eclipse y muy popular en proyectos IoT. Por supuesto lo uso dentro de un contenedor Docker ;)

    En vez de usar la imagen oficial de Docker de Mosquitto, me he construído la mía propia, simplemente porque la original, a la hora de crear usuarios, etc tienes que crearte los ficheros de configuración y copiarlos dentro de la imagen, no una buena idea si publicas la imagen en algún registro público. En la imagen que creé te permite pasarle como parámetros (en forma de variables de entorno) el nombre de un usuario y la contraseña. Este sería el único usuario con acceso al servidor, de forma que tu servidor sólo admite conexiones para el mismo.

    Todo lo que tienes que hacer para arrancar tu contenedor con Mosquitto es:

    docker container run -d --name mosquitto -p 1883 --restart=always -e MOSQUITTO_USER=YOUR_USERNAME -e MOSQUITTO_PASS=YOUR_PASS tuxotron/mosquitto
    

    Asegúrate de que el puerto 1883/tcp del sistema donde lo corras es accesible desde afuera. Por supuesto cambia los valores YOUR_USERNAME y YOUR_PASS por el nombre del usuario y la contraseña que decidas. Los accesos anónimos serán denegados.

    El siguiente paso sería reconfigurar el código del micro controlador, para asignarle los nuevos valores: servidor, usuario y contraseña, como ya se explicó en la primera parte de esta serie. Asegúrate de actualizar el código fuente. Desde el directorio donde tienes clonado el proyecto:

    git pull origin master
    

    Después de reconfigurar y volcar el nuevo código en el chip, deberías ver en los logs algo así:

    I (2545) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
    I (3195) GARAGE: MQTT_EVENT_CONNECTED
    I (3195) GARAGE: sent subscribe successful, msg_id=63593
    I (3195) GARAGE: sent subscribe successful, msg_id=48468
    I (3205) GARAGE: sent subscribe successful, msg_id=29761
    I (3205) GARAGE: sent subscribe successful, msg_id=63161
    I (3215) GARAGE: MQTT_EVENT_SUBSCRIBED, msg_id=63593
    I (3225) GARAGE: MQTT_EVENT_SUBSCRIBED, msg_id=48468
    I (3435) GARAGE: MQTT_EVENT_SUBSCRIBED, msg_id=29761
    

    Esto nos indica que estamos conectado al servidor. En este punto ya podemos instalar nuestro dispositivo en el garaje. En mi caso, el cableado entre el relé y los abridores de las puertas es el siguiente:

    Relay - Opener connections

    *Relay - Opener connections*

    Si tu abridor/es tiene distintas conexiones, busca los dos hilos que se conectan del botón que usas para abrir y cerrar.

    Lo siguiente sería montar los commutadores magnéticos. En este caso tendrás que encontrar el mejor sitio para instalarlos. Yo he montado este tinglado en tres casas distintas y cada vez los monté en sitios distintos, por la diferencia entre las puertas. A continuación puedes ver una foto del montaje actual:

    Magnetic switch

    *Magnetic switch*

    Una vez tienes el cableado del relé con los abridores y los sensores de las puertas, ya lo tienes listo. Aquí tienes un vídeo de demostración de mi sistema funcionando:

    A partir de aquí la forma en que quieras interactuar con el ESP32 depende de ti. Todo lo que tienes que hacer es poder mandar mensajes a los canales del servidor MQTT. En los vídeos anteriores mostré el uso de mosquitto_pub, en este vídeo usé MQTTLens. Mi objectivo es tener una aplicación nativa en el móvil para manejar las puertas. Tengo media terminada una pequeña aplicación en iOS, que publicaré cuando la tenga funcionando por completo. Cuando la termine publicaré el código fuente y actualice esta entrada con el enlace al mismo.

    Lo próximo que tengo en mi lista para hacer en relación a este proyecto, es activar TLS y communicarme de forma segura con el servidor, pero actualmente tengo otras prioridades, así que lo dejaré para más tarde. Lo haga cuando lo haga, publicaré una entrada al respecto. Mientras tanto, happy hacking!

    Partes previas:

    Controlando las puertas de un garaje con un ESP32 - Parte 1

    Controlando las puertas de un garaje con un ESP32 - Parte 2

Controlando las puertas de un garaje con un ESP32 - Parte 2

  • September 21, 2018
  • tuxotron
  • ESP32 - Relay - Sensor wiring

    *ESP32 - Relay - Sensor wiring*

    En esta segunda entrada de esta serie, veremos como conectar un par de interruptores magnéticos, uno para cada puerta, los cuales nos permitirán saber si las puertas están cerradas o no.

    En este caso, estoy usando este interruptor: https://www.adafruit.com/product/375.

    El interruptor tiene dos cables, uno va a tierra (GND) y el otro a uno de los GPIO de nuestro micro controlador. En este caso uso los GPIOs 25 y 26 para las puertas izquierda y derecha respectivamente.

    Con esta nueva adición, nuestro esquema sería algo así:

    ESP32 - Relay - Sensor schematics

    *ESP32 - Relay - Sensor schematics*

    He actualizado el repositorio con el código necesario para manejar estos nuevos sensores. Todos los cambios se han hecho en el fichero main/app_main.c. A continuación veremos las partes del código relevantes a esta entrada:

    static const char *LEFT_DOOR_STATUS_TOPIC = "garage/door/left/status";
    static const char *RIGHT_DOOR_STATUS_TOPIC = "garage/door/right/status";
    ...
    #define LEFT_DOOR_SENSOR_GPIO 25
    #define RIGHT_DOOR_SENSOR_GPIO 26
    

    Aquí podemos ver cuatro constantes nuevas, dos para definir los canales por los que se recibirá y enviará la información relevante a los interruptores, y otras dos para definir los GPIO a los que estos se conectarán.

    En la parte de inicialización:

    gpio_set_direction(LEFT_DOOR_SENSOR_GPIO, GPIO_MODE_INPUT);
    gpio_set_direction(RIGHT_DOOR_SENSOR_GPIO, GPIO_MODE_INPUT);
    gpio_pullup_en(LEFT_DOOR_SENSOR_GPIO);
    gpio_pullup_en(RIGHT_DOOR_SENSOR_GPIO);
    

    Establecemos el modo de los GPIOs como entrada (INPUT), ya que vamos a leer el estado de estos, y los ponemos en modo alto (pullup)

    case MQTT_EVENT_CONNECTED:
        ...
        msg_id = esp_mqtt_client_subscribe(client, LEFT_DOOR_STATUS_TOPIC, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
    
        msg_id = esp_mqtt_client_subscribe(client, RIGHT_DOOR_STATUS_TOPIC, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
    

    En esta parte se muestra que, cuando nos conectamos al servidor MQTT, nos suscribimos a los dos canales (topics) que acabamos de definir, para así recibir la información que se envíen por ellos.

    Y por último:

    if (strcmp(topic, LEFT_DOOR_TOPIC) == 0) {
      ...
    } else if (strcmp(topic, RIGHT_DOOR_TOPIC) == 0) {
      ...
    } else if (strcmp(topic, LEFT_DOOR_STATUS_TOPIC) == 0) {
        if (strcmp(data, "get") == 0) {
            if (gpio_get_level(LEFT_DOOR_SENSOR_GPIO)) {
                esp_mqtt_client_publish(client, LEFT_DOOR_STATUS_TOPIC, "status:open", 0, 0, 0);
            } else {
                esp_mqtt_client_publish(client, LEFT_DOOR_STATUS_TOPIC, "status:closed", 0, 0, 0);
            }
        }
    } else if (strcmp(topic, RIGHT_DOOR_STATUS_TOPIC) == 0) {
        if (strcmp(data, "get") == 0) {
            if (gpio_get_level(RIGHT_DOOR_SENSOR_GPIO)) {
                esp_mqtt_client_publish(client, RIGHT_DOOR_STATUS_TOPIC, "status:open", 0, 0, 0);
            } else {
                esp_mqtt_client_publish(client, RIGHT_DOOR_STATUS_TOPIC, "status:closed", 0, 0, 0);
            }
        }
    }
    

    Aquí es donde decidimos que hacer cuando nos llega un mensaje. Vemos que sólo respondemos cuando recibimos el mensage get, en cuyo caso, leemos el estado del sensor (izquierdo o derecho, dependiendo del canal por el que nos llegue el mensaje) con la función gpio_get_level(….), y si el resultado es true (distinto de 0), devolvemos el mensaje status:open, o en caso contrario devolvemos status:closed.

    Aquí os dejo un pequeño vídeo mostrando la funcionalidad descrita en esta entrada, usando un sólo sensor:

    Si te perdiste la primera entrada de este seria, la puedes ver en Parte 1

    This post is also available in English

Controlando las puertas de un garaje con un ESP32 - Parte 1

  • September 20, 2018
  • tuxotron
  • Garage

    *Garage*

    Hace cuatro años que cableé un Raspberry Pi con un relé de dos canales y un par de sensores, y lo conecté a las puertas de mi garaje. Aquí tenéis la entrada donde expliqué entonces este proyecto: https://www.cyberhades.com/2014/02/04/controlando-las-puertas-del-garaje-con-un-raspberry-pi/.

    Este montaje con el tiempo sufrió algunos cambios, como: el uso de un servidor MQTT (mosquitto), una aplicación Android nativa, e incluso una aplicación para mi viejo relog Pebble. Nunca escribí sobre ello, pero aquí podéis ver un vídeo dónde uso el reloj para abrir la puerta del garaje (como podéis ver la casa es también distinta, he sufrido varias mudanzas desde entonces):

    Este montaje funcionó bastante bien a lo largo de estos cuatro años, aunque de vez en cuando perdía la conexión de red, e incluso tuve que reemplazar la tarjeta de memoria (SD Card) un par de veces. Parece que estas no duran mucho.

    Por ello, me decidí a re-hacer el proyecto de nuevo, pero esta vez usando un micro controlador ESP32. Para este tipo de proyectos, es mucho mejor porque no necesitas mucha capacidad de proceso (para este proyecto el ESP32 también va sobrado), el tiempo de arranque del micro controlador es muchísimo más rápido que el de un RPi, ya que no tienes sistema operativo, en este caso vuelcas el código fuente en el chip, no tarjetas de memoria, no corrupción del sistema de fichero, etc. En este caso estoy usando esta placa.

    En esta entrada vamos ver como conectar el relé y como interactuar con él. El cableado es muy sencillo y prácticamente igual que al de la entrada original. Aquí tenéis una imagen del mismo:

    ESP32-relay-wiring

    *ESP32-relay wiring*

    Como podéis ver sólo se necesitan cuatro cables, alimentación (VCC), tierra (GND) y un cable para cada canal. En mi caso estoy usando los GPIO 32 y 33 para los canales izquierdo y derecho respectivamente.

    Para programar el controlador usé: ESP-IDF (Espressif IoT Development Framework).

    La interacción con nuestro ESP32 la haremos a través de un servidor MQTT (Mosquitto), pero cualquier otro valdría. El código fuente del proyecto está alojado en este repositorio, y por último también necesitas este cliente MQTT para nuestro micro controlador.

    Asumiendo que ya tienes instalado el ESP-DIF y el componente esp-mqtt, lo siguiente sería descargarte el código fuente del proyecto:

    git clone https://github.com/tuxotron/garage-esp32
    

    Una vez hecho esto, tenemos que configurar el proyecto con el siguiente comando (ejecutando desde dentro del directorio del proyecto que acabos de clonar):

    make menuconfig
    

    Y deberías ver algo así:

    menuconfig

    *make menuconfig*

    Una vez aquí lo primero sería configurar la comunicación serie (esta placa de desarrollo acepta hasta 921600 baudios, lo cual recomiendo, porque así ahorras tiempo durante el volcado del código):

    Serial

    *Serial configuration*

    Lo siguiete sería configurar algunos parámetros específicos del proyecto. Para ello selecionaremos la opción Garage Door Opener. Aquí debes introducir tu SSID y clave, servidor MQTT, usuario y contraseña (para las pruebas estoy usando mqtt://iot.eclipse.org, el cual no requiere autentificación, por lo que puedes dejar el usuario y la contraseña en blanco). Asegúrate de salvar los cambios.

    Garage Door Opener menu option

    *Garage Door Opener menu option*

    Una vez hecho esto y antes de volcar nuestro programa en el chip, vamos a ver algunas partes del código del fichero main/app_main.c

    static const char *LEFT_DOOR_TOPIC = "/garage/door/left";
    static const char *RIGHT_DOOR_TOPIC = "/garage/door/right";
    
    #define LEFT_DOOR_GPIO 32
    #define RIGHT_DOOR_GPIO 33
    

    Aquí vemos algunas de las constantes definidas. Nos suscribiremos a los canales (topics): /garage/door/left y /garage/door/right, y los pins que usaramos para interactuar con el relé con el 32 y 33.

    Durante el proceso de inicialización, además de la conexión a nuestra WIFI, etc, configuramos los pins (GPIOs). Ambos los ponemos como salida (output) y alto (high).

    void app_main()
    {
        ...
    
        gpio_pad_select_gpio(LEFT_DOOR_GPIO);
        gpio_pad_select_gpio(RIGHT_DOOR_GPIO);
        /* Set the GPIO as a push/pull output */
        gpio_set_direction(LEFT_DOOR_GPIO, GPIO_MODE_OUTPUT);
        gpio_set_direction(RIGHT_DOOR_GPIO, GPIO_MODE_OUTPUT);
        /* Set these PINs high */
        gpio_set_level(LEFT_DOOR_GPIO, 1);
        gpio_set_level(RIGHT_DOOR_GPIO, 1);
    
        ...
    }
    

    Por último vemos parte del código que maneja los mensajes que nos llegan:

    static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
    {
        esp_mqtt_client_handle_t client = event->client;
        int msg_id;
        
        switch (event->event_id) {
            ...
            case MQTT_EVENT_DATA:
                ...
                // Get the topic name
                char *topic = malloc(event->topic_len);
                memcpy(topic, event->topic, event->topic_len);
                topic[event->topic_len] = '\0';
    
                // Get the data
                char *data = malloc(event->data_len);
                memcpy(data, event->data, event->data_len);
                data[event->data_len] = '\0';
    
                if (strcmp(topic, LEFT_DOOR_TOPIC) == 0) {
                    if (strcmp(data, "PUSH") == 0) {
                        gpio_set_level(LEFT_DOOR_GPIO, 0);
                        vTaskDelay(500 / portTICK_PERIOD_MS);
                        gpio_set_level(LEFT_DOOR_GPIO, 1);
                    }
    
                } else if (strcmp(topic, RIGHT_DOOR_TOPIC) == 0) {
                    if (strcmp(data, "PUSH") == 0) {
                        gpio_set_level(RIGHT_DOOR_GPIO, 0);
                        vTaskDelay(500 / portTICK_PERIOD_MS);
                        gpio_set_level(RIGHT_DOOR_GPIO, 1);
                    }
                }
                ...
        }
        return ESP_OK;
    }
    

    Como se puede observar, sólo cerramos el relay cuando nos llega el mensaje PUSH, y lo cerramos por .5 segundos.

    Lo siguiente sería volcar nuestro programa al chip. Para ello puedes ejecutar el siguiente comando (asegúrate que tienes conectado la placa a tu ordenador):

        make flash
    

    Si quieres ver lo que está ocurriendo en el chip, puedes ejecutar:

        make monitor
    

    O puedes volcar y ver los logs combinando ambos comandos:

        make flash monitor
    

    Para hacer mis pruebas estoy usando el comando mosquitto_pub. Ejecutando los siguientes comandos debería ver como el relé enciende los LEDs:

    mosquitto_pub -h iot.eclipse.org -t "/garage/door/right" -m "PUSH"
    
    mosquitto_pub -h iot.eclipse.org -t "/garage/door/left" -m "PUSH"
    

    Aquí tenéis un vídeo mostrando la ejecución de los comandos anteriormente mencionados:

    En las siguientes entradas sobre esta serie, veremos la conexión de los sensores, el cableado de instalación en el garaje e incluso una aplicación móvil para controlar las puertas desde el mismo.

    This post is also available in English.