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

Переход через ноль не определяется: AC-диммер не реагирует на команды

AC-диммер застрял на полной яркости или не реагирует? Сбой детектирования перехода через ноль — самая частая причина: неправильный пин прерывания, нет питания на цепи ZC или помехи. 5-шаговая диагностика с тестовым кодом и исправлением для Arduino и ESP32.

Кратко: AC-диммер не может регулировать нагрузку, если микроконтроллер не получает сигнал перехода через ноль (момент пересечения синусоиды нуля). Без этого сигнала невозможно рассчитать момент включения TRIAC. Причины: неправильный пин (не поддерживает прерывания), неверная схема подключения ZC, помехи на линии сигнала или программная ошибка (неверный режим прерывания). Пройдите по чек-листу ниже шаг за шагом.



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

Вы подключили AC-диммер, загрузили пример кода — но нагрузка не реагирует или всегда работает на полной яркости. Подключение выглядит правильно, ошибок компиляции нет.

Наиболее частая причина в этих случаях — переход через ноль не определяется микроконтроллером. Диммирование с фазовой отсечкой работает по принципу: обнаружить переход синусоиды через ноль → подождать расчётную задержку → включить TRIAC. Без перехода через ноль нет тайминга, и диммер либо не включает нагрузку вообще, либо держит её на 100% без регулировки.

Типичные симптомы:

  • Нагрузка всегда на полной яркости (TRIAC всегда открыт)
  • Нагрузка вообще не включается
  • setPower(50) не даёт эффекта — нагрузка либо полностью включена, либо выключена
  • Счётчик переходов через ноль не увеличивается в мониторе порта (если добавить отладку)
  • Код компилируется без ошибок, но диммер «не работает»

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

  • «Диммер не диммирует — лампа всегда на полной яркости»
  • «setPower не даёт эффекта»
  • «Прерывание перехода через ноль не срабатывает»
  • «Диммер работает иногда, случайным образом»



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

Сигнал перехода через ноль формируется детектором перехода через ноль, встроенным в AC-диммер или подключённым внешне. Обычно это оптопара + резистивный делитель, который формирует короткий импульс при каждом пересечении синусоиды нуля.

Частота импульсов: 100 Гц (сеть 50 Гц) / 120 Гц (сеть 60 Гц). Один импульс на каждый полупериод.

Для приёма этих импульсов микроконтроллер использует внешнее прерывание (attachInterrupt()). Если прерывание настроено неправильно или сигнал не доходит до нужного пина — обработчик ISR никогда не вызывается, тайминг невозможно вычислить, и диммер не работает.

Основные причины сбоя:

  1. Неправильный пин (не поддерживает прерывания) — самая частая ошибка на Arduino
  2. Неверная схема подключения ZC — сигнал не доходит до пина
  3. Неверный режим прерывания (FALLING вместо RISING или наоборот — зависит от схемы)
  4. Помехи — множественные ложные срабатывания за один полупериод
  5. Проблема с питанием цепи ZC — нет VCC на детекторе перехода через ноль



Решения



🟢 Для начинающих: DimmerLink — переход через ноль встроен

Не хотите разбираться с пинами прерываний, RISING/FALLING и электрическими помехами? DimmerLink имеет собственный детектор перехода через ноль и управляет TRIAC автономно.

Любой ESP32 detects zero-cross in hardware and handles TRIAC firing timing internally. Your microcontroller only sets the brightness level — no ISR, no interrupts, no pin headaches.

