Ir al contenido

El dimmer AC TRIAC no es PWM: cómo funciona la atenuación por corte de fase

analogWrite o ledc en el pin DIM de tu dimmer AC solo da brillo máximo — el TRIAC no responde al PWM. La atenuación por corte de fase requiere una señal de cruce por cero y una biblioteca adecuada. Explicado con diagramas ASCII y código funcional.

Resumen: analogWrite() y PWM no funcionan con un dimmer AC TRIAC. Un TRIAC no es una resistencia ni un transistor DC. Regula la potencia mediante control de fase: se abre en un momento calculado con precisión en cada semiciclo, sincronizado con la señal de cruce por cero. Sin cruce por cero y la biblioteca correcta — la carga solo estará completamente encendida o apagada, sin valores intermedios.



Descripción del problema

Conectaste un dimmer AC, encontraste el pin DIM — e intentaste controlarlo con analogWrite() como una salida PWM normal. O usaste ledc en ESP32. Resultado: la carga está al 100% o completamente apagada. Sin brillo intermedio.

A veces el comportamiento es aún más inesperado:

  • Con valores PWM bajos, la carga no enciende en absoluto
  • Con valores altos — salta directamente a potencia máxima
  • Un valor de 50% (128 de 255) no da 50% de brillo

Esto no es un error de código ni un módulo defectuoso. Es una incompatibilidad fundamental entre el método de control y el tipo de carga.

Situaciones típicas en foros:

  • «Conecté el pin DIM del dimmer a PWM, analogWrite(128), pero la lámpara está a máxima potencia»
  • «Probé PWM en el dimmer TRIAC, pero no hay efecto de atenuación»
  • «ESP32 ledc en el pin del dimmer — la luz solo se enciende y se apaga»
  • «¿Por qué analogWrite no funciona con el dimmer AC?»
  • Pin ZC no conectado en absoluto — solo DIM en una salida PWM



Causa raíz


PWM — para corriente continua (DC)

PWM (modulación por ancho de pulso) controla la potencia mediante conmutación rápida: un transistor se abre y se cierra miles de veces por segundo, y la carga recibe un voltaje promedio proporcional al ciclo de trabajo.

text
PWM 50% ciclo de trabajo (carga DC — tira LED, motor, calentador DC):
Voltaje:
 ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐
─┘  └──┘  └──┘  └──┘  └──┘  └─
│←ON→│←OFF→│   ← frecuencia 1–20 kHz
Potencia promedio = 50% ✅

Esto funciona porque una carga DC responde al voltaje promedio. Un calentador calienta a potencia promedio; un LED parpadea lo suficientemente rápido para que el ojo no lo note.


TRIAC — para corriente alterna (AC): funciona diferente

Un TRIAC es un rectificador controlado por silicio para AC. Se comporta de manera fundamentalmente diferente:

  1. Se enclava — una vez que la corriente comienza a fluir (y supera la corriente de mantenimiento), el TRIAC permanece abierto hasta el final del semiciclo, incluso si la señal de compuerta se retira.
  2. Se apaga automáticamente en el cruce por cero de la onda sinusoidal — cuando la corriente cae por debajo de la corriente de mantenimiento.
  3. No responde al PWM rápido: un solo pulso de compuerta de cualquier duración abre el TRIAC hasta el final del semiciclo. 10 µs o 5 ms — el resultado es el mismo.
text
Onda sinusoidal AC (50 Hz):
    ╭──────╮         ╭──────╮
    │      │         │      │
────╯      ╰────────╯      ╰────
    ↑      ↑         ↑      ↑
   ZC    ZC(-)      ZC    ZC(-)
   (100 pulsos por segundo)
text
TRIAC se abre en ZC — la carga recibe el semiciclo COMPLETO (100%):
    ╭──────╮         ╭──────╮
    │//////│         │//////│
────╯      ╰────────╯      ╰────
↑ se abre              ↑ se abre en ZC
text
TRIAC se abre con 5 ms de retraso — la carga recibe la MITAD del semiciclo (~50%):
    ──────╮         ──────╮
          │               │
    ──────╯────────  ─────╯──────
    ↑     ↑          ↑    ↑
   ZC   se abre     ZC  se abre
        (después de 5 ms)  (después de 5 ms)


Por qué el PWM "no ve" el TRIAC

