Zum Inhalt springen

Nulldurchgang wird nicht erkannt: AC-Dimmer reagiert nicht auf Befehle

AC-Dimmer auf voller Helligkeit oder keine Reaktion? Nulldurchgangserkennung ist die häufigste Ursache — falscher Interrupt-Pin, keine Stromversorgung der ZC-Schaltung oder Störungen. 5-Schritte-Diagnose mit Testcode und Lösung für Arduino und ESP32.

Kurzfassung: Ein AC-Dimmer kann die Last nicht regeln, wenn der Mikrocontroller das Nulldurchgangssignal (den Nulldurchgang der Sinuswelle) nicht empfängt. Ohne dieses Signal ist es unmöglich, den TRIAC-Zündzeitpunkt zu berechnen. Ursachen: falscher Pin (nicht interrupt-fähig), fehlerhafte ZC-Verdrahtung, Störungen auf der Signalleitung oder ein Softwarefehler (falscher Interrupt-Modus). Arbeiten Sie die Checkliste unten Schritt für Schritt ab.



Problembeschreibung

Sie haben einen AC-Dimmer angeschlossen, Beispielcode hochgeladen — aber die Last reagiert nicht oder ist immer auf voller Helligkeit. Die Verdrahtung sieht korrekt aus, keine Kompilierungsfehler.

Die häufigste Ursache ist, dass der Nulldurchgang vom Mikrocontroller nicht erkannt wird. Phasenanschnitt-Dimmung funktioniert nach diesem Prinzip: Nulldurchgang der Sinuswelle erkennen → berechnete Verzögerung abwarten → TRIAC zünden. Ohne Nulldurchgang gibt es kein Timing, und der Dimmer schaltet die Last entweder gar nicht ein oder hält sie bei 100% ohne Regelung.

Typische Symptome:

  • Last ist immer auf voller Helligkeit (TRIAC immer offen)
  • Last schaltet sich gar nicht ein
  • setPower(50) hat keine Wirkung — Last ist entweder voll an oder aus
  • Nulldurchgangszähler erhöht sich nicht im seriellen Monitor (bei Debug-Ausgabe)
  • Code kompiliert fehlerfrei, aber der Dimmer „funktioniert nicht"

Typische Forenbeiträge:

  • „Dimmer dimmt nicht — Lampe immer volle Helligkeit"
  • „setPower hat keine Wirkung"
  • „Nulldurchgang-Interrupt löst nicht aus"
  • „Dimmer funktioniert manchmal, zufällig"



Grundursache

Das Nulldurchgangssignal wird von einer Nulldurchgangsdetektorschaltung erzeugt, die in den AC-Dimmer eingebaut oder extern angeschlossen ist. Typischerweise ist es ein Optokoppler + Widerstandsteiler, der bei jedem Nulldurchgang der Sinuswelle einen kurzen Impuls erzeugt.

Impulsfrequenz: 100 Hz (50-Hz-Netz) / 120 Hz (60-Hz-Netz). Ein Impuls pro Halbzyklus.

Um diese Impulse zu empfangen, verwendet der Mikrocontroller einen externen Interrupt (attachInterrupt()). Wenn der Interrupt nicht korrekt konfiguriert ist oder das Signal nicht den richtigen Pin erreicht — wird der ISR-Handler nie aufgerufen, das Timing kann nicht berechnet werden, und der Dimmer funktioniert nicht.

Hauptursachen für den Fehler:

  1. Falscher Pin (nicht interrupt-fähig) — häufigster Fehler bei Arduino
  2. Fehlerhafte ZC-Verdrahtung — Signal erreicht den Pin nicht
  3. Falscher Interrupt-Modus (FALLING statt RISING oder umgekehrt — abhängig von der Schaltung)
  4. Störungen — mehrfache Fehlauslösungen pro Halbzyklus
  5. Stromversorgungsproblem der ZC-Schaltung — kein VCC am Nulldurchgangsdetektor



Lösungen



🟢 Für Einsteiger: DimmerLink — Nulldurchgang integriert

Sie möchten sich nicht mit Interrupt-Pins, RISING/FALLING und elektrischen Störungen beschäftigen? DimmerLink hat einen eigenen Nulldurchgangsdetektor und steuert den TRIAC autonom.

Jeder 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.

