Display domótico - segunda parte

Proyecto Display domótico – Segunda parte

Segunda parte del desarrollo de un proyecto de diseño y fabricación de un display para la domótica, realizado principalmente con Displays de 7 segmentos. Estos displays son muy versátiles y los hay de todos los tipos, tamaños y colores.

En el vídeo de hoy utilizamos un display de 4 dígitos controlado desde un ESP8266. Y además haremos una práctica automatización para convertirlo en un reloj digital.

El material utilizado en el vídeo es el siguiente:

A continuación te dejo el enlace al vídeo:

Este es el Mini PC que gobierna mi domótica

Si estás empezando con la domótica, te bastará con una Raspberry Pi 4

Y a continuación el código ESP8266. En primer lugar main.cpp:


#include <ESP8266WiFi.h>          // Librería para manejar la conexión WiFi en ESP8266
#include <ESPAsyncTCP.h>          // Librería para manejar conexiones TCP asíncronas
#include <ESPAsyncWebServer.h>    // Librería para manejar un servidor web asíncrono
#include <ElegantOTA.h>           // Librería para permitir actualizaciones OTA (Over The Air)
#include <PubSubClient.h>         // Librería para manejar la comunicación MQTT

#include "secrets.h"              // Archivo que contiene las credenciales WiFi (ssid y password)
#include "ota.h"                  // Archivo que contiene funciones de callbacks para ElegantOTA

AsyncWebServer server(80);        // Instancia del servidor web en el puerto 80
WiFiClient espClient;             // Instancia del cliente WiFi
PubSubClient client(espClient);   // Instancia del cliente MQTT usando el cliente WiFi
unsigned long lastMsg = 0;        // Variable para almacenar el tiempo del último mensaje MQTT
#define MSG_BUFFER_SIZE (50)      // Tamaño del buffer para mensajes MQTT
char msg[MSG_BUFFER_SIZE];        // Buffer para mensajes MQTT

// Tópicos MQTT para mostrar datos y ajustar el retardo
const char* topic_mostrar = "display7seg/mostrar";
const char* topic_retardo = "display7seg/retardo";

int retardo = 0;                  // Variable para almacenar el retardo entre actualizaciones
int mostrar[4] = {0, 0, 0, 0};    // Array para almacenar los dígitos a mostrar

// Pines para los segmentos del display de 7 segmentos
int segmentos[7] = {12, 16, 10, 15, 13, 2, 14};

// Pines para los dígitos del display
int digitos[4] = {5, 4, 0, 9};

// Mapeo de los números (0-9) a los segmentos del display
int numeros[10] = {
  0b1000000,  // 0
  0b1111001,  // 1
  0b0100100,  // 2
  0b0110000,  // 3
  0b0011001,  // 4
  0b0010010,  // 5
  0b0000010,  // 6
  0b1111000,  // 7
  0b0000000,  // 8
  0b0010000   // 9
};

// Función para configurar la conexión WiFi
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Conexión a ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);           // Configura el modo WiFi como cliente
  WiFi.begin(ssid, password);    // Inicia la conexión a la red WiFi

  // Espera hasta que se establezca la conexión WiFi
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi conectado");
  Serial.println("IP: ");
  Serial.println(WiFi.localIP());  // Imprime la IP asignada
}

// Callback para manejar mensajes MQTT recibidos
void mqtt_recibido(char* topic, byte* payload, unsigned int length) {
  Serial.print("Recibido topic: ");
  Serial.print(topic);

  // Si el mensaje es para ajustar el retardo
  if (String(topic) == topic_retardo) {
    Serial.print(" Retardo: ");
    retardo = 0;
    for (unsigned int i = 0; i < length; i++) {
      Serial.print((char)payload[i]);
      retardo = retardo * 10 + (char)payload[i] - '0';  // Convierte el payload a entero
    }
    Serial.println();
  }

  // Si el mensaje es para mostrar dígitos
  if (String(topic) == topic_mostrar) {
    Serial.print(" Mostrar: ");
    for (unsigned int i = 0; i < length; i++) {
      Serial.print((char)payload[i]);
      mostrar[i] = (char)payload[i] - '0';  // Convierte el payload a entero
    }
    Serial.println();
  }
}