Cuando aplicas 50% PWM (p. ej., 500 Hz) al pin DIM del TRIAC:

  • PWM conmuta 500 veces por segundo
  • El primer pulso HIGH abre el TRIAC
  • Los pulsos LOW posteriores no significan nada — el TRIAC ya está enclavado
  • Permanece abierto hasta el final del semiciclo (10 ms a 50 Hz)
  • La carga termina a potencia máxima

Sin cruce por cero — sin temporización — sin atenuación.



Tabla: PWM vs corte de fase

Parámetro PWM (DC) Corte de fase (AC TRIAC)
Tipo de señal Conmutación continua Pulso de compuerta único
¿Necesita ZC? No Sí — obligatorio
¿Necesita biblioteca? No (analogWrite)
Tipo de carga DC (tira LED, motor, calentador) AC (lámpara, halógena, calentador)
Método de control Ciclo de trabajo del pulso Retraso de compuerta relativo a ZC
Frecuencia de conmutación 1–20 kHz 100–120 Hz (una vez por semiciclo)
Rango de trabajo 0–100% ~10–95% (limitación del TRIAC)



Soluciones



🟢 Para principiantes: DimmerLink — conectar y usar

¿No quieres lidiar con cruce por cero, ángulo de fase e ISR? DimmerLink maneja toda la lógica internamente — solo configura un nivel.

Cualquier ESP32 is a controller with its own zero-cross detector and TRIAC phase control. Your microcontroller only sends a brightness level (0–100%) via I2C or UART. No PWM, no zero-cross on the MCU side.

