Кратко: AC-диммер не может регулировать нагрузку, если микроконтроллер не получает сигнал перехода через ноль (момент пересечения синусоиды нуля). Без этого сигнала невозможно рассчитать момент включения TRIAC. Причины: неправильный пин (не поддерживает прерывания), неверная схема подключения ZC, помехи на линии сигнала или программная ошибка (неверный режим прерывания). Пройдите по чек-листу ниже шаг за шагом.
Описание проблемы
Вы подключили AC-диммер, загрузили пример кода — но нагрузка не реагирует или всегда работает на полной яркости. Подключение выглядит правильно, ошибок компиляции нет.
Наиболее частая причина в этих случаях — переход через ноль не определяется микроконтроллером. Диммирование с фазовой отсечкой работает по принципу: обнаружить переход синусоиды через ноль → подождать расчётную задержку → включить TRIAC. Без перехода через ноль нет тайминга, и диммер либо не включает нагрузку вообще, либо держит её на 100% без регулировки.
Типичные симптомы:
- Нагрузка всегда на полной яркости (TRIAC всегда открыт)
- Нагрузка вообще не включается
setPower(50)не даёт эффекта — нагрузка либо полностью включена, либо выключена- Счётчик переходов через ноль не увеличивается в мониторе порта (если добавить отладку)
- Код компилируется без ошибок, но диммер «не работает»
Типичные сообщения на форумах:
- «Диммер не диммирует — лампа всегда на полной яркости»
- «setPower не даёт эффекта»
- «Прерывание перехода через ноль не срабатывает»
- «Диммер работает иногда, случайным образом»
Причина проблемы
Сигнал перехода через ноль формируется детектором перехода через ноль, встроенным в AC-диммер или подключённым внешне. Обычно это оптопара + резистивный делитель, который формирует короткий импульс при каждом пересечении синусоиды нуля.
Частота импульсов: 100 Гц (сеть 50 Гц) / 120 Гц (сеть 60 Гц). Один импульс на каждый полупериод.
Для приёма этих импульсов микроконтроллер использует внешнее прерывание
(attachInterrupt()). Если прерывание настроено неправильно или сигнал
не доходит до нужного пина — обработчик ISR никогда не вызывается, тайминг
невозможно вычислить, и диммер не работает.
Основные причины сбоя:
- Неправильный пин (не поддерживает прерывания) — самая частая ошибка на Arduino
- Неверная схема подключения ZC — сигнал не доходит до пина
- Неверный режим прерывания (
FALLINGвместоRISINGили наоборот — зависит от схемы) - Помехи — множественные ложные срабатывания за один полупериод
- Проблема с питанием цепи 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.
Когда выбрать это решение:
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)
Код:
// 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.
// 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 поддерживает прерывания:
// ESP32: любой GPIO — все работают
attachInterrupt(digitalPinToInterrupt(18), zeroCrossISR, RISING); // ✅
attachInterrupt(digitalPinToInterrupt(34), zeroCrossISR, RISING); // ✅На ESP8266 — все GPIO кроме GPIO 16 поддерживают прерывания; рекомендуются 4, 5, 12, 13, 14 (нет зависимостей от режима загрузки).
Шаг 2: Добавьте счётчик для проверки
Простейший тест — подсчёт импульсов за 1 секунду:
// Диагностика перехода через ноль: ожидается ~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:
RBDimmer → Arduino/ESP32
------- -------------
VCC → 5V (Arduino) / 3.3V (ESP32)
GND → GND
ZC → Пин с поддержкой прерываний (2 или 3 на Arduino Uno)
DIM → Любой цифровой выходЧек-лист:
Шаг 4: Проверьте режим прерывания (RISING/FALLING/CHANGE)
Режим зависит от схемы детектора ZC конкретного модуля:
// Большинство модулей 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 Гц) — ложные срабатывания из-за помех.
Это особенно часто при длинных проводах или близости к нагрузке:
// Программный дебаунс для 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 и тайминги автоматически:
// Платформа: двухъядерный 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
// Платформа: 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 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 Error →
troubleshooting/esp32-iram-attr.md - Trailing vs Leading Edge →
load-types/trailing-vs-leading-edge.md - Диммер не регулирует LED →
load-types/led-lamp-compatibility-triac.md
Остались вопросы?
Ask on forum.rbdimmer.com or open a GitHub Issue.