ESP32 MQTT: tutorial con Mosquitto, Home Assistant y ESPHome
Tutorial ESP32 MQTT: instala Mosquitto, publica datos de sensores y controla un relé con PubSubClient, descubrimiento en Home Assistant, YAML de ESPHome y QoS.
La combinación ESP32 y MQTT es la forma más habitual de que los sensores y relés de tu casa hablen entre sí y con un sistema domótico como Home Assistant. MQTT es un protocolo de mensajería ligero pensado para dispositivos pequeños: el ESP32 deja la temperatura que ha medido en un topic como "ev/salon/sicaklik" y cualquier programa que escuche ese topic recibe el valor al instante. Del mismo modo, cuando pulsas un botón en la app de Home Assistant del móvil, el comando "ON" llega al ESP32 en mucho menos de un segundo y activa el relé. A diferencia de HTTP, no abre una conexión nueva en cada petición; funciona sobre una única conexión abierta con cabeceras de solo dos bytes, así que es rápido y apenas carga el Wi-Fi y el procesador.
En esta guía vas a montar desde cero un nodo domótico que funciona. Primero aprenderás los conceptos de MQTT broker, topic, publish y subscribe, y después instalarás el broker Mosquitto en Home Assistant o en una Raspberry Pi. Luego usarás la librería PubSubClient para publicar desde el ESP32 la temperatura y la humedad de un DHT22, escuchar comandos para un relé y añadir MQTT Discovery para que el dispositivo aparezca solo en Home Assistant. También veremos QoS y retain, el mensaje de última voluntad (LWT) que marca el dispositivo como "no disponible" cuando se corta la conexión, la alternativa ESPHome que hace lo mismo sin escribir C++, las diferencias con el ESP8266 y la Raspberry Pi Pico W, y los errores más comunes uno por uno.

