Resumen: Un dimmer AC no puede regular la carga si el microcontrolador no está recibiendo la señal de cruce por cero (el momento en que la sinusoide cruza por cero). Sin esta señal es imposible calcular el momento de disparo del TRIAC. Causas: pin incorrecto (no soporta interrupciones), cableado ZC incorrecto, ruido en la línea de señal o error de software (modo de interrupción incorrecto). Siga la lista de verificación paso a paso.
Descripción del problema
Conectaste un dimmer AC, subiste código de ejemplo — pero la carga no responde o siempre está a brillo máximo. El cableado parece correcto, sin errores de compilación.
La causa más común en estos casos es que el cruce por cero no está siendo detectado por el microcontrolador. La atenuación por corte de fase funciona con este principio: detectar el cruce por cero de la sinusoide → esperar el retardo calculado → disparar el TRIAC. Sin cruce por cero no hay temporización, y el dimmer o no enciende la carga o la mantiene al 100% sin regulación.
Síntomas típicos:
- Carga siempre a brillo máximo (TRIAC siempre abierto)
- Carga no se enciende en absoluto
setPower(50)no tiene efecto — carga completamente encendida o apagada- El contador de cruce por cero no se incrementa en el monitor serial (con depuración)
- El código compila sin errores pero el dimmer «no funciona»
Mensajes típicos en foros:
- «El dimmer no atenúa — lámpara siempre a brillo máximo»
- «setPower no tiene efecto»
- «La interrupción de cruce por cero no dispara»
- «El dimmer funciona a veces, aleatoriamente»
Causa raíz
La señal de cruce por cero es generada por un circuito detector de cruce por cero integrado en el dimmer AC o conectado externamente. Típicamente es un optoacoplador + divisor resistivo que produce un pulso corto en cada cruce por cero de la sinusoide.
Frecuencia de pulsos: 100 Hz (red 50 Hz) / 120 Hz (red 60 Hz). Un pulso por cada semiciclo.
Para recibir estos pulsos el microcontrolador usa una interrupción externa
(attachInterrupt()). Si la interrupción no está configurada correctamente
o la señal no llega al pin correcto — el manejador ISR nunca se llama, la
temporización no se puede calcular, y el dimmer no funciona.
Causas principales de fallo:
- Pin incorrecto (no soporta interrupciones) — error más común en Arduino
- Cableado ZC incorrecto — la señal no llega al pin
- Modo de interrupción incorrecto (
FALLINGen lugar deRISINGo viceversa — depende del circuito) - Ruido — múltiples disparos falsos en un semiciclo
- Problema de alimentación del circuito ZC — sin VCC en el detector de cruce por cero
Soluciones
🟢 Para principiantes: DimmerLink — cruce por cero integrado
¿No quieres lidiar con pines de interrupción, RISING/FALLING y ruido eléctrico? DimmerLink tiene su propio detector de cruce por cero y controla el TRIAC de forma autónoma.
Cualquier 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.
Cuándo elegir esta solución:
DimmerLink → Arduino/ESP32 cableado:
- VCC → 3.3V (ESP32) o 5V (Arduino)
- GND → GND
- SDA → SDA (GPIO 21 en ESP32, A4 en Arduino Uno)
- SCL → SCL (GPIO 22 en ESP32, A5 en Arduino Uno)
Código:
// DimmerLink — detector de cruce por cero y control TRIAC dentro del módulo
// El microcontrolador solo establece el nivel de brillo
// Documentación: 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 por defecto para tu placa
setLevel(50); // brillo 50%
}
void loop() {
// Sin ISR, sin interrupciones, sin cruce por cero en el MCU
}Resultado: El dimmer funciona sin depender de pines de interrupción ni de la señal de cruce por cero en el microcontrolador.
🔵 Para usuarios avanzados: diagnosticar y corregir el ZC
¿Quieres mantener la conexión ISR directa? Sigue los pasos de diagnóstico.
Diagnóstico: pasos 1–5
Paso 1: Verificar que el pin soporta interrupciones
Esta es la causa n.º 1 en Arduino.
// Arduino Uno / Nano / Mini:
// ✅ Soportan interrupciones: SOLO pines 2 y 3
// ❌ Todos los demás pines (4, 5, 6...) — interrupciones no soportadas
// Correcto:
attachInterrupt(digitalPinToInterrupt(2), zeroCrossISR, RISING); // pin 2 ✅
attachInterrupt(digitalPinToInterrupt(3), zeroCrossISR, RISING); // pin 3 ✅
// Incorrecto:
attachInterrupt(4, zeroCrossISR, RISING); // ❌ pin 4 — no soporta interrupciones
// En Uno, attachInterrupt(4) se refiere a INT4, que no existeEn ESP32 — cualquier GPIO soporta interrupciones:
// ESP32: cualquier GPIO — todos funcionan
attachInterrupt(digitalPinToInterrupt(18), zeroCrossISR, RISING); // ✅
attachInterrupt(digitalPinToInterrupt(34), zeroCrossISR, RISING); // ✅En ESP8266 — todos los GPIO excepto GPIO 16 soportan interrupciones; se recomiendan 4, 5, 12, 13, 14 (sin dependencias de modo de arranque).
Paso 2: Agregar un contador para verificar
La prueba más simple — contar pulsos durante 1 segundo:
// Diagnóstico de cruce por cero: se esperan ~100 pulsos/seg (red 50 Hz)
// o ~120 pulsos/seg (red 60 Hz)
#define ZC_PIN 2 // ← asegúrate de que es un pin con soporte de interrupciones
volatile uint32_t zcCount = 0;
#ifdef ESP32
void IRAM_ATTR zeroCrossISR() { // IRAM_ATTR requerido en ESP32
#else
void zeroCrossISR() { // Arduino/ESP8266: IRAM_ATTR no necesario
#endif
zcCount++;
}
void setup() {
Serial.begin(115200);
pinMode(ZC_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
}
void loop() {
delay(1000);
Serial.print("Pulsos ZC por segundo: ");
Serial.println(zcCount);
zcCount = 0;
// Resultados esperados:
// ~100 — red 50 Hz, todo correcto
// ~120 — red 60 Hz, todo correcto
// 0 — ZC no detectado (problema de cableado o pin)
// >200 — ruido, disparos falsos (problema de filtro RC)
}Paso 3: Verificar el cableado ZC
Cableado típico RBDimmer → Arduino/ESP32:
RBDimmer → Arduino/ESP32
------- -------------
VCC → 5V (Arduino) / 3.3V (ESP32)
GND → GND
ZC → Pin con soporte de interrupciones (2 o 3 en Arduino Uno)
DIM → Cualquier salida digitalLista de verificación:
Paso 4: Verificar el modo de interrupción (RISING/FALLING/CHANGE)
El modo depende del circuito detector ZC de tu módulo específico:
// La mayoría de los módulos RBDimmer: RISING (pulso en flanco ascendente)
attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
// Algunos circuitos con optoacoplador inversor: FALLING
attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, FALLING);
// Si no estás seguro — prueba CHANGE (captura ambos flancos):
// Nota: con CHANGE el contador mostrará ~200 Hz en lugar de 100 Hz
attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, CHANGE);Usa un osciloscopio o analizador lógico para verificar la forma de la señal ZC — identifica si necesitas capturar el flanco ascendente o descendente.
Paso 5: Filtrar ruido
Si el contador muestra >200 (en red de 50 Hz) — disparos falsos por ruido.
Esto es especialmente común con cables largos o proximidad a la carga:
// Antirrebote por software para ZC: ignorar pulsos demasiado cercanos al anterior
// Período del semiciclo a 50 Hz = 10 000 µs → filtrar todo lo menor a 8 000 µs
volatile uint32_t lastZC = 0;
void IRAM_ATTR zeroCrossISR() {
uint32_t now = micros();
if (now - lastZC > 8000) { // intervalo mínimo de 8 ms entre pulsos ZC
lastZC = now;
// tu lógica de temporización TRIAC aquí
}
}Filtro de hardware: un filtro RC en la línea ZC (resistencia de 1 kΩ + condensador de 100 nF entre ZC y GND) elimina el ruido de alta frecuencia.
Corrección: elige tu opción
Opción A: rbdimmerESP32 en ESP32 ✅
Cuándo: ESP32 de doble núcleo con conexión directa al dimmer. La biblioteca maneja ZC y temporización automáticamente:
// Plataforma: ESP32 doble núcleo
// Biblioteca: rbdimmerESP32
#include "rbdimmerESP32.h"
#define ZC_PIN 18 // cualquier GPIO ESP32
#define DIM_PIN 19 // cualquier GPIO ESP32
rbdimmer dimmer;
void setup() {
Serial.begin(115200);
dimmer.begin(ZC_PIN, DIM_PIN, 50); // red 50 Hz
dimmer.setPower(50);
Serial.println("Dimmer inicializado");
}
void loop() {}Errores comunes:
- ZC_PIN y DIM_PIN intercambiados en
dimmer.begin()— verifica cuál pin va a la carga y cuál al cruce por cero. - Frecuencia de red incorrecta (tercer parámetro) — 50 o 60 Hz.
Opción B: Arduino Uno/Mega con RBDdimmer
// Plataforma: Arduino Uno / Mega / Nano (AVR)
// Biblioteca: RBDdimmer — https://github.com/robotdyn/dimmer
// ¡ADVERTENCIA: para ESP32 usa rbdimmerESP32, no esta biblioteca!
#include <RBDdimmer.h>
// ⚠️ Arduino Uno: ZC SOLO en pines 2 o 3
#define ZC_PIN 2 // soporta interrupciones ✅
#define DIM_PIN 11 // cualquier salida digital
dimmerLamp dimmer(DIM_PIN, ZC_PIN);
void setup() {
Serial.begin(9600);
dimmer.begin(NORMAL_MODE, ON);
dimmer.setPower(50); // 50%
Serial.println("Dimmer listo");
}
void loop() {}⚠️ Errores comunes
-
«Uso el pin 4 en Arduino Uno — no funciona»: Los pines 4, 5, 6, ... en el Uno no soportan interrupciones externas. Solo los pines 2 y 3. Este es el error más común de principiantes.
-
«
attachInterrupt(4, ...)compila — debería funcionar»: Compila, pero no funcionará.attachInterrupt(4, ...)en el Uno se refiere a INT4 (número de interrupción por hardware), no a GPIO 4. Usa siempredigitalPinToInterrupt(pin). -
«Agregué contador — siempre 0»: Tres posibles causas: 1. Pin incorrecto (no soporta interrupciones) — verifica arriba 2. Sin alimentación en el módulo ZC 3. Pin ZC no conectado físicamente a la placa
-
«Contador muestra 300–400 en lugar de 100»: Disparos falsos por ruido. Agrega antirrebote por software (ver arriba) o un filtro RC de hardware.
-
«Pin 34 en ESP32 no funciona para ZC»: GPIO 34–39 en ESP32 son solo entrada — soportan interrupciones. Pero no tienen pull-up/pull-down interno. Agrega una resistencia externa de 10 kΩ a 3.3V.
-
«Funciona sin carga, falla con carga conectada»: El ruido eléctrico de la carga (especialmente motores, transformadores) se acopla a la línea ZC. Separa físicamente los cables de potencia y señal.
Verificación rápida
Antes de publicar en el foro, verifica:
Tabla de compatibilidad de pines de interrupción
| Placa | Pines con soporte de interrupciones | Nota |
|---|---|---|
| Arduino Uno | 2, 3 | Solo estos dos |
| Arduino Nano | 2, 3 | Solo estos dos |
| Arduino Mega | 2, 3, 18, 19, 20, 21 | Seis pines |
| ❌ no | Todos los GPIO (0–39) | Excepto reservados |
| ESP8266 | Todos los GPIO excepto GPIO 16 | Recomendados 4,5,12,13,14 (sin boot) |
| — (sin ISR) | Todos los GPIO (vía pigpio) | Sin tiempo real — preferir DimmerLink |
Problemas relacionados
- ESP32 + dimmer AC: Guru Meditation Error →
troubleshooting/esp32-iram-attr.md - Trailing vs Leading Edge →
load-types/trailing-vs-leading-edge.md - El dimmer no regula LED →
load-types/led-lamp-compatibility-triac.md
¿Todavía tienes preguntas?
Ask on forum.rbdimmer.com or open a GitHub Issue.