Cuándo elegirlo:

  • ☐ Solo quieres configurar el brillo sin aprender sobre corte de fase
  • ☐ Raspberry Pi (sin tiempo real para ISR)
  • ☐ ESP32-S2/C3/H2 (núcleo único, ISR no soportado)
  • ☐ Necesitas control simultáneo por WiFi/MQTT sin cuelgues
  • Cableado:

    text
    DimmerLink → Arduino/ESP32
    VCC → 3.3V (ESP32) / 5V (Arduino)
    GND → GND
    SDA → SDA (GPIO 21 en ESP32, A4 en Uno)
    SCL → SCL (GPIO 22 en ESP32, A5 en Uno)

    Código:

    cpp
    // DimmerLink — configurar brillo como un simple valor 0–100
    // Sin PWM, cruce por cero o ISR — todo gestionado dentro de DimmerLink
    // Docs: 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();
        setLevel(0);    // apagado
        delay(2000);
        setLevel(50);   // brillo 50%
        delay(2000);
        setLevel(100);  // brillo máximo
    }
    void loop() {}


    🔵 Para usuarios avanzados: control de fase correcto

    ¿Quieres trabajar con el dimmer directamente sin DimmerLink? Necesitas conectar el cruce por cero y usar una biblioteca.

    Qué necesitas conectar:

    Pin del dimmer Conectar a
    VCC 5V (Arduino) / 3.3V (ESP32)
    GND GND
    ZC Pin con capacidad de interrupción (2 o 3 en Arduino Uno; cualquier GPIO en ESP32)
    DIM Cualquier salida digital

    Diferencia clave con PWM: el pin DIM recibe un único pulso corto (~100 µs) en el momento correcto, calculado en relación al ZC. Sin señal PWM continua.


    Opción A: ESP32 con rbdimmerESP32 ✅ Recomendado

    cpp
    // Plataforma: ESP32 de doble núcleo
    // Biblioteca: rbdimmerESP32 — corte de fase, no PWM
    // https://github.com/robotdyn-dimmer/rbdimmerESP32
    #include "rbdimmerESP32.h"
    #define ZC_PIN  18  // cruce por cero — OBLIGATORIO
    #define DIM_PIN 19  // control TRIAC
    rbdimmer dimmer;
    void setup() {
        dimmer.begin(ZC_PIN, DIM_PIN, 50);  // red 50 Hz
        dimmer.setPower(50);                 // 50% — funciona por corte de fase
    }
    void loop() {
        // Barrido suave de brillo
        for (int p = 10; p <= 95; p++) {
            dimmer.setPower(p);
            delay(30);
        }
        for (int p = 95; p >= 10; p--) {
            dimmer.setPower(p);
            delay(30);
        }
    }

    Qué sucede dentro de la biblioteca:

    text
    1. Se dispara la interrupción ZC (t=0)
       ↓
    2. Calcular retraso de compuerta (borde de ataque, 50 Hz):
       delay_us = (100 - power%) × 78
       Ejemplo: 50% → delay = 50 × 78 = 3 900 µs
       ↓
    3. Timer de hardware: se dispara después de 3 900 µs
       ↓
    4. Enviar pulso corto a DIM (~100 µs)
       ↓
    5. TRIAC se abre y permanece abierto hasta el siguiente ZC
    ⚠️ Rango de trabajo — no 0–100%, sino ~10–95%:
       100% → delay = 0 µs (se abre en ZC, potencia máxima) ✅
         0% → delay = 7 800 µs (78% del semiciclo) → la carga recibe 22%,
               no 0%. Para apagar — no abrir el TRIAC en absoluto.
       < 10% — TRIAC se abre tan tarde que la corriente no puede superar
               la corriente de mantenimiento → parpadeo inestable.
       Las bibliotecas por lo tanto limitan el rango: setPower(0) = apagado,
       setPower(1–9) ≈ 10%, setPower(95–100) ≈ 95%.


    Opción B: Arduino Uno/Mega con RBDdimmer

    cpp
    // Plataforma: Arduino Uno / Mega / Nano (AVR)
    // Biblioteca: RBDdimmer — https://github.com/robotdyn/dimmer
    // ¡ZC ES OBLIGATORIO! Arduino Uno: solo pines 2 o 3
    #include <RBDdimmer.h>
    #define ZC_PIN   2   // cruce por cero — pin 2 (con capacidad de interrupción en Uno)
    #define DIM_PIN  11  // control TRIAC
    dimmerLamp dimmer(DIM_PIN, ZC_PIN);
    void setup() {
        dimmer.begin(NORMAL_MODE, ON);
        dimmer.setPower(50);  // 50%
    }
    void loop() {}


    ⚠️ Errores comunes

    • «Conecté DIM a PWM, no conecté ZC en absoluto»: Sin ZC, el corte de fase es físicamente imposible. El pin ZC es una parte obligatoria del circuito, no opcional.

    • «analogWrite(DIM_PIN, 128) — la lámpara no parpadea, solo está encendida»: Ese es el comportamiento esperado. analogWrite abre el TRIAC con el primer pulso HIGH y permanece enclavado. Use una biblioteca con ZC.

    • «Configuré la frecuencia PWM a 100 Hz — nada cambió»: Igualar la frecuencia PWM a la tasa de ZC no ayuda. El TRIAC se enclava de todos modos con el primer pulso.

    • «PWM funcionó con carga DC — ¿por qué no con AC?»: Las cargas DC (tiras LED 12V, motores DC) usan control por transistor y responden al voltaje promedio. El TRIAC AC es una física diferente. Vea la tabla anterior.

    • «¿Puedo dejar DIM en PWM solo para encender/apagar?»: Técnicamente sí — puedes encender/apagar la carga con un simple HIGH/LOW. Pero eso no es atenuación. Y sin ZC, el TRIAC puede dispararse en un punto aleatorio de la onda sinusoidal, causando interferencias y estrés en el TRIAC.




    Verificación rápida

  • ☐ ¿Pin ZC conectado a un GPIO con capacidad de interrupción? Sin esto, nada funciona.
  • ☐ ¿Estás usando una biblioteca con cruce por cero (rbdimmerESP32 / RBDdimmer / DimmerLink)?
  • ☐ ¿Eliminaste `analogWrite()` / `ledc` del pin DIM?
  • ☐ Arduino Uno: ¿ZC en pin 2 o 3 (no 4, 5 u otros)?
  • ☐ ¿ESP32 de núcleo único (S2/C3/H2)? → Usa DimmerLink.


  • Problemas relacionados

    • Cruce por cero no detectadotroubleshooting/zero-cross-detection-errors.md
    • ESP32 + TRIAC: Guru Meditation Errortroubleshooting/esp32-iram-attr.md
    • Trailing vs Leading Edgeload-types/trailing-vs-leading-edge.md



    ¿Todavía tienes preguntas?

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

    Compartir esta publicación
    Iniciar sesión para dejar un comentario