Когда выбрать это решение:

  • ☐ Не можете разобраться с пинами прерываний на Arduino
  • ☐ Используете Raspberry Pi (нет реального времени для прерываний)
  • ☐ Используете ESP32-S2/C3/H2 (одноядерный, ISR не работает)
  • ☐ Хотите надёжное решение без отладки цепи ZC
  • DimmerLink → Arduino/ESP32 схема подключения:

    • VCC → 3.3V (ESP32) или 5V (Arduino)
    • GND → GND
    • SDA → SDA (GPIO 21 на ESP32, A4 на Arduino Uno)
    • SCL → SCL (GPIO 22 на ESP32, A5 на Arduino Uno)

    Код:

    cpp
    // DimmerLink — детектор перехода через ноль и управление TRIAC внутри модуля
    // Микроконтроллер только задаёт уровень яркости
    // Документация: https://www.rbdimmer.com/docs/dimmerlink-I2CCommunication
    #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() {
        // Нет ISR, нет прерываний, нет перехода через ноль на MCU
    }

    Результат: Диммер работает без зависимости от пинов прерываний или сигнала перехода через ноль на микроконтроллере.



    🔵 Для продвинутых: диагностика и исправление ZC

    Хотите сохранить прямое подключение через ISR? Выполните шаги диагностики.


    Диагностика: шаги 1–5

    Шаг 1: Убедитесь, что пин поддерживает прерывания

    Это причина №1 на Arduino.

    cpp
    // Arduino Uno / Nano / Mini:
    // ✅ Поддерживают прерывания: ТОЛЬКО пины 2 и 3
    // ❌ Все остальные пины (4, 5, 6...) — прерывания не поддерживаются
    // Правильно:
    attachInterrupt(digitalPinToInterrupt(2), zeroCrossISR, RISING);  // пин 2 ✅
    attachInterrupt(digitalPinToInterrupt(3), zeroCrossISR, RISING);  // пин 3 ✅
    // Неправильно:
    attachInterrupt(4, zeroCrossISR, RISING);  // ❌ пин 4 — не поддерживает прерывания
    // На Uno, attachInterrupt(4) ссылается на INT4, который не существует

    На ESP32 — любой GPIO поддерживает прерывания:

    cpp
    // ESP32: любой GPIO — все работают
    attachInterrupt(digitalPinToInterrupt(18), zeroCrossISR, RISING);  // ✅
    attachInterrupt(digitalPinToInterrupt(34), zeroCrossISR, RISING);  // ✅

    На ESP8266 — все GPIO кроме GPIO 16 поддерживают прерывания; рекомендуются 4, 5, 12, 13, 14 (нет зависимостей от режима загрузки).


    Шаг 2: Добавьте счётчик для проверки

    Простейший тест — подсчёт импульсов за 1 секунду:

    cpp
    // Диагностика перехода через ноль: ожидается ~100 импульсов/сек (сеть 50 Гц)
    // или ~120 импульсов/сек (сеть 60 Гц)
    #define ZC_PIN 2  // ← убедитесь, что это пин с поддержкой прерываний
    volatile uint32_t zcCount = 0;
    #ifdef ESP32
    void IRAM_ATTR zeroCrossISR() {  // IRAM_ATTR обязателен на ESP32
    #else
    void zeroCrossISR() {             // Arduino/ESP8266: IRAM_ATTR не нужен
    #endif
        zcCount++;
    }
    void setup() {
        Serial.begin(115200);
        pinMode(ZC_PIN, INPUT);
        attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
    }
    void loop() {
        delay(1000);
        Serial.print("Импульсов ZC в секунду: ");
        Serial.println(zcCount);
        zcCount = 0;
        // Ожидаемые результаты:
        // ~100 — сеть 50 Гц, всё в порядке
        // ~120 — сеть 60 Гц, всё в порядке
        //   0  — ZC не определяется (проблема с подключением или пином)
        //  >200 — помехи, ложные срабатывания (проблема с RC-фильтром)
    }

    Шаг 3: Проверьте подключение ZC

    Типичная схема подключения RBDimmer → Arduino/ESP32:

    text
    RBDimmer  →  Arduino/ESP32
    -------      -------------
    VCC       →  5V (Arduino) / 3.3V (ESP32)
    GND       →  GND
    ZC        →  Пин с поддержкой прерываний (2 или 3 на Arduino Uno)
    DIM       →  Любой цифровой выход

    Чек-лист:

  • ☐ Пин ZC подключён к правильному GPIO с поддержкой прерываний
  • ☐ VCC подведено к модулю (без питания цепь ZC не работает)
  • ☐ Общий GND — и логическая, и силовая части
  • ☐ Кабель ZC не проложен параллельно проводам 220 В (наводит помехи)
  • ☐ Разъёмы ZC и DIM не перепутаны (на некоторых модулях они рядом)

  • Шаг 4: Проверьте режим прерывания (RISING/FALLING/CHANGE)

    Режим зависит от схемы детектора ZC конкретного модуля:

    cpp
    // Большинство модулей RBDimmer: RISING (импульс по переднему фронту)
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
    // Некоторые схемы с инвертирующей оптопарой: FALLING
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, FALLING);
    // Если не уверены — попробуйте CHANGE (ловит оба фронта):
    // Примечание: с CHANGE счётчик покажет ~200 Гц вместо 100 Гц
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, CHANGE);

    Используйте осциллограф или логический анализатор для проверки формы сигнала ZC — определите, нужно ли ловить передний или задний фронт.


    Шаг 5: Фильтрация помех

    Если счётчик показывает >200 (при сети 50 Гц) — ложные срабатывания из-за помех. Это особенно часто при длинных проводах или близости к нагрузке:

    cpp
    // Программный дебаунс для ZC: игнорировать импульсы слишком близкие к предыдущему
    // Период полупериода при 50 Гц = 10 000 мкс → фильтровать всё короче 8 000 мкс
    volatile uint32_t lastZC = 0;
    void IRAM_ATTR zeroCrossISR() {
        uint32_t now = micros();
        if (now - lastZC > 8000) {  // минимальный интервал между импульсами ZC 8 мс
            lastZC = now;
            // ваша логика тайминга TRIAC здесь
        }
    }

    Аппаратный фильтр: RC-фильтр на линии ZC (резистор 1 кОм + конденсатор 100 нФ между ZC и GND) убирает высокочастотные помехи.



    Исправление: выберите вариант

    Вариант A: rbdimmerESP32 на ESP32 ✅

    Когда: двухъядерный ESP32 с прямым подключением диммера. Библиотека обрабатывает ZC и тайминги автоматически:

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

    Типичные ошибки:

    • ZC_PIN и DIM_PIN перепутаны в dimmer.begin() — проверьте, какой пин идёт к нагрузке, а какой к детектору перехода через ноль.
    • Неверная частота сети (третий параметр) — 50 или 60 Гц.

    Вариант B: Arduino Uno/Mega с RBDdimmer
    cpp
    // Платформа: Arduino Uno / Mega / Nano (AVR)
    // Библиотека: RBDdimmer — https://github.com/robotdyn/dimmer
    // ВНИМАНИЕ: для ESP32 используйте rbdimmerESP32, а не эту библиотеку!
    #include <RBDdimmer.h>
    // ⚠️ Arduino Uno: ZC ТОЛЬКО на пинах 2 или 3
    #define ZC_PIN   2   // поддерживает прерывания ✅
    #define DIM_PIN  11  // любой цифровой выход
    dimmerLamp dimmer(DIM_PIN, ZC_PIN);
    void setup() {
        Serial.begin(9600);
        dimmer.begin(NORMAL_MODE, ON);
        dimmer.setPower(50);  // 50%
        Serial.println("Диммер готов");
    }
    void loop() {}


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

    • «Использую пин 4 на Arduino Uno — не работает»: Пины 4, 5, 6, ... на Uno не поддерживают внешние прерывания. Только пины 2 и 3. Это самая частая ошибка начинающих.

    • «attachInterrupt(4, ...) компилируется — должно работать»: Компилируется, но работать не будет. attachInterrupt(4, ...) на Uno ссылается на INT4 (номер аппаратного прерывания), а не на GPIO 4. Всегда используйте digitalPinToInterrupt(pin).

    • «Добавил счётчик — всегда 0»: Три возможные причины: 1. Неправильный пин (не поддерживает прерывания) — проверьте выше 2. Нет питания на модуле ZC 3. Пин ZC физически не подключён к плате

    • «Счётчик показывает 300–400 вместо 100»: Ложные срабатывания из-за помех. Добавьте программный дебаунс (см. выше) или аппаратный RC-фильтр.

    • «Пин 34 на ESP32 не работает для ZC»: GPIO 34–39 на ESP32 работают только на вход — они поддерживают прерывания. Но у них нет встроенного pull-up/pull-down. Добавьте внешний резистор 10 кОм к 3.3 В.

    • «Работает без нагрузки, с нагрузкой ломается»: Электрические помехи от нагрузки (особенно моторы, трансформаторы) проникают в линию ZC. Физически разведите силовые и сигнальные провода.




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

    Перед публикацией на форуме проверьте:

  • ☐ Arduino: пин ZC — только 2 или 3 (Uno/Nano). Остальные не работают.
  • ☐ Подведено ли VCC к цепи ZC диммера?
  • ☐ Пин ZC физически подключён к плате (не только DIM)?
  • ☐ Счётчик переходов через ноль за 1 секунду: ~100 (50 Гц) или ~120 (60 Гц)?
  • ☐ Счётчик `>200`? Помехи — добавьте дебаунс или RC-фильтр.
  • ☐ Режим прерывания: `RISING` или `FALLING` для вашей схемы?
  • ☐ ESP32: используете `rbdimmerESP32`, а не старую `RBDdimmer`?


  • Таблица совместимости пинов прерываний

    Плата Пины с поддержкой прерываний Примечание
    Arduino Uno 2, 3 Только эти два
    Arduino Nano 2, 3 Только эти два
    Arduino Mega 2, 3, 18, 19, 20, 21 Шесть пинов
    ❌ нет Все GPIO (0–39) Кроме зарезервированных
    ESP8266 Все GPIO кроме GPIO 16 Рекомендуются 4,5,12,13,14 (без boot)
    — (без ISR) Все GPIO (через pigpio) Нет реального времени — используйте DimmerLink



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

    • ESP32 + AC-диммер: Guru Meditation Errortroubleshooting/esp32-iram-attr.md
    • Trailing vs Leading Edgeload-types/trailing-vs-leading-edge.md
    • Диммер не регулирует LEDload-types/led-lamp-compatibility-triac.md



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

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

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