Wann diese Lösung wählen:

  • ☐ Können mit Interrupt-Pins auf Arduino nicht umgehen
  • ☐ Verwenden Raspberry Pi (keine Echtzeit für Interrupts)
  • ☐ Verwenden ESP32-S2/C3/H2 (Single-Core, ISR funktioniert nicht)
  • ☐ Möchten eine zuverlässige Lösung ohne ZC-Schaltungsdebugging
  • DimmerLink → Arduino/ESP32 Verdrahtung:

    • VCC → 3.3V (ESP32) oder 5V (Arduino)
    • GND → GND
    • SDA → SDA (GPIO 21 auf ESP32, A4 auf Arduino Uno)
    • SCL → SCL (GPIO 22 auf ESP32, A5 auf Arduino Uno)

    Code:

    cpp
    // DimmerLink — Nulldurchgangsdetektor und TRIAC-Steuerung im Modul
    // Mikrocontroller setzt nur die Helligkeit
    // Dokumentation: 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();        // Standard-SDA/SCL für Ihr Board
        setLevel(50);        // 50% Helligkeit
    }
    void loop() {
        // Kein ISR, keine Interrupts, kein Nulldurchgang am MCU
    }

    Ergebnis: Dimmer funktioniert ohne Abhängigkeit von Interrupt-Pins oder Nulldurchgangssignal am Mikrocontroller.



    🔵 Für Fortgeschrittene: ZC diagnostizieren und beheben

    Sie möchten die direkte ISR-Verbindung beibehalten? Folgen Sie den Diagnoseschritten.


    Diagnose: Schritte 1–5

    Schritt 1: Prüfen Sie, ob der Pin interrupt-fähig ist

    Dies ist Ursache Nr. 1 bei Arduino.

    cpp
    // Arduino Uno / Nano / Mini:
    // ✅ Interrupt-fähig: NUR Pins 2 und 3
    // ❌ Alle anderen Pins (4, 5, 6...) — Interrupts nicht unterstützt
    // Richtig:
    attachInterrupt(digitalPinToInterrupt(2), zeroCrossISR, RISING);  // Pin 2 ✅
    attachInterrupt(digitalPinToInterrupt(3), zeroCrossISR, RISING);  // Pin 3 ✅
    // Falsch:
    attachInterrupt(4, zeroCrossISR, RISING);  // ❌ Pin 4 — nicht interrupt-fähig
    // Auf dem Uno verweist attachInterrupt(4) auf INT4, der nicht existiert

    Auf ESP32 — jeder GPIO unterstützt Interrupts:

    cpp
    // ESP32: jeder GPIO — alle funktionieren
    attachInterrupt(digitalPinToInterrupt(18), zeroCrossISR, RISING);  // ✅
    attachInterrupt(digitalPinToInterrupt(34), zeroCrossISR, RISING);  // ✅

    Auf ESP8266 — alle GPIOs außer GPIO 16 unterstützen Interrupts; empfohlen werden 4, 5, 12, 13, 14 (keine Boot-Modus-Abhängigkeiten).


    Schritt 2: Zähler zur Überprüfung hinzufügen

    Der einfachste Test — Impulse über 1 Sekunde zählen:

    cpp
    // Nulldurchgangsdiagnose: erwartet ~100 Impulse/Sek. (50-Hz-Netz)
    // oder ~120 Impulse/Sek. (60-Hz-Netz)
    #define ZC_PIN 2  // ← stellen Sie sicher, dass dies ein interrupt-fähiger Pin ist
    volatile uint32_t zcCount = 0;
    #ifdef ESP32
    void IRAM_ATTR zeroCrossISR() {  // IRAM_ATTR auf ESP32 erforderlich
    #else
    void zeroCrossISR() {             // Arduino/ESP8266: IRAM_ATTR nicht nötig
    #endif
        zcCount++;
    }
    void setup() {
        Serial.begin(115200);
        pinMode(ZC_PIN, INPUT);
        attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
    }
    void loop() {
        delay(1000);
        Serial.print("ZC-Impulse pro Sekunde: ");
        Serial.println(zcCount);
        zcCount = 0;
        // Erwartete Ergebnisse:
        // ~100 — 50-Hz-Netz, alles in Ordnung
        // ~120 — 60-Hz-Netz, alles in Ordnung
        //   0  — ZC nicht erkannt (Verdrahtungs- oder Pin-Problem)
        //  >200 — Störungen, Fehlauslösungen (RC-Filter-Problem)
    }

    Schritt 3: ZC-Verdrahtung prüfen

    Typische RBDimmer → Arduino/ESP32 Verdrahtung:

    text
    RBDimmer  →  Arduino/ESP32
    -------      -------------
    VCC       →  5V (Arduino) / 3.3V (ESP32)
    GND       →  GND
    ZC        →  Interrupt-fähiger Pin (2 oder 3 bei Arduino Uno)
    DIM       →  Beliebiger digitaler Ausgang

    Checkliste:

  • ☐ ZC-Pin mit dem richtigen interrupt-fähigen GPIO verbunden
  • ☐ VCC am Modul angeschlossen (ohne Strom funktioniert die ZC-Schaltung nicht)
  • ☐ Gemeinsames GND — sowohl Logik- als auch Leistungsteil
  • ☐ ZC-Kabel verläuft nicht parallel zu 220V-Leitungen (induziert Störungen)
  • ☐ ZC-Stecker nicht mit DIM vertauscht (bei einigen Modulen nebeneinander)

  • Schritt 4: Interrupt-Modus prüfen (RISING/FALLING/CHANGE)

    Der Modus hängt von der ZC-Detektorschaltung Ihres Moduls ab:

    cpp
    // Die meisten RBDimmer-Module: RISING (Impuls bei steigender Flanke)
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
    // Einige Schaltungen mit invertierendem Optokoppler: FALLING
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, FALLING);
    // Wenn unsicher — versuchen Sie CHANGE (fängt beide Flanken):
    // Hinweis: mit CHANGE zeigt der Zähler ~200 Hz statt 100 Hz
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, CHANGE);

    Verwenden Sie ein Oszilloskop oder einen Logikanalysator, um die ZC-Signalform zu prüfen — bestimmen Sie, ob Sie die steigende oder fallende Flanke erfassen müssen.


    Schritt 5: Störungen filtern

    Wenn der Zähler >200 zeigt (bei 50-Hz-Netz) — Fehlauslösungen durch Störungen. Dies ist besonders häufig bei langen Kabeln oder Nähe zur Last:

    cpp
    // Software-Entprellung für ZC: Impulse zu nahe am vorherigen ignorieren
    // Halbzyklusperiode bei 50 Hz = 10 000 µs → alles unter 8 000 µs filtern
    volatile uint32_t lastZC = 0;
    void IRAM_ATTR zeroCrossISR() {
        uint32_t now = micros();
        if (now - lastZC > 8000) {  // 8 ms Mindestintervall zwischen ZC-Impulsen
            lastZC = now;
            // Ihre TRIAC-Timing-Logik hier
        }
    }

    Hardware-Filter: Ein RC-Filter auf der ZC-Leitung (1 kΩ Widerstand + 100 nF Kondensator zwischen ZC und GND) entfernt Hochfrequenzstörungen.



    Lösung: Wählen Sie Ihre Option

    Option A: rbdimmerESP32 auf ESP32 ✅

    Wann: Dual-Core-ESP32 mit direkter Dimmerverbindung. Die Bibliothek übernimmt ZC und Timing automatisch:

    cpp
    // Plattform: Dual-Core-ESP32
    // Bibliothek: rbdimmerESP32
    #include "rbdimmerESP32.h"
    #define ZC_PIN  18  // beliebiger ESP32-GPIO
    #define DIM_PIN 19  // beliebiger ESP32-GPIO
    rbdimmer dimmer;
    void setup() {
        Serial.begin(115200);
        dimmer.begin(ZC_PIN, DIM_PIN, 50);  // 50-Hz-Netz
        dimmer.setPower(50);
        Serial.println("Dimmer initialisiert");
    }
    void loop() {}

    Häufige Fehler:

    • ZC_PIN und DIM_PIN in dimmer.begin() vertauscht — prüfen Sie, welcher Pin zur Last und welcher zum Nulldurchgang geht.
    • Falsche Netzfrequenz (dritter Parameter) — 50 oder 60 Hz.

    Option B: Arduino Uno/Mega mit RBDdimmer
    cpp
    // Plattform: Arduino Uno / Mega / Nano (AVR)
    // Bibliothek: RBDdimmer — https://github.com/robotdyn/dimmer
    // WARNUNG: Für ESP32 verwenden Sie rbdimmerESP32, nicht diese Bibliothek!
    #include <RBDdimmer.h>
    // ⚠️ Arduino Uno: ZC NUR auf Pins 2 oder 3
    #define ZC_PIN   2   // interrupt-fähig ✅
    #define DIM_PIN  11  // beliebiger digitaler Ausgang
    dimmerLamp dimmer(DIM_PIN, ZC_PIN);
    void setup() {
        Serial.begin(9600);
        dimmer.begin(NORMAL_MODE, ON);
        dimmer.setPower(50);  // 50%
        Serial.println("Dimmer bereit");
    }
    void loop() {}


    ⚠️ Häufige Fallstricke

    • „Verwende Pin 4 auf Arduino Uno — funktioniert nicht": Pins 4, 5, 6, ... auf dem Uno unterstützen keine externen Interrupts. Nur Pins 2 und 3. Dies ist der häufigste Anfängerfehler.

    • attachInterrupt(4, ...) kompiliert — sollte funktionieren": Es kompiliert, funktioniert aber nicht. attachInterrupt(4, ...) auf dem Uno verweist auf INT4 (Hardware-Interrupt-Nummer), nicht auf GPIO 4. Verwenden Sie immer digitalPinToInterrupt(pin).

    • „Zähler hinzugefügt — immer 0": Drei mögliche Ursachen: 1. Falscher Pin (nicht interrupt-fähig) — siehe oben 2. Keine Stromversorgung am ZC-Modul 3. ZC-Pin nicht physisch mit dem Board verbunden

    • „Zähler zeigt 300–400 statt 100": Fehlauslösungen durch Störungen. Software-Entprellung hinzufügen (siehe oben) oder Hardware-RC-Filter.

    • „Pin 34 auf ESP32 funktioniert nicht für ZC": GPIO 34–39 auf ESP32 sind nur Eingang — sie unterstützen Interrupts. Aber sie haben keinen internen Pull-up/Pull-down. Fügen Sie einen externen 10 kΩ Widerstand an 3,3V hinzu.

    • „Funktioniert ohne Last, mit Last bricht es ab": Elektrische Störungen von der Last (besonders Motoren, Transformatoren) koppeln in die ZC-Leitung ein. Trennen Sie Leistungs- und Signalleitungen physisch.




    Schnell-Check

    Vor dem Posten im Forum überprüfen Sie:

  • ☐ Arduino: ZC-Pin — nur 2 oder 3 (Uno/Nano). Andere funktionieren nicht.
  • ☐ Wird VCC an die ZC-Schaltung des Dimmers versorgt?
  • ☐ Ist der ZC-Pin physisch mit dem Board verbunden (nicht nur DIM)?
  • ☐ Nulldurchgangszähler über 1 Sekunde: ~100 (50 Hz) oder ~120 (60 Hz)?
  • ☐ Zähler `>200`? Störungen — Entprellung oder RC-Filter hinzufügen.
  • ☐ Interrupt-Modus: `RISING` oder `FALLING` für Ihre Schaltung?
  • ☐ ESP32: Verwenden Sie `rbdimmerESP32`, nicht die alte `RBDdimmer`?


  • Interrupt-Pin-Kompatibilitätstabelle

    Platine Interrupt-fähige Pins Hinweis
    Arduino Uno 2, 3 Nur diese zwei
    Arduino Nano 2, 3 Nur diese zwei
    Arduino Mega 2, 3, 18, 19, 20, 21 Sechs Pins
    ❌ nein Alle GPIO (0–39) Außer reservierten
    ESP8266 Alle GPIO außer GPIO 16 Empfohlen 4,5,12,13,14 (kein Boot)
    — (kein ISR) Alle GPIO (über pigpio) Keine Echtzeit — DimmerLink bevorzugen



    Verwandte Probleme

    • ESP32 + AC-Dimmer: Guru Meditation Errortroubleshooting/esp32-iram-attr.md
    • Trailing vs Leading Edgeload-types/trailing-vs-leading-edge.md
    • Dimmer regelt LED nichtload-types/led-lamp-compatibility-triac.md



    Noch Fragen?

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

    Diesen Beitrag teilen
    Anmelden , um einen Kommentar zu hinterlassen