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

ESP32-S2/C3/H2: AC-диммер не работает на одноядерном ESP32

ESP32-C3/S2/H2 — одноядерные. rbdimmerESP32 не может изолировать задачу диммера от WiFi. Прерывания WiFi DMA работают на уровне NMI и нарушают тайминги TRIAC на аппаратном уровне. DimmerLink по I2C — единственное надёжное решение.

Кратко: rbdimmerESP32 рассчитан на двухъядерный ESP32 (оригинальный и S3). ESP32-S2, C3, C6 и H2 — одноядерные. Библиотека пытается выделить задачу диммера на отдельное ядро — на одноядерных чипах это невозможно. ISR конкурирует со стеком WiFi, тайминги нарушаются, результат — мерцание, случайные сбои и нестабильная яркость. Решение — DimmerLink (аппаратный ZC + I2C) или переход на двухъядерный чип.



Описание проблемы

Вы купили ESP32-C3 или ESP32-S2 — компактный, с WiFi/BLE, недорогой. Подключаете AC-диммер напрямую, используете rbdimmerESP32 — и видите:

  • Нагрузка мерцает на любом уровне яркости
  • Тайминг нестабиленsetPower(50) даёт то 30%, то 70%
  • Сбои при активном WiFi — без WiFi лучше, с WiFi — хаос
  • Код компилируется без ошибок, нестабильность только в рантайме

Или компилятор предупреждает о xTaskCreatePinnedToCore с несуществующим ядром.

Типичные сообщения на форумах:

  • «ESP32-C3 + диммер — мерцает на любом уровне мощности»
  • «rbdimmerESP32 работает на ESP32, но не на ESP32-S2»
  • «setPower(50) даёт случайную яркость на C3»
  • «Без WiFi работает нормально, при подключении WiFi всё ломается»



Причина проблемы


Какие чипы ESP32 двухъядерные, а какие нет

Чип Ядра Архитектура Прямое ISR-диммирование
ESP32 (оригинал) 2 Xtensa LX6 rbdimmerESP32
ESP32-S3 2 Xtensa LX7 rbdimmerESP32
ESP32-S2 1 Xtensa LX7 ⚠️ Только DimmerLink
ESP32-C3 1 RISC-V ⚠️ Только DimmerLink
ESP32-C6 1 RISC-V ⚠️ Только DimmerLink
ESP32-H2 1 RISC-V ⚠️ Только DimmerLink

⚠️ Примечание об ESP32-C6: у C6 есть дополнительное LP-ядро (Low Power), но оно не подходит для ISR-диммирования — работает на пониженной частоте без полной поддержки FreeRTOS. Основное ядро — одно.

⚠️ Частая путаница: ESP32-S3 — двухъядерный (работает). ESP32-S2 — одноядерный (не работает с прямым ISR). Это разные чипы.


Почему диммирование с фазовой отсечкой требует изоляции ядер

Тайминг затвора TRIAC: от перехода через ноль до импульса на затворе — задержка 100–9000 мкс, точность ±10–20 мкс. Любое отклонение — неправильная яркость.

rbdimmerESP32 достигает такой точности на двухъядерном чипе так:

  • Ядро 0 — задача диммера (цикл тайминга, обработка ZC, включение TRIAC)
  • Ядро 1 — стек WiFi, TCP/IP, пользовательский код

Они работают параллельно и не блокируют друг друга.

На одноядерном (C3, S2, H2) — оба потока делят одно ядро. Стек WiFi регулярно блокирует процессор на 1–5 мс для обработки пакетов. В это время ISR диммера пропускает своё окно → лампа мерцает.

Прерывания WiFi DMA имеют фиксированный высокий аппаратный приоритет в ESP-IDF — они не вытесняются пользовательскими IRAM_ATTR ISR на одноядерном чипе. portDISABLE_INTERRUPTS() тоже не помогает: WiFi DMA работает на уровне NMI. Программно это не решается.



Решения



🟢 Для всех: DimmerLink — аппаратное решение

Единственное надёжное решение для одноядерного ESP32 с WiFi. DimmerLink сам определяет переход через ноль и управляет TRIAC — ваш MCU только отправляет уровень яркости по I2C.

Любой ESP32 has its own controller. Your ESP32-C3 only sets the brightness — no ISR, no timing-critical code on the MCU side.

Схема подключения для ESP32-C3:

  • VCC → 3.3V
  • GND → GND
  • SDA → GPIO 8 (стандартный SDA на ESP32-C3)
  • SCL → GPIO 9 (стандартный SCL на ESP32-C3)

⚠️ На некоторых модулях ESP32-C3 GPIO 8/9 используются внешней flash-памятью. Если Wire.begin() не работает — укажите пины явно: Wire.begin(SDA_PIN, SCL_PIN) со свободными GPIO (например, 1, 10).

