Ir al contenido

Dimmer AC vía MQTT con ESP32 y ESP8266

Controla el brillo de una carga AC por MQTT — suscríbete a un topic, recibe valores 0–100, ajusta el dimmer. Funciona con Home Assistant, Node-RED, AWS IoT y cualquier broker MQTT.

Resumen: Instala rbdimmerESP32 (para ESP32) o RBDdimmer (para ESP8266) más PubSubClient. Conéctate a tu broker MQTT, suscríbete a un topic de brillo y llama a rbdimmer_set_level() en el callback. Publica de vuelta el nivel actual como topic de estado.



Cómo funciona

text
ESP32  ──WiFi──►  Broker MQTT  ◄──────  Home Assistant / Node-RED
  │                   │
  │  suscripción: dimmer/level/set
  │  publicación: dimmer/level/state
  │
  └──ISR──►  Gate TRIAC  ──►  Carga (lámpara, calefactor…)

El ESP32 se suscribe a un topic de comando. Cuando HA publica un valor (0–100), el callback lo aplica al dimmer. El ESP32 también publica su nivel actual para que HA se mantenga sincronizado.




Hardware

Componente Ejemplo
MCU ESP32 (doble núcleo) o ESP8266
Dimmer Cualquier módulo rbdimmer 1CH/2CH/4CH
Pin ZC GPIO 5 (cualquier GPIO en ESP32)
Pin DIM GPIO 4 (cualquier GPIO en ESP32)
VCC 3,3V

⚠️ Use rbdimmerESP32 on dual-core ESP32. The older RBDdimmer library has no IRAM_ATTR on its ISR — it crashes when WiFi is active. rbdimmerESP32 pins the ISR to Core 0; WiFi runs on Core 1. See: Wrong Library: RBDdimmer vs rbdimmerESP32




Bibliotecas necesarias

Instala mediante Arduino Library Manager o PlatformIO:

Biblioteca Propósito Placa
rbdimmerESP32 Control ISR del dimmer AC Solo ESP32
RBDdimmer Control ISR del dimmer AC Arduino, ESP8266
PubSubClient Cliente MQTT Cualquiera
WiFi.h WiFi (integrada) ❌ no
ESP8266WiFi.h WiFi (integrada) ESP8266

PlatformIO (platformio.ini):

ini
lib_deps =
  https://github.com/robotdyn-dimmer/rbdimmerESP32
  knolleary/PubSubClient @ ^2.8



Código completo — ESP32

cpp
// AC Dimmer + MQTT on ESP32 (dual-core)
// Library: rbdimmerESP32 + PubSubClient
// IMPORTANT: use rbdimmerESP32, NOT RBDdimmer, on ESP32
#include <WiFi.h>
#include <PubSubClient.h>
#include "rbdimmerESP32.h"
// ── Configuration ─────────────────────────────────────────────
const char* WIFI_SSID     = "YourSSID";
const char* WIFI_PASS     = "YourPassword";
const char* MQTT_SERVER   = "192.168.1.100"; // broker IP
const int   MQTT_PORT     = 1883;
const char* DEVICE_ID     = "dimmer_esp32_01"; // unique per device
// MQTT topics
const char* TOPIC_SET     = "dimmer/level/set";   // receive command
const char* TOPIC_STATE   = "dimmer/level/state"; // publish state
// Dimmer pins
#define ZC_PIN   5
#define DIM_PIN  4
// ── Globals ───────────────────────────────────────────────────
WiFiClient   wifiClient;
PubSubClient mqtt(wifiClient);
rbdimmer_channel_t dimmerCh;
int currentLevel = 0;
// ── MQTT callback ─────────────────────────────────────────────
void onMessage(char* topic, byte* payload, unsigned int length) {
    char buf[8];
    int len = min((int)length, 7);
    memcpy(buf, payload, len);
    buf[len] = '\0';
    int level = atoi(buf);
    level = constrain(level, 0, 100);
    rbdimmer_set_level(dimmerCh, level);
    currentLevel = level;
    // Publish confirmed state
    char stateStr[8];
    itoa(level, stateStr, 10);
    mqtt.publish(TOPIC_STATE, stateStr, true); // retained
}
// ── MQTT reconnect ────────────────────────────────────────────
void mqttReconnect() {
    while (!mqtt.connected()) {
        Serial.print("Connecting to MQTT…");
        if (mqtt.connect(DEVICE_ID)) {
            Serial.println(" connected.");
            mqtt.subscribe(TOPIC_SET);
            // Republish retained state on reconnect
            char stateStr[8];
            itoa(currentLevel, stateStr, 10);
            mqtt.publish(TOPIC_STATE, stateStr, true);
        } else {
            Serial.print(" failed, rc=");
            Serial.println(mqtt.state());
            delay(5000);
        }
    }
}
// ── Setup ─────────────────────────────────────────────────────
void setup() {
    Serial.begin(115200);
    // Connect WiFi
    WiFi.begin(WIFI_SSID, WIFI_PASS);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500); Serial.print(".");
    }
    Serial.println("\nWiFi connected: " + WiFi.localIP().toString());
    // Init dimmer
    rbdimmer_init();
    rbdimmer_register_zero_cross(ZC_PIN, RBDIMMER_PHASE_DEFAULT);
    rbdimmer_config_t cfg = {
        .gpio_pin      = DIM_PIN,
        .phase         = RBDIMMER_PHASE_DEFAULT,
        .initial_level = 0,
        .curve_type    = RBDIMMER_CURVE_RMS
    };
    rbdimmer_create_channel(&cfg, &dimmerCh);
    // Setup MQTT
    mqtt.setServer(MQTT_SERVER, MQTT_PORT);
    mqtt.setCallback(onMessage);
}
// ── Loop ──────────────────────────────────────────────────────
void loop() {
    if (!mqtt.connected()) mqttReconnect();
    mqtt.loop();
}



