Перейти к содержимому

AC-диммер через MQTT с ESP32 и ESP8266

Управление яркостью AC-нагрузки по MQTT — подписка на топик, приём значений 0–100, установка уровня диммера. Работает с Home Assistant, Node-RED, AWS IoT и любым MQTT-брокером.

Коротко: Установите rbdimmerESP32 (для ESP32) или RBDdimmer (для ESP8266) плюс PubSubClient. Подключитесь к MQTT-брокеру, подпишитесь на топик яркости и вызовите rbdimmer_set_level() в колбэке. Публикуйте обратно текущий уровень как топик состояния.



Как это работает

text
ESP32  ──WiFi──►  MQTT-брокер  ◄──────  Home Assistant / Node-RED
  │                   │
  │  подписка: dimmer/level/set
  │  публикация: dimmer/level/state
  │
  └──ISR──►  затвор TRIAC  ──►  Нагрузка (лампа, нагреватель…)

ESP32 подписывается на командный топик. Когда HA публикует значение (0–100), колбэк применяет его к диммеру. ESP32 также публикует текущий уровень, чтобы HA оставался синхронизированным.




Оборудование

Компонент Пример
MCU ESP32 (двухъядерный) или ESP8266
Диммер Любой модуль rbdimmer 1CH/2CH/4CH
Пин ZC GPIO 5 (любой GPIO на ESP32)
Пин DIM GPIO 4 (любой GPIO на ESP32)
VCC 3,3 В

⚠️ 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




Необходимые библиотеки

Установите через Arduino Library Manager или PlatformIO:

Библиотека Назначение Плата
rbdimmerESP32 Управление ISR AC-диммера Только ESP32
RBDdimmer Управление ISR AC-диммера Arduino, ESP8266
PubSubClient MQTT-клиент Любая
WiFi.h WiFi (встроенная) ❌ нет
ESP8266WiFi.h WiFi (встроенная) ESP8266

PlatformIO (platformio.ini):

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



Полный код — 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();
}



Полный код — ESP8266

Для ESP8266 замените библиотеку и заголовок WiFi. Логика MQTT идентична.

cpp
// AC-диммер + MQTT на ESP8266 — полный скетч
#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();
}



Структура MQTT-топиков

Топик Направление Значение Пример
dimmer/level/set → ESP32 0100 75
dimmer/level/state ← ESP32 0100 75

Для нескольких диммеров используйте уникальные префиксы топиков для каждого устройства:

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



Конфигурация Home Assistant

Добавьте MQTT light в configuration.yaml (или используйте 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" — при включении света из HA отправляется значение яркости в качестве MQTT-payload (например, 75) вместо строки "ON". Прошивка получает число напрямую и применяет его к диммеру.

После добавления перезапустите HA. Диммер появится как сущность диммируемого света с ползунком яркости 0–100 %.




Многоканальный MQTT

Для модулей 2CH или 4CH создайте дополнительные пары топиков и дескрипторов каналов. Каждый канал получает свою пару подписки/публикации:

cpp
// Дополнительный канал
rbdimmer_channel_t ch2;
const char* TOPIC_SET2   = "dimmer/ch2/level/set";
const char* TOPIC_STATE2 = "dimmer/ch2/level/state";
// В onMessage(): проверяем топик, направляем на нужный канал
if (strcmp(topic, TOPIC_SET) == 0) {
    rbdimmer_set_level(dimmerCh, level);
} else if (strcmp(topic, TOPIC_SET2) == 0) {
    rbdimmer_set_level(ch2, level);
}



Связанные статьи



Остались вопросы?

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

Поделиться этой записью
Войти оставить комментарий