¿Qué es MQTT y cómo funciona?
MQTT (Message Queuing Telemetry Transport) funciona con un modelo de publicación/suscripción. En el centro hay un servidor llamado broker; el ESP32, Home Assistant, tu móvil o un programa del ordenador se conectan a él como clientes. Los clientes no se conocen ni saben las direcciones IP de los demás: uno publica un mensaje en un topic y el broker lo reenvía a todos los que están suscritos a ese topic. Piénsalo como una oficina de correos: el ESP32 echa una carta en el buzón "ev/salon/sicaklik" y Home Assistant, que vigila ese buzón, la recibe al momento. Como siempre es el cliente quien abre la conexión, el ESP32 no necesita una IP fija ni abrir puertos en el router; solo tiene que conocer la dirección del broker.
| Concepto | Para qué sirve | Ejemplo |
|---|---|---|
| Broker | Servidor que recibe los mensajes y los reparte entre los suscriptores | Mosquitto, el broker Mosquitto de Home Assistant |
| Cliente | Cualquier dispositivo o programa que se conecta al broker; cada uno tiene un identificador único (client ID) | ESP32, Home Assistant, MQTT Explorer |
| Topic (tema) | Dirección del mensaje, formada por niveles separados por barras | ev/salon/sicaklik |
| Publish (publicar) | Enviar un mensaje a un topic | Mandar el valor "23.4" a ev/salon/sicaklik |
| Subscribe (suscribirse) | Registrarse para recibir los mensajes de un topic | El ESP32 escuchando ev/salon/role/komut |
| Payload (carga útil) | Contenido del mensaje: texto, número o JSON | ON, 23.4, {"sicaklik":23.4} |
| Retain | El broker guarda el último mensaje de un topic y se lo entrega enseguida a los nuevos suscriptores | Estado del relé: ON |
| LWT (última voluntad) | Mensaje que el broker publica en nombre de un cliente si este se desconecta sin avisar | ev/salon/baglanti: offline |
| QoS | Nivel de garantía de entrega del mensaje (0, 1 o 2) | QoS 1 para los comandos del relé |
¿Por qué MQTT? Comparación con servidor web, Blynk y Telegram
| Método | Ventajas | Inconvenientes | Ideal para |
|---|---|---|---|
| MQTT + Home Assistant | Local y rápido, bidireccional; cientos de dispositivos en un panel, automatizaciones | Hay que tener un broker | Gestionar toda la casa desde un solo sitio |
| Servidor web en el ESP32 | Sin servidor extra, se abre en el navegador | Cada dispositivo tiene su propia página, sin automatizaciones entre ellos | Panel de control de un solo dispositivo |
| Blynk IoT | Panel para el móvil ya hecho, acceso por internet | Depende de la nube, límites en el plan gratuito | Prototipos móviles rápidos |
| Bot de Telegram | Notificaciones gratis al instante, sin abrir puertos | Sin gráficas ni panel | Alarmas y comandos sencillos |
| ESP-NOW | Las placas ESP se comunican sin router ni broker | Solo entre placas ESP, no llega directamente a Home Assistant | Llevar datos de un sensor a pilas hasta una pasarela |
En resumen, MQTT brilla cuantos más dispositivos tienes: al añadir un sensor nuevo no tocas ningún código existente, simplemente empiezas a publicar en un topic nuevo. Si quieres controlar un único dispositivo desde el navegador, la guía ESP32 Async Web Server y WebSocket te lleva antes al resultado; para notificaciones en el móvil tienes Bot de Telegram con ESP32 y para un panel móvil ya hecho, Blynk con ESP32. Para un sensor del jardín fuera del alcance del router, una buena solución es llevar los datos con ESP-NOW a un ESP32 pasarela conectado a MQTT.
Estructura de los topics MQTT y comodines
Los topics se parecen a rutas de carpetas: "ev/salon/sicaklik" (en turco, casa/salón/temperatura) tiene tres niveles y distingue mayúsculas de minúsculas, así que "Ev/Salon" y "ev/salon" son dos topics distintos. No empieces con una barra ("/ev/salon" crea un primer nivel vacío) y evita espacios, tildes y la ñ; bastan letras simples, números, guiones y guiones bajos. Al suscribirte puedes usar dos comodines: + sustituye exactamente un nivel ("ev/+/sicaklik" devuelve la temperatura de todas las habitaciones) y # abarca todo lo que viene detrás y solo puede ir al final ("ev/#" devuelve todos los mensajes de la casa). Los comodines solo valen al suscribirse; un mensaje siempre se publica en un topic concreto. Los topics que empiezan por $ (como $SYS) están reservados para las estadísticas del propio broker y una suscripción a # no los incluye. Para el proyecto de esta guía usaremos esta estructura:
| Topic | Lo publica | Lo escucha | Retain |
|---|---|---|---|
| ev/salon/sicaklik | ESP32 (cada 30 segundos) | Home Assistant | Sí |
| ev/salon/nem | ESP32 (cada 30 segundos) | Home Assistant | Sí |
| ev/salon/role/komut | Home Assistant, móvil, automatizaciones | ESP32 | No |
| ev/salon/role/durum | ESP32 (en cada cambio) | Home Assistant | Sí |
| ev/salon/baglanti | ESP32 (online), broker (offline) | Home Assistant | Sí |
| homeassistant/status | Home Assistant (online al arrancar) | ESP32 | No |
Instalar un broker MQTT: Mosquitto
El broker MQTT gratuito más popular es Eclipse Mosquitto; incluso en una Raspberry Pi maneja sin esfuerzo cientos de mensajes por segundo. Hay tres formas sencillas de instalarlo: la aplicación oficial Mosquitto broker si usas Home Assistant OS, el paquete apt en una Raspberry Pi u ordenador Linux aparte, o el instalador de Mosquitto en Windows. Elijas la que elijas, el broker usa el puerto 1883 para MQTT sin cifrar y el 8883 para MQTT cifrado con TLS, y basta con que esté en la misma red doméstica que el ESP32. En Home Assistant, la instalación es así:
- En Home Assistant abre Ajustes > Aplicaciones (en versiones anteriores esta sección se llama Complementos o Add-ons), pulsa Instalar aplicación y elige Mosquitto broker en la lista.
- Pulsa Instalar; cuando termine, pulsa Iniciar y deja activada la opción de arrancar al inicio.
- Crea un usuario propio para el ESP32: en Ajustes > Personas > Usuarios, añade un usuario llamado "esp32" con una contraseña segura. Si no ves esa pestaña, activa el modo avanzado en tu perfil. Los nombres "homeassistant" y "addons" están reservados para el broker, no los uses.
- En la página Ajustes > Dispositivos y servicios, la integración MQTT suele aparecer sola como "Descubierto"; pulsa Configurar y confirma. Si no aparece, busca MQTT con Añadir integración y elige la opción que configura Mosquitto automáticamente.
- En el código del ESP32 pondrás como dirección del broker la IP de Home Assistant (por ejemplo 192.168.1.10), como puerto 1883 y el usuario que acabas de crear.
En una máquina Linux como una Raspberry Pi o Ubuntu bastan unos pocos comandos. Instala el broker y las herramientas de prueba con sudo apt install mosquitto mosquitto-clients. Crea el usuario "esp32" con sudo mosquitto_passwd -c /etc/mosquitto/passwd esp32 y escribe la contraseña dos veces; la opción -c crea el archivo desde cero, así que no la pongas al añadir un segundo usuario o se borrarán los anteriores. Después crea un archivo llamado local.conf en la carpeta /etc/mosquitto/conf.d/, escribe en él las tres líneas de abajo y reinicia el servicio con sudo systemctl restart mosquitto. En Windows basta con añadir las mismas líneas al archivo mosquitto.conf de la carpeta de instalación y reiniciar el servicio Mosquitto Broker.
| Línea | Significado |
|---|---|
| listener 1883 | Escucha en el puerto 1883 en todas las interfaces de red; sin esta línea, Mosquitto 2.x solo acepta conexiones desde el mismo ordenador |
| allow_anonymous false | Rechaza las conexiones sin usuario y contraseña; en la versión 2.x ya es el valor por defecto, pero escribirlo es un buen hábito |
| password_file /etc/mosquitto/passwd | Usa el archivo de contraseñas que creaste con mosquitto_passwd; las contraseñas se guardan en él como hash |
Antes de pasar al ESP32, prueba el broker desde el ordenador; separar desde el principio los problemas del broker de los del código te ahorra mucho tiempo. En un terminal, empieza a escuchar todos los topics de la casa con mosquitto_sub -h 192.168.1.10 -u esp32 -P tucontrasena -t "ev/#" -v. En un segundo terminal escribe mosquitto_pub -h 192.168.1.10 -u esp32 -P tucontrasena -t ev/salon/test -m hola. Si en el primer terminal aparece la línea "ev/salon/test hola", el broker funciona y la contraseña es correcta. La opción -v muestra el topic delante de cada mensaje y la opción -r envía el mensaje como retenido.
Materiales y esquema de conexión
Para el proyecto necesitas una placa de desarrollo ESP32, un sensor de temperatura y humedad DHT22, un módulo de relé que se pueda activar con 3,3 V, una protoboard y cables jumper. El DHT22 es más preciso que el DHT11: mide la temperatura con ±0,5 °C y la humedad con aproximadamente ±2–5 %, y funciona entre -40 y 80 °C; explicamos la familia de sensores en detalle en Sensor de temperatura y humedad DHT11. Si tienes un DHT11, solo cambia DHT22 por DHT11 en el código. Para la primera prueba de conexión basta con un ESP32 y un cable USB; el sensor y el relé puedes añadirlos después.