// Función para reconectar al broker MQTT
void reconectar_mqtt() {
  while (!client.connected()) {
    Serial.print("Conectando MQTT...");
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);  // Genera un ID de cliente aleatorio

    if (client.connect(clientId.c_str())) {
      Serial.println("conectado");
      client.publish("display7seg/estado", "Display 7 segmentos conectado");
      client.subscribe(topic_mostrar);  // Suscribe a los tópicos necesarios
      client.subscribe(topic_retardo);
    } else {
      Serial.print("fallo, rc=");
      Serial.print(client.state());
      Serial.println(" intentando de nuevo en 5 segundos");
      delay(5000);  // Espera 5 segundos antes de intentar reconectar
    }
  }
}

// Configuración inicial
void setup() {
  Serial.begin(115200);          // Inicializa la comunicación serie a 115200 baudios
  setup_wifi();                  // Configura la conexión WiFi
  
  // Configura el servidor web
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "MicroPIC Display 7 segmentos. Entra en IP/update para actualizar");
  });

  server.begin();                // Inicia el servidor web
  Serial.println("HTTP server iniciado");

  ElegantOTA.begin(&server);     // Configura ElegantOTA para actualizaciones OTA
  ElegantOTA.onStart(onOTAStart);
  ElegantOTA.onProgress(onOTAProgress);
  ElegantOTA.onEnd(onOTAEnd);

  client.setServer(mqtt_server, 1883);  // Configura el servidor MQTT
  client.setCallback(mqtt_recibido);    // Configura el callback para mensajes MQTT recibidos
  
  // Configura los pines de los dígitos como salidas y los apaga
  for (int i = 0; i < 4; i++) {
    pinMode(digitos[i], OUTPUT);
    digitalWrite(digitos[i], false);
  }
  
  // Configura los pines de los segmentos como salidas y los enciende (apagado activo bajo)
  for (int i = 0; i < 7; i++) {
    pinMode(segmentos[i], OUTPUT);
    digitalWrite(segmentos[i], true);
  }
}

// Bucle principal
void loop() {
  static int i, turno, cifra;

  if (!client.connected()) {
    reconectar_mqtt();  // Reconecta al broker MQTT si la conexión se ha perdido
  }
  client.loop();        // Mantiene la conexión MQTT
  ElegantOTA.loop();    // Mantiene la funcionalidad de ElegantOTA

  // Apaga el dígito actual
  digitalWrite(digitos[turno % 4], false);
  
  // Cambia al siguiente dígito
  turno++;
  
  // Extrae la forma de la cifra a mostrar
  cifra = numeros[ mostrar[turno % 4] ];

  // Recorre los 7 segmentos
  for (i = 0; i < 7; i++) {
    // Enciende o apaga cada segmento según la coincidencia con la matriz de números
    digitalWrite(segmentos[i], (cifra & (1 << i)));
  }

  // Enciende el nuevo dígito configurado
  digitalWrite(digitos[j], true);

  // Retardo entre actualizaciones, configurable vía MQTT
  delay(retardo);
}

Las funciones de programación Over The Air las he dejado en un fichero llamado ota.h:

unsigned long ota_progress_millis = 0;

void onOTAStart() {
  // Log when OTA has started
  Serial.println("OTA update started!");
  // <Add your own code here>
}

void onOTAProgress(size_t current, size_t final) {
  // Log every 1 second
  if (millis() - ota_progress_millis > 1000) {
    ota_progress_millis = millis();
    Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
  }
}

void onOTAEnd(bool success) {
  // Log when OTA has finished
  if (success) {
    Serial.println("OTA update finished successfully!");
  } else {
    Serial.println("There was an error during OTA update!");
  }
  // <Add your own code here>
}

Y por último también necesitarás un fichero para guardar los strings de configuración, llamado secrets.h:

const char* ssid =         "tuSSID";
const char* password =     "tuPassword";
const char* mqtt_server =  "Dirección IP de tu servidor MQTT";
const char* mqtt_usuario = "Usuario de tu servidor MQTT";
const char* mqtt_contrase ="tuPassword";
const char* topic_estado = "display7seg/estado";
Scroll al inicio