Código completo — ESP8266

Para ESP8266, cambia la biblioteca y el encabezado WiFi. La lógica MQTT es idéntica.

cpp
// Dimmer AC + MQTT en ESP8266 — sketch completo
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <RBDdimmer.h>
const char* WIFI_SSID   = "YourSSID";
const char* WIFI_PASS   = "YourPassword";
const char* MQTT_SERVER = "192.168.1.100";
const int   MQTT_PORT   = 1883;
const char* DEVICE_ID   = "dimmer_esp8266_01";
const char* TOPIC_SET   = "dimmer/level/set";
const char* TOPIC_STATE = "dimmer/level/state";
#define ZC_PIN  5   // D1 (NodeMCU)
#define DIM_PIN 4   // D2
WiFiClient   wifiClient;
PubSubClient mqtt(wifiClient);
dimmerLamp   dimmer(DIM_PIN, ZC_PIN);
int currentLevel = 0;
void onMessage(char* topic, byte* payload, unsigned int length) {
    char buf[8];
    int len = min((int)length, 7);
    memcpy(buf, payload, len);
    buf[len] = '\0';
    int level = constrain(atoi(buf), 0, 100);
    dimmer.setPower(level);
    currentLevel = level;
    char stateStr[8];
    itoa(level, stateStr, 10);
    mqtt.publish(TOPIC_STATE, stateStr, true);
}
void mqttReconnect() {
    while (!mqtt.connected()) {
        if (mqtt.connect(DEVICE_ID)) {
            mqtt.subscribe(TOPIC_SET);
        } else {
            delay(5000);
        }
    }
}
void setup() {
    WiFi.begin(WIFI_SSID, WIFI_PASS);
    while (WiFi.status() != WL_CONNECTED) delay(500);
    dimmer.begin(NORMAL_MODE, ON);
    dimmer.setPower(0);
    mqtt.setServer(MQTT_SERVER, MQTT_PORT);
    mqtt.setCallback(onMessage);
}
void loop() {
    if (!mqtt.connected()) mqttReconnect();
    mqtt.loop();
}



Estructura de topics MQTT

Topic Dirección Valor Ejemplo
dimmer/level/set → ESP32 0100 75
dimmer/level/state ← ESP32 0100 75

Para múltiples dimmers, usa prefijos de topics únicos por dispositivo:

text
dimmer/kitchen/level/set
dimmer/bedroom/level/set



Configuración de Home Assistant

Añade un light MQTT en configuration.yaml (o usa MQTT Discovery):

yaml
mqtt:
  light:
    - name: "Living Room Dimmer"
      unique_id: "dimmer_esp32_01"
      command_topic: "dimmer/level/set"
      state_topic: "dimmer/level/state"
      brightness_command_topic: "dimmer/level/set"
      brightness_state_topic: "dimmer/level/state"
      brightness_scale: 100
      on_command_type: "brightness"
      retain: true
      optimistic: false

on_command_type: "brightness" — cuando enciendes la luz desde HA, se envía el valor de brillo como payload MQTT (por ejemplo, 75) en lugar de la cadena "ON". El firmware recibe un número directamente y lo aplica al dimmer.

Después de añadir, reinicia HA. El dimmer aparece como una entidad de luz regulable con un deslizador de brillo de 0–100 %.




MQTT multicanal

Para módulos 2CH o 4CH, crea pares de topics y descriptores de canal adicionales. Cada canal obtiene su propio par de suscripción/publicación:

cpp
// Canal adicional
rbdimmer_channel_t ch2;
const char* TOPIC_SET2   = "dimmer/ch2/level/set";
const char* TOPIC_STATE2 = "dimmer/ch2/level/state";
// En onMessage(): verificar topic, dirigir al canal correcto
if (strcmp(topic, TOPIC_SET) == 0) {
    rbdimmer_set_level(dimmerCh, level);
} else if (strcmp(topic, TOPIC_SET2) == 0) {
    rbdimmer_set_level(ch2, level);
}



Artículos relacionados



¿Todavía tienes preguntas?

Ask on forum.rbdimmer.com or open a GitHub Issue.

Compartir esta publicación
Iniciar sesión para dejar un comentario