| Conexión | ESP32 | ESP8266 (NodeMCU) | Raspberry Pi Pico W |
|---|---|---|---|
| DHT22 VCC | 3V3 | 3V3 | 3V3(OUT) |
| DHT22 DATA | GPIO 4 | D2 (GPIO 4) | GP15 |
| DHT22 GND | GND | GND | GND |
| Relé VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| Relé IN | GPIO 26 | D1 (GPIO 5) | GP14 |
| Relé GND | GND | GND | GND |
Instalar PubSubClient en el Arduino IDE
- Si todavía no tienes instalado el paquete de placas ESP32 en el Arduino IDE, instálalo primero; los pasos están en ¿Qué es el ESP32?.
- Abre Herramientas > Administrar bibliotecas, escribe PubSubClient en el buscador e instala la librería de Nick O'Leary. El código de esta guía se compiló con la versión 2.8.
- En la misma ventana busca DHT sensor library e instala la librería de Adafruit; cuando te pregunte por las dependencias, elige Install all para instalar también Adafruit Unified Sensor.
- Si tienes problemas al instalar librerías, consulta la guía Cómo añadir librerías en Arduino.
Primera prueba: conectar el ESP32 al broker MQTT
Antes de añadir sensores, vamos a comprobar con el código más sencillo posible que los datos del Wi-Fi, la dirección del broker y la contraseña son correctos. El siguiente código se conecta al broker, publica "merhaba" (hola) en el topic "test/esp32/durum" y muestra en el Monitor Serie cada mensaje que llega a "test/esp32/komut". Cambia el nombre del Wi-Fi, la contraseña, la IP del broker y el usuario MQTT por los tuyos, sube el código y abre el Monitor Serie a 115200 baudios:
1#include <WiFi.h>
2#include <PubSubClient.h>
3
4const char* WIFI_SSID = "AgAdi"; // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
5const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
6const char* MQTT_SUNUCU = "192.168.1.10"; // broker'ın IP adresi / broker IP address
7const int MQTT_PORT = 1883; // şifresiz MQTT portu / unencrypted MQTT port
8const char* MQTT_KULLANICI = "esp32"; // broker kullanıcı adı / broker username
9const char* MQTT_SIFRE = "mqttSifre"; // broker şifresi / broker password
10
11WiFiClient wifiIstemci; // TCP bağlantısı / TCP connection
12PubSubClient mqtt(wifiIstemci); // MQTT istemcisi / MQTT client
13
14// Abone olunan konuya mesaj gelince çalışır / runs when a subscribed topic receives a message
15void mesajGeldi(char* topic, byte* payload, unsigned int length) {
16 Serial.print("Mesaj [");
17 Serial.print(topic);
18 Serial.print("] ");
19 for (unsigned int i = 0; i < length; i++) {
20 Serial.print((char)payload[i]); // payload sonunda \0 yoktur / payload is not null-terminated
21 }
22 Serial.println();
23}
24
25void setup() {
26 Serial.begin(115200);
27 WiFi.mode(WIFI_STA);
28 WiFi.begin(WIFI_SSID, WIFI_PASS);
29 while (WiFi.status() != WL_CONNECTED) {
30 delay(500);
31 Serial.print(".");
32 }
33 Serial.print("\nIP: ");
34 Serial.println(WiFi.localIP());
35
36 mqtt.setServer(MQTT_SUNUCU, MQTT_PORT); // broker adresi / broker address
37 mqtt.setCallback(mesajGeldi); // gelen mesaj fonksiyonu / incoming message handler
38}
39
40void loop() {
41 if (!mqtt.connected()) {
42 Serial.print("MQTT baglaniyor... ");
43 if (mqtt.connect("esp32-test", MQTT_KULLANICI, MQTT_SIFRE)) { // istemci kimliği benzersiz olmalı / client ID must be unique
44 Serial.println("baglandi");
45 mqtt.subscribe("test/esp32/komut"); // bu konuyu dinle / listen to this topic
46 mqtt.publish("test/esp32/durum", "merhaba"); // ilk mesaj / first message
47 } else {
48 Serial.print("hata, rc=");
49 Serial.println(mqtt.state()); // hata kodu / error code
50 delay(5000);
51 return;
52 }
53 }
54 mqtt.loop(); // mesajları işle, bağlantıyı canlı tut / process messages, keep the connection alive
55}Cuando veas "baglandi" (conectado) en el Monitor Serie, envía desde un terminal de tu ordenador mosquitto_pub -h 192.168.1.10 -u esp32 -P tucontrasena -t test/esp32/komut -m hola; en pocos milisegundos aparecerá en el Monitor Serie la línea "Mesaj [test/esp32/komut] hola". Si la conexión falla, el código muestra un código de error con mqtt.state() y vuelve a intentarlo a los 5 segundos. Estos códigos te dicen exactamente dónde está el problema:
| rc | Nombre en PubSubClient | Significado y solución |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | El broker no respondió a tiempo; red débil o broker sobrecargado |
| -3 | MQTT_CONNECTION_LOST | Una conexión establecida se cayó; normalmente porque loop() no se llamó durante mucho tiempo o se perdió el Wi-Fi |
| -2 | MQTT_CONNECT_FAILED | La conexión TCP nunca se abrió: IP o puerto incorrectos, broker apagado, Mosquitto en modo solo local o un cortafuegos bloqueando el 1883 |
| -1 | MQTT_DISCONNECTED | La conexión se cerró de forma ordenada (se llamó a disconnect) o nunca llegó a conectarse |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Se rechazó el identificador de cliente; se usó un client ID vacío o no válido |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Usuario o contraseña incorrectos (algunos brokers devuelven este código) |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Sin autorización; Mosquitto devuelve este código tanto con una contraseña errónea como al intentar conectar sin credenciales |
Publicar datos de sensores y escuchar comandos del relé con el ESP32
Ahora montamos el proyecto de verdad. El siguiente código lee el DHT22 cada 30 segundos y publica la temperatura y la humedad con la bandera retain, controla el relé con los comandos ON, OFF y TOGGLE que llegan a "ev/salon/role/komut" e informa del nuevo estado del relé en "ev/salon/role/durum". Al conectarse deja una última voluntad en el broker, se presenta a Home Assistant mediante MQTT Discovery y, si se corta la conexión, reintenta cada 5 segundos sin bloquear loop(). Gracias al bloque #if defined(ESP8266) del principio, el mismo código también compila en un NodeMCU sin ningún cambio.
1#if defined(ESP8266)
2#include <ESP8266WiFi.h>
3#else
4#include <WiFi.h>
5#endif
6#include <PubSubClient.h>
7#include <DHT.h>
8
9// ---- Ayarlar / Settings ----
10const char* WIFI_SSID = "AgAdi";
11const char* WIFI_PASS = "Sifre123";
12const char* MQTT_SUNUCU = "192.168.1.10"; // broker IP (Home Assistant ya da Raspberry Pi) / broker IP (Home Assistant or Raspberry Pi)
13const int MQTT_PORT = 1883;
14const char* MQTT_KULLANICI = "esp32";
15const char* MQTT_SIFRE = "mqttSifre";
16const char* ISTEMCI_ID = "esp32-salon"; // her cihazda farklı olmalı / must be different on every device
17
18#if defined(ESP8266)
19const int DHT_PIN = 4; // NodeMCU D2
20const int ROLE_PIN = 5; // NodeMCU D1
21#else
22const int DHT_PIN = 4; // ESP32 GPIO 4
23const int ROLE_PIN = 26; // ESP32 GPIO 26
24#endif
25const bool ROLE_AKTIF_LOW = true; // çoğu röle modülü LOW ile çeker / most relay modules switch on LOW
26const unsigned long OLCUM_ARALIGI = 30000; // 30 saniyede bir ölçüm / one reading every 30 s
27
28// ---- Konular (topic) / Topics ----
29const char* T_SICAKLIK = "ev/salon/sicaklik";
30const char* T_NEM = "ev/salon/nem";
31const char* T_KOMUT = "ev/salon/role/komut"; // ON, OFF ya da TOGGLE / ON, OFF or TOGGLE
32const char* T_DURUM = "ev/salon/role/durum";
33const char* T_BAGLANTI = "ev/salon/baglanti"; // online / offline (son vasiyet / last will)
34const char* T_HA_DURUM = "homeassistant/status"; // HA açılınca "online" yayınlar / HA publishes "online" when it starts
35
36WiFiClient wifiIstemci;
37PubSubClient mqtt(wifiIstemci);
38DHT dht(DHT_PIN, DHT22);
39
40bool roleAcik = false;
41unsigned long sonOlcum = 0;
42unsigned long sonDeneme = 0;
43
44// Röleyi sür ve yeni durumu kalıcı (retain) yayınla / drive the relay and publish the new state as retained
45void roleAyarla(bool acik) {
46 roleAcik = acik;
47 bool seviye = ROLE_AKTIF_LOW ? !acik : acik;
48 digitalWrite(ROLE_PIN, seviye ? HIGH : LOW);
49 mqtt.publish(T_DURUM, acik ? "ON" : "OFF", true);
50 Serial.println(acik ? "Role: ON" : "Role: OFF");
51}
52
53// Tek bir Home Assistant keşif mesajı gönder / send one Home Assistant discovery message
54void kesifYayinla(const char* tur, const char* nesne, const char* ad, const char* ekAlanlar) {
55 char konu[96];
56 snprintf(konu, sizeof(konu), "homeassistant/%s/%s/%s/config", tur, ISTEMCI_ID, nesne);
57 char json[600];
58 snprintf(json, sizeof(json),
59 "{\"name\":\"%s\",\"unique_id\":\"%s_%s\",%s,\"availability_topic\":\"%s\","
60 "\"device\":{\"identifiers\":[\"%s\"],\"name\":\"Salon ESP32\",\"model\":\"ESP32 + DHT22\"}}",
61 ad, ISTEMCI_ID, nesne, ekAlanlar, T_BAGLANTI, ISTEMCI_ID);
62 if (!mqtt.publish(konu, json, true)) { // false: tampon küçük ya da bağlantı yok / false: buffer too small or not connected
63 Serial.println("Kesif mesaji gonderilemedi");
64 }
65}
66
67// Sıcaklık, nem ve röleyi Home Assistant'a tanıt / announce temperature, humidity and relay to Home Assistant
68void haKesifGonder() {
69 char ek[200];
70 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"temperature\",\"unit_of_measurement\":\"°C\",\"state_class\":\"measurement\"", T_SICAKLIK);
71 kesifYayinla("sensor", "sicaklik", "Sıcaklık", ek);
72 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"humidity\",\"unit_of_measurement\":\"%%\",\"state_class\":\"measurement\"", T_NEM);
73 kesifYayinla("sensor", "nem", "Nem", ek);
74 snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"command_topic\":\"%s\"", T_DURUM, T_KOMUT);
75 kesifYayinla("switch", "role", "Röle", ek);
76}
77
78// DHT22'yi oku ve değerleri yayınla / read the DHT22 and publish the values
79void olcumGonder() {
80 float t = dht.readTemperature();
81 float h = dht.readHumidity();
82 if (isnan(t) || isnan(h)) {
83 Serial.println("DHT22 okunamadi");
84 return;
85 }
86 char deger[12];
87 snprintf(deger, sizeof(deger), "%.1f", t); // 23.4 gibi / like 23.4
88 mqtt.publish(T_SICAKLIK, deger, true);
89 snprintf(deger, sizeof(deger), "%.0f", h); // 45 gibi / like 45
90 mqtt.publish(T_NEM, deger, true);
91 Serial.printf("Sicaklik %.1f C, nem %.0f %%\n", t, h);
92}
93
94// Abone olunan konulara mesaj gelince çalışır / runs when a subscribed topic receives a message
95void mesajGeldi(char* topic, byte* payload, unsigned int length) {
96 // Önce kopyala: yayın yapınca kütüphane aynı tamponu kullanır / copy first: publishing reuses the same buffer
97 String konu = topic;
98 String mesaj;
99 for (unsigned int i = 0; i < length; i++) mesaj += (char)payload[i];
100 mesaj.trim();
101
102 if (konu == T_KOMUT) {
103 if (mesaj == "ON") roleAyarla(true);
104 else if (mesaj == "OFF") roleAyarla(false);
105 else if (mesaj == "TOGGLE") roleAyarla(!roleAcik);
106 } else if (konu == T_HA_DURUM && mesaj == "online") {
107 haKesifGonder(); // HA yeniden başladı, tanıtımı yenile / HA restarted, announce again
108 mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
109 }
110}
111
112bool mqttBaglan() {
113 Serial.print("MQTT baglaniyor... ");
114 // Bağlantı koparsa broker T_BAGLANTI'ya "offline" yazar / if we drop, the broker publishes "offline" to T_BAGLANTI
115 if (!mqtt.connect(ISTEMCI_ID, MQTT_KULLANICI, MQTT_SIFRE, T_BAGLANTI, 1, true, "offline")) {
116 Serial.print("hata, rc=");
117 Serial.println(mqtt.state());
118 return false;
119 }
120 Serial.println("baglandi");
121 mqtt.publish(T_BAGLANTI, "online", true);
122 mqtt.subscribe(T_KOMUT, 1); // komutları QoS 1 ile dinle / listen to commands with QoS 1
123 mqtt.subscribe(T_HA_DURUM);
124 haKesifGonder();
125 mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
126 return true;
127}
128
129void setup() {
130 Serial.begin(115200);
131 pinMode(ROLE_PIN, OUTPUT);
132 roleAyarla(false); // güvenli başlangıç: röle kapalı / safe start: relay off
133 dht.begin();
134
135 WiFi.mode(WIFI_STA);
136 WiFi.begin(WIFI_SSID, WIFI_PASS);
137 Serial.print("Wi-Fi");
138 while (WiFi.status() != WL_CONNECTED) {
139 delay(500);
140 Serial.print(".");
141 }
142 Serial.print(" IP: ");
143 Serial.println(WiFi.localIP());
144
145 mqtt.setServer(MQTT_SUNUCU, MQTT_PORT);
146 mqtt.setCallback(mesajGeldi);
147 mqtt.setBufferSize(1024); // keşif JSON'u varsayılan 256 bayta sığmaz / discovery JSON doesn't fit the default 256 bytes
148}
149
150void loop() {
151 if (!mqtt.connected()) {
152 unsigned long simdi = millis();
153 if (sonDeneme == 0 || simdi - sonDeneme >= 5000) { // 5 sn'de bir dene, loop'u kilitleme / retry every 5 s without blocking
154 sonDeneme = simdi;
155 if (mqttBaglan()) sonOlcum = simdi - OLCUM_ARALIGI; // bağlanınca hemen ölç / measure right after connecting
156 }
157 return;
158 }
159 mqtt.loop(); // gelen komutlar ve keep-alive / incoming commands and keep-alive
160
161 if (millis() - sonOlcum >= OLCUM_ARALIGI) {
162 sonOlcum = millis();
163 olcumGonder();
164 }
165}| Línea | Qué hace |
|---|---|
| mqtt.connect(ISTEMCI_ID, ..., T_BAGLANTI, 1, true, "offline") | Registra la última voluntad al conectarse: si el ESP32 se cae sin avisar, el broker escribe un "offline" retenido en ev/salon/baglanti |
| mqtt.publish(T_BAGLANTI, "online", true) | Pone el estado en "online" nada más conectarse; Home Assistant vuelve a mostrar el dispositivo como disponible |
| mqtt.subscribe(T_KOMUT, 1) | Escucha los comandos del relé con QoS 1; PubSubClient solo admite QoS 0 y 1 en las suscripciones |
| mqtt.setBufferSize(1024) | Amplía el búfer de paquetes desde los 256 bytes por defecto; el mayor paquete de descubrimiento ocupa unos 355 bytes |
| mqtt.loop() | Lee los mensajes entrantes y envía los paquetes keep-alive (PINGREQ); hay que llamarlo en cada vuelta de loop |
| ROLE_AKTIF_LOW | La mayoría de los módulos de relé se activan con LOW; si el tuyo se activa con HIGH, ponlo en false |
| OLCUM_ARALIGI | Cada cuánto se mide; el DHT22 se puede leer como mucho cada 2 segundos y para la temperatura ambiente lo ideal son 30–60 segundos |
Algunos detalles del código son intencionados. La función mesajGeldi() copia primero el topic y el payload en Strings, porque PubSubClient usa un único búfer para los mensajes entrantes y salientes: en cuanto llamas a publish() dentro del callback, se sobrescriben los datos a los que apuntan topic y payload. Además, el payload no termina en \0 como una cadena de C, así que leemos exactamente length caracteres. Usamos retain en las lecturas y en el estado del relé para que Home Assistant vea el último valor nada más reiniciarse, sin esperar 30 segundos. Los tiempos se controlan también con millis(): un delay() largo dentro de loop() retrasaría los paquetes keep-alive y el broker cortaría la conexión. Explicamos esta técnica en detalle en Arduino millis() frente a delay().
Cómo se ve en Home Assistant: MQTT Discovery
Home Assistant escucha constantemente los topics que empiezan por el prefijo homeassistant/. Cuando un dispositivo publica una configuración JSON en un topic con la forma homeassistant/[componente]/[nodo]/[objeto]/config, Home Assistant crea esa entidad automáticamente; a esto se le llama MQTT Discovery. Nuestro código envía tres mensajes: dos para los sensores (temperatura y humedad) en homeassistant/sensor/esp32-salon/... y uno para el relé en homeassistant/switch/esp32-salon/role/config. Como son retenidos, sobreviven a un reinicio de Home Assistant. El código también está suscrito a homeassistant/status: Home Assistant escribe ahí "online" al arrancar y el ESP32 vuelve a presentarse. Si tienes muchos dispositivos, la documentación de Home Assistant recomienda añadir un pequeño retardo aleatorio antes de esa nueva presentación.
| Campo JSON | Significado | Nuestro valor |
|---|---|---|
| name | Nombre visible de la entidad; delante se añade el nombre del dispositivo | Sıcaklık (aparece como Salon ESP32 Sıcaklık) |
| unique_id | Identificador permanente de la entidad; sin él no se pueden editar su nombre ni su área desde la interfaz | esp32-salon_sicaklik |
| state_topic | Topic del que se lee el valor | ev/salon/sicaklik |
| command_topic | Topic al que se envían los comandos del interruptor (switch) | ev/salon/role/komut |
| device_class | Define el icono, las unidades disponibles y el tipo de gráfica | temperature, humidity |
| unit_of_measurement | Unidad de medida | °C y % |
| state_class | El valor measurement activa las estadísticas a largo plazo y las gráficas | measurement |
| availability_topic | Topic del que se lee el estado online/offline; los payloads por defecto son online y offline | ev/salon/baglanti |
| device | Agrupa las entidades en una sola tarjeta de dispositivo | identifiers, name, model |
Unos segundos después de subir el código aparece un dispositivo llamado "Salon ESP32" en Ajustes > Dispositivos y servicios > MQTT. Su tarjeta incluye los sensores de temperatura y humedad y un interruptor para el relé; si pulsas la temperatura se abre su historial y si accionas el interruptor el relé se activa. Puedes añadir estas entidades a un panel, exponerlas a asistentes de voz o usarlas en automatizaciones: por ejemplo, una regla como "si el salón supera los 27 °C, enciende el relé del ventilador y apágalo por debajo de 25 °C" se crea en unos pocos clics. Si desenchufas el ESP32, las entidades pasan a "No disponible" como mucho unos 22 segundos después, lo que demuestra que la última voluntad funciona. Si prefieres no usar el descubrimiento, también puedes definir las mismas entidades a mano en configuration.yaml, como sensor y switch bajo mqtt:.
QoS y retain: cuál usar y cuándo
QoS (Quality of Service) define lo seguro que quieres estar de que un mensaje llega al otro lado. Un detalle importante: el QoS se aplica por separado en dos tramos. Entre quien publica y el broker vale el QoS de la publicación, y entre el broker y el suscriptor, el de la suscripción; la garantía que recibe el suscriptor es la menor de las dos. PubSubClient solo admite QoS 0 al publicar y QoS 0 y 1 en las suscripciones. En el Wi-Fi de casa es muy raro perder un mensaje con QoS 0, pero poner la suscripción de comandos en QoS 1 es un seguro barato.
| QoS | Garantía | Cómo funciona | Cuándo usarlo |
|---|---|---|---|
| 0 | Como mucho una vez | El mensaje se envía una vez sin confirmación; puede perderse si la conexión se corta justo entonces | Lecturas frecuentes de sensores |
| 1 | Al menos una vez | El receptor confirma con PUBACK y, si no llega la confirmación, se reenvía; puede llegar dos veces | Comandos del relé, alarmas |
| 2 | Exactamente una vez | Negociación en cuatro pasos con PUBREC, PUBREL y PUBCOMP; el nivel más lento | Contadores y casos en los que los duplicados hacen daño; PubSubClient no lo admite |
La bandera retain no tiene que ver con la entrega sino con la memoria: el broker guarda solo el último mensaje retenido de cada topic y se lo envía al momento a quien se suscriba. Es ideal para mensajes de "estado actual" como los valores de los sensores, el estado del relé o la conexión, pero peligrosa para comandos y eventos puntuales. La última voluntad (LWT) la complementa: si el broker no recibe ningún paquete del cliente durante 1,5 veces el periodo de keep-alive, da la conexión por perdida y publica el mensaje de última voluntad. El keep-alive por defecto de PubSubClient es de 15 segundos, así que un ESP32 desenchufado aparece como "offline" como mucho unos 22 segundos después. Puedes cambiarlo con una línea como mqtt.setKeepAlive(30), pero cuanto más largo, más tarde notarás una desconexión.
La alternativa sin código: ESPHome
ESPHome es una herramienta gratuita que convierte un dispositivo descrito en un archivo YAML en un firmware listo para el ESP32; no escribes ni una línea de C++. En Home Assistant instala ESPHome Device Builder desde Ajustes > Aplicaciones, abre su interfaz y crea una configuración con el asistente Nuevo dispositivo. La primera carga se hace una sola vez por USB: Chrome o Edge pueden programar la placa directamente y, si no funciona, puedes grabar el archivo que descarga el asistente con la página ESPHome Web. Todas las actualizaciones posteriores van por Wi-Fi (OTA). La siguiente configuración equivale al proyecto de Arduino anterior: lee el DHT22 del GPIO 4 cada 30 segundos y ofrece el relé activo a nivel bajo del GPIO 26 como interruptor. Los datos del Wi-Fi y las contraseñas se leen con !secret de un archivo secrets.yaml aparte:
1esphome:
2 name: salon-sensor # küçük harf, rakam ve tire / lowercase, digits and hyphens
3 friendly_name: Salon Sensör
4
5esp32:
6 board: esp32dev
7
8logger:
9
10api: # Home Assistant ile doğrudan bağlantı / direct link to Home Assistant
11 encryption:
12 key: !secret api_anahtari
13
14ota:
15 - platform: esphome
16 password: !secret ota_sifresi
17
18wifi:
19 ssid: !secret wifi_ssid
20 password: !secret wifi_sifresi
21 ap: # Wi-Fi yoksa kendi ağını açar / opens its own network if Wi-Fi fails
22 ssid: "Salon-Sensor"
23 password: "yedek1234"
24
25captive_portal:
26
27sensor:
28 - platform: dht
29 pin: GPIO4
30 model: DHT22
31 update_interval: 30s
32 temperature:
33 name: "Sıcaklık"
34 humidity:
35 name: "Nem"
36
37switch:
38 - platform: gpio
39 name: "Röle"
40 pin:
41 number: GPIO26
42 inverted: true # aktif LOW röle / active-LOW relay
43 restore_mode: ALWAYS_OFF # açılışta kapalı / off at boot
44
45# MQTT ile kullanmak için api: bloğunu sil, bunu aç / to use MQTT, delete api: and uncomment this
46# mqtt:
47# broker: 192.168.1.10
48# username: esp32
49# password: !secret mqtt_sifresiHay una diferencia que debes tener en cuenta: por defecto ESPHome no usa MQTT, sino su propia API nativa cifrada. Home Assistant encuentra el dispositivo solo en la red y no necesita ningún broker. Elige MQTT únicamente si quieres que otros sistemas, como Node-RED, lean los mismos datos o si no usas Home Assistant. Para ello, descomenta las líneas mqtt: del final del archivo y borra el bloque api:; si no lo borras, el dispositivo se reinicia cada vez que pasan 15 minutos sin que nadie se conecte a la API nativa (es el ajuste reboot_timeout). En modo MQTT, ESPHome envía por sí mismo los mensajes de descubrimiento de Home Assistant y los mensajes online/offline en salon-sensor/status, es decir, hace automáticamente todo lo que hicimos a mano en el código de Arduino.
| Característica | PubSubClient (código Arduino) | ESPHome (YAML) |
|---|---|---|
| Programación | Escribes todo tú en C++ | Solo YAML; el firmware se compila automáticamente |
| Integración en Home Assistant | Envías tú los JSON de descubrimiento | Se detecta automáticamente |
| Flexibilidad | Ilimitada; puedes usar cualquier librería y lógica | Limitada a componentes ya hechos, aunque admite cientos de sensores; se puede añadir C++ con lambdas |
| Actualización inalámbrica | Tienes que añadir ArduinoOTA tú | Viene incluida |
| Necesita broker | Sí | No con la API nativa |
| Valor didáctico | Enseña el protocolo MQTT y a programar | Resultados rápidos, oculta lo que pasa por debajo |
MQTT con el ESP8266 y la Raspberry Pi Pico W
El código completo de arriba funciona sin cambios en las placas ESP8266 NodeMCU y Wemos D1 Mini: al elegir NodeMCU 1.0 en Herramientas > Placa, el bloque #if defined(ESP8266) selecciona automáticamente ESP8266WiFi.h en lugar de WiFi.h y los pines D2 y D1. En la Raspberry Pi Pico W usamos el módulo umqtt.simple de MicroPython. No viene incluido en el firmware MicroPython de la Pico W; instálalo en Thonny buscando umqtt.simple en Herramientas > Gestionar paquetes, o escribe import mip; mip.install("umqtt.simple") en la REPL con el Wi-Fi conectado. El módulo dht que necesita el DHT22, en cambio, sí viene con el firmware. El siguiente código hace que una Pico W en la cocina haga el mismo trabajo de medición y relé. Para que sea corto no incluye los mensajes de descubrimiento; para ver la Pico W en Home Assistant, publica los mismos JSON de descubrimiento que en el código del ESP32 con los topics ev/mutfak, o define las entidades a mano bajo mqtt: en configuration.yaml. Guarda el archivo como main.py y se ejecutará cada vez que la placa se encienda:
1import network, time, dht
2from machine import Pin
3from umqtt.simple import MQTTClient # önce mip ile kur / install with mip first
4
5WIFI_SSID = "AgAdi" # Wi-Fi adın / your Wi-Fi name
6WIFI_PASS = "Sifre123" # Wi-Fi şifren / your Wi-Fi password
7MQTT_SUNUCU = "192.168.1.10" # broker IP / broker IP
8ISTEMCI_ID = "pico-mutfak" # benzersiz olmalı / must be unique
9
10T_SICAKLIK = b"ev/mutfak/sicaklik"
11T_NEM = b"ev/mutfak/nem"
12T_KOMUT = b"ev/mutfak/role/komut"
13T_DURUM = b"ev/mutfak/role/durum"
14T_BAGLANTI = b"ev/mutfak/baglanti"
15
16sensor = dht.DHT22(Pin(15)) # DHT22 DATA -> GP15
17role = Pin(14, Pin.OUT, value=1) # aktif LOW röle kapalı başlar / active-LOW relay starts off
18led = Pin("LED", Pin.OUT) # Pico W dahili LED / Pico W onboard LED
19
20wlan = network.WLAN(network.STA_IF)
21wlan.active(True)
22wlan.connect(WIFI_SSID, WIFI_PASS)
23while not wlan.isconnected():
24 time.sleep(0.5)
25print("IP:", wlan.ifconfig()[0])
26
27def mesaj_geldi(konu, mesaj): # ikisi de bytes gelir / both arrive as bytes
28 if konu == T_KOMUT and mesaj in (b"ON", b"OFF"):
29 acik = mesaj == b"ON"
30 role.value(0 if acik else 1)
31 led.value(1 if acik else 0)
32 istemci.publish(T_DURUM, mesaj, retain=True)
33
34istemci = MQTTClient(ISTEMCI_ID, MQTT_SUNUCU, user="esp32", password="mqttSifre", keepalive=60) # Mosquitto kullanıcısı / Mosquitto user
35istemci.set_callback(mesaj_geldi)
36istemci.set_last_will(T_BAGLANTI, b"offline", retain=True) # son vasiyet / last will
37
38def baglan():
39 while True:
40 try:
41 if not wlan.isconnected(): # Wi-Fi koptuysa yeniden bağlan / reconnect Wi-Fi if it dropped
42 wlan.connect(WIFI_SSID, WIFI_PASS)
43 time.sleep(5)
44 istemci.connect()
45 istemci.publish(T_BAGLANTI, b"online", retain=True)
46 istemci.publish(T_DURUM, b"OFF" if role.value() else b"ON", retain=True)
47 istemci.subscribe(T_KOMUT)
48 print("MQTT baglandi")
49 return
50 except Exception as e: # MQTTException(5): yetki yok / not authorized
51 print("MQTT hatasi:", e)
52 time.sleep(5)
53
54baglan()
55son = time.ticks_add(time.ticks_ms(), -30000) # ilk ölçüm hemen / first reading right away
56while True:
57 try:
58 istemci.check_msg() # bekleyen komut var mı? / any pending command?
59 if time.ticks_diff(time.ticks_ms(), son) >= 30000:
60 son = time.ticks_ms()
61 try:
62 sensor.measure()
63 except OSError:
64 print("DHT22 okunamadi")
65 istemci.ping() # veri yoksa da bağlantıyı canlı tut / keep the link alive without data
66 else:
67 istemci.publish(T_SICAKLIK, "{:.1f}".format(sensor.temperature()).encode(), retain=True)
68 istemci.publish(T_NEM, "{:.0f}".format(sensor.humidity()).encode(), retain=True)
69 except OSError:
70 print("Baglanti koptu, yeniden baglaniliyor")
71 time.sleep(5)
72 baglan()
73 time.sleep_ms(100)Ideas de proyectos con ESP32 y MQTT
- Mapa de temperatura por habitaciones: Pon en cada habitación un ESP32 con un DHT22 o un BME280 y sigue toda la casa en una sola gráfica con una suscripción a ev/+/sicaklik.
- Lámpara y enchufe inteligentes: Conecta el relé a una automatización de Home Assistant que lo encienda al anochecer y lo apague a medianoche.
- Sensor de puertas y ventanas: Con un interruptor reed, envía un mensaje MQTT al abrirse la puerta y recibe un aviso en el móvil cuando no haya nadie en casa.
- Luz de pasillo con movimiento: Deja que un sensor PIR publique el movimiento y que una automatización de Home Assistant encienda la luz.
- Estación de riego: Publica el valor del sensor de humedad del suelo y activa la bomba con un comando MQTT cuando la tierra esté seca.
- Monitor de consumo: Mide la corriente de un aparato con un sensor de corriente ACS712 y representa su consumo diario.
- Sensor a pilas: Monta un nodo que mida, envíe el dato por MQTT y vuelva a dormir; los detalles están en ESP32 Deep Sleep.
Errores comunes de ESP32 y MQTT y cómo solucionarlos
| Síntoma o mensaje | Causa | Solución |
|---|---|---|
| rc=-2 todo el rato | IP o puerto del broker incorrectos, broker apagado o Mosquitto en modo solo local | Revisa la IP, añade listener 1883 a la configuración y permite el puerto 1883 en el cortafuegos |
| rc=5 | Usuario o contraseña incorrectos, o intento de conexión sin credenciales | Prueba la contraseña desde el ordenador con mosquitto_sub; si hace falta, vuelve a crear el usuario |
| Se conecta, se cae a los pocos segundos y vuelve a empezar | Dos dispositivos usan el mismo client ID; Mosquitto registra "Client esp32-salon already connected, closing old connection." | Da a cada dispositivo un ISTEMCI_ID único |
| No llegan los mensajes de descubrimiento, publish() devuelve false | El búfer de PubSubClient es de 256 bytes por defecto y el JSON no cabe | Llama a mqtt.setBufferSize(1024) en setup() |
| rc=-3 frecuente | Un delay() largo dentro de loop() impide enviar los keep-alive, o la señal Wi-Fi es débil | Controla los tiempos con millis() y acerca la placa al router |
| Los comandos no llegan al ESP32 | El nombre del topic no coincide en mayúsculas o en la escritura, o subscribe() no se repite al reconectar | Comprueba el topic con mosquitto_sub; pon las líneas subscribe() dentro de la función de conexión |
| El relé se enciende solo al arrancar | Hay un mensaje retenido antiguo en el topic de comandos | Bórralo con mosquitto_pub -r -n -t ev/salon/role/komut |
| Entidades "No disponible" en Home Assistant | El topic de disponibilidad dice offline u online está escrito de otra forma | Escucha ev/salon/baglanti y asegúrate de que el código publica online al conectarse |
| Caracteres extraños en lugar del valor | El payload no termina en \0 y se leyó sin usar length | Copia el payload con un bucle hasta length |
| El dispositivo ESPHome se reinicia cada 15 minutos | En modo MQTT sigue el bloque api:, pero Home Assistant nunca se conecta | Borra el bloque api: o añade reboot_timeout: 0s |
Con un broker, unos cuantos topics y PubSubClient, tu ESP32 se ha convertido en un dispositivo domótico que habla el mismo idioma que el resto de la casa. Puedes añadir sensores nuevos, otras habitaciones y placas distintas como la Pico W, y gestionarlo todo desde un único panel de Home Assistant. En la app BlueGrays encontrarás proyectos como la conexión Wi-Fi del ESP32, la medición de temperatura y humedad con el DHT22, la iluminación inteligente con PIR y relé, el control en tiempo real con WebSocket y el riego inteligente de plantas, paso a paso con esquemas de circuito y código.
Preguntas frecuentes
¿Qué es MQTT y para qué sirve?
MQTT es un protocolo de mensajería ligero en el que los dispositivos envían y reciben mensajes a través de un broker usando nombres de topic. Como consume poco ancho de banda y poca energía, es ideal para que dispositivos pequeños como el ESP32 envíen datos de sensores y reciban comandos. Home Assistant, Node-RED y la mayoría de los sistemas IoT industriales hablan MQTT.
¿Qué puerto usa MQTT?
MQTT sin cifrar usa por defecto el puerto 1883 y MQTT cifrado con TLS, el 8883. El puerto WebSocket para conexiones desde el navegador depende del broker; en el broker Mosquitto de Home Assistant es el 1884. En una red doméstica el ESP32 suele conectarse al 1883.
¿MQTT necesita conexión a internet?
No. Si el broker funciona en casa, en una Raspberry Pi o en Home Assistant, el tráfico entre el ESP32 y el broker se queda por completo en tu red local; los sensores y relés siguen funcionando aunque se caiga internet. Solo necesitas internet para acceder desde fuera o para usar un broker en la nube.
¿Por qué mi ESP32 no aparece en Home Assistant?
Las causas más habituales son un mensaje de descubrimiento que no cabe en el búfer de 256 bytes de PubSubClient, un topic config mal escrito o un error de comillas en el JSON. Escucha homeassistant/# con mosquitto_sub para comprobar si el mensaje llega al broker y asegúrate de que el código incluye la línea mqtt.setBufferSize(1024). Además, la integración MQTT de Home Assistant debe estar conectada al mismo broker.
¿Uso ESPHome o MQTT con Arduino?
Si tu objetivo es tener rápido un dispositivo domótico que funcione, ESPHome da menos trabajo: escribes YAML, el dispositivo aparece solo en Home Assistant y se actualiza por Wi-Fi. Si quieres aprender el protocolo MQTT, escribir tu propia lógica o trabajar con sistemas distintos de Home Assistant, Arduino con PubSubClient es más flexible. Ambos conviven sin problemas en la misma casa.
¿Qué diferencia hay entre MQTT QoS 0, 1 y 2?
QoS 0 envía el mensaje una vez sin esperar confirmación, QoS 1 lo reenvía hasta que se confirma aunque puede llegar dos veces, y QoS 2 garantiza con una negociación de cuatro pasos que llegue exactamente una vez. Para lecturas de sensores basta QoS 0 y para comandos, QoS 1. PubSubClient solo puede publicar con QoS 0 y suscribirse con QoS 0 o 1.
Proyectos para hacer después de leer esto
Ver todos los proyectos
LED Parpadeante con ESP32
Enseña el uso básico de la salida GPIO con ESP32. Conseguimos un efecto de parpadeo encendiendo y apagando un LED desde el pin digital. Versión para ESP32 del clásico proyecto blink en Arduino UNO; los comandos básicos de blink en el entorno Arduino son los mismos, pero la distribución de pines y los niveles GPIO de 3,3 V del ESP32 difieren de los del Arduino UNO.