Схема подключения для ESP32-S2:

  • SDA → GPIO 3 (проверьте распиновку вашей платы)
  • SCL → GPIO 4

Код (одинаковый для всех платформ):

cpp
// DimmerLink на ESP32-C3 / S2 / H2 — без ISR на MCU
// Стабильная работа при активном WiFi
#include <Wire.h>
#define DIMMER_ADDR 0x50
#define REG_LEVEL   0x10
void setLevel(uint8_t level) {  // level: 0–100%
    Wire.beginTransmission(DIMMER_ADDR);
    Wire.write(REG_LEVEL);
    Wire.write(level);
    Wire.endTransmission();
}
void setup() {
    Wire.begin();  // стандартные SDA/SCL для вашей платы
    setLevel(50);  // яркость 50%
}
void loop() {
    // WiFi можно использовать свободно — диммер не затрагивается
}

Результат: Стабильная работа с WiFi/BLE на любом одноядерном ESP32.



🔵 Нужен прямой ISR — переходите на двухъядерный ESP32

Нужна архитектура с прямым ISR? Используйте оригинальный ESP32 или S3.

Оригинальный ESP32 и ESP32-S3 — двухъядерные, rbdimmerESP32 работает штатно:

cpp
// ESP32 оригинальный / ESP32-S3 — двухъядерный
// rbdimmerESP32 работает как задумано
#include "rbdimmerESP32.h"
#define ZC_PIN  18
#define DIM_PIN 19
rbdimmer dimmer;
void setup() {
    Serial.begin(115200);
    dimmer.begin(ZC_PIN, DIM_PIN, 50);  // ZC_PIN, DIM_PIN, 50 Гц
    dimmer.setPower(50);
}
void loop() {}

Когда стоит менять чип:

  • ☐ Уже есть код с прямым подключением ZC/DIM
  • ☐ Нужно управлять несколькими каналами одновременно
  • ☐ Разница в цене между ESP32 и ESP32-C3 несущественна для проекта
  • the project



    ⚠️ Частые ошибки

    • «Без WiFi на C3 работает, с WiFi — мерцает»: Классический симптом конфликта ISR на одноядерном чипе. Хорошо задокументированное поведение. Решение: DimmerLink.

    • «rbdimmerESP32 скомпилировался на C3 — значит работает»: Компилируется, потому что ядро 0 существует даже на одноядерных чипах. xTaskCreatePinnedToCore(..., 0) создаст задачу, но без изоляции от WiFi. Тайминг нестабилен — это не «работает».

    • «Использую ESPHome / Tasmota на ESP32-C3 — диммер не работает»: ESPHome и Tasmota на одноядерном ESP32 имеют те же ограничения, что и пользовательский код. Стек WiFi конкурирует с ISR диммера. Используйте DimmerLink с ESPHome через I2C (i2c: + sensor: или custom_component).

    • «У ESP32-S3 тоже есть S — он тоже одноядерный?»: Нет. ESP32-S3 — двухъядерный (как оригинальный ESP32, только LX7). Его путают с ESP32-S2. Проверьте маркировку чипа на плате.

    • «Я использую FreeRTOS вручную и могу привязать задачи сам»: На одноядерном xTaskCreatePinnedToCore(..., 0) создаст задачу, но стек WiFi тоже на ядре 0. Любой vTaskDelay(1) в задаче диммера отдаёт процессор WiFi — тайминг ломается. А прерывания WiFi DMA имеют аппаратный приоритет выше пользовательских ISR. Программно это не решается. DimmerLink — единственное надёжное решение.

    • "I'm using FreeRTOS manually and can pin tasks myself": On single-core xTaskCreatePinnedToCore(..., 0) creates the task, but the WiFi stack is also on Core 0. Any vTaskDelay(1) in the dimmer task yields the CPU to WiFi — timing breaks. And WiFi DMA interrupts have a hardware priority above user ISR. This can't be solved in software. DimmerLink is the only reliable solution.




    Экспресс-проверка

  • ☐ ESP32-C3/S2/H2/C6 — одноядерный → DimmerLink
  • ☐ ESP32 (без суффикса) / S3 — двухъядерный → `rbdimmerESP32` работает
  • ☐ Работает без WiFi, ломается с WiFi? → Конфликт ISR на одноядерном чипе
  • ☐ DimmerLink: правильные пины SDA/SCL для вашего чипа?
  • ☐ DimmerLink: correct SDA/SCL pins for your chip?


  • Связанные проблемы

    • ESP32 + TRIAC: Guru Meditation Errortroubleshooting/esp32-iram-attr.md
    • Переход через ноль не обнаруженtroubleshooting/zero-cross-detection-errors.md
    • Zero-cross not detectedtroubleshooting/zero-cross-detection-errors.md



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

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

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