Conexión Wi-Fi STA con ESP32 (con pantalla LCD)
En este proyecto, conectamos el ESP32 a la red Wi-Fi doméstica en modo Station (STA) de forma segura y tolerante a fallos. Separamos el SSID y la contraseña en el archivo secrets.h para evitar que se filtren a Git, configuramos un mecanismo de reconexión automática y mostramos el nivel de señal RSSI y la dirección IP en una pantalla LCD I2C. El LED interno actúa como indicador de estado.

ESP32 DHT22 Temperatura y humedad (con pantalla LCD)
Se trata de un proyecto que mide la temperatura y la humedad ambientales mediante el sensor DHT22 y las muestra en tiempo real en una pantalla LCD I2C 16x2. Los datos del sensor se leen mediante el protocolo de un solo cable (one-wire) utilizando la biblioteca DHT de Adafruit. Este proyecto constituye la base para estaciones meteorológicas IoT, sistemas de automatización doméstica inteligente o sistemas de monitorización ambiental en invernaderos.

Iluminación Inteligente ESP32 (PIR + LDR + Relé + OLED)
¡Convierte tu pasillo, baño o despensa en iluminación inteligente con ESP32! Utilizando un sensor de movimiento PIR, un sensor de luz LDR y un módulo de relé, construimos el escenario clásico "la luz se enciende cuando se detecta movimiento en la oscuridad" — si el ambiente ya está iluminado, el movimiento NO activa la luz, ahorrando energía durante el día. La luz permanece encendida durante 30 segundos; si el movimiento continúa, el temporizador se reinicia y la luz permanece encendida más tiem

Contador ESP32 WebSocket (2 botones + pantalla LCD + web en tiempo real)
En este proyecto, dos botones conectados al ESP32 (GPIO 5 suma, GPIO 4 resta) cambian un contador. El valor se actualiza al instante en una pantalla LCD I2C de 16x2 y, gracias al soporte WebSocket de ESPAsyncWebServer, en todos los navegadores conectados sin recargar la página. En lugar del sondeo HTTP se usa una única conexión persistente y el ESP32 envía el nuevo valor a todos con ws.textAll().

ESP32 mDNS: Acceso al dispositivo mediante bluegrays-esp.local (con pantalla LCD)
En este proyecto, al añadir el servicio mDNS (Multicast DNS) al ESP32, permitimos el acceso mediante un nombre de host legible, como bluegrays-esp.local, en lugar de una dirección IP. Aunque la IP asignada por el DHCP cambie, siempre podrás acceder al dispositivo con el mismo nombre. Además, al ejecutar un pequeño servidor web en él, enviamos comandos de control de los LED desde el navegador mediante la dirección http://bluegrays-esp.local/. En la pantalla LCD I2C se muestran el nombre de ho

Sistema de Alarma de Movimiento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema inteligente de alarma de seguridad que activa el buzzer y el LED cuando el sensor PIR detecta movimiento, y envía simultáneamente una notificación instantánea al propietario vía Telegram. Cuenta con un tiempo de espera de 30 segundos para limitar la frecuencia de las alarmas repetidas; este mecanismo no comprueba la validez de las detecciones del sensor PIR ni elimina las falsas detecciones. Ideal para seguridad del hogar, vigilancia de garajes, almacenes o salas de trabajo. Con ESP32
Más artículos
App BlueGrays
Monta este circuito paso a paso en tu teléfono
Esquema, código y lista de piezas en una sola pantalla. Más de 200 proyectos de Arduino, ESP32 y Pico listos para montar.
Escanea con tu teléfono
Apunta la cámara al código y se abrirá la tienda de apps de tu teléfono.
o ábrela en la tienda

