TL;DR: If your ESP32 with an AC dimmer crashes ("Guru Meditation Error" or "Cache disabled but cached memory region accessed"), the cause is that the zero-cross ISR handler is not marked with
IRAM_ATTRand runs from flash memory. When WiFi accesses flash, ESP32 temporarily disables it — the ISR can't execute and the core panics. Fix for ESP32: use the ESP32 Dual-Core library — it handlesIRAM_ATTRautomatically and runs dimmer and WiFi/BLE tasks on separate cores. Note: the library only works on dual-core ESP32 (not the C/S/H series). The oldRBDdimmeron ESP32 requires a manual patch.
Problembeschreibung
Sie haben einen AC-Dimmer an einen ESP32 angeschlossen und der Beispielcode läuft
einwandfrei — stabil ohne WiFi. Aber sobald Sie WiFi aktivieren (WiFi.begin()) oder
aktive Datenübertragung starten (MQTT, HTTP, OTA), friert der ESP32 ein oder startet
mit einem Core Panic neu.
Typisches Muster: Ein Stack-Trace erscheint im Serial-Monitor, dann ein Neustart. Häufigste Auslöser:
- WiFi nach dem Start des Dimmers aktivieren
- Aktives MQTT (häufiges publish/subscribe)
- OTA-Firmware-Update
- Verwendung der alten
RBDdimmer-Bibliothek auf ESP32
Typische Fehlermeldungen:
Guru Meditation Error: Core 0 panic'ed (LoadProhibited)
PC: 0x400xxxxx
Cache disabled but cached memory region accessed
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction)Typische Symptome:
- Dimmer ist stabil ohne WiFi, stürzt innerhalb von Sekunden mit aktivem WiFi ab
- Abstürze geschehen genau während WiFi-Aktivität (MQTT publish, HTTP-Request, OTA)
- Stack-Trace-Adresse nahe
0x400Dxxxx(Flash-Bereich) statt0x400Cxxxx(IRAM-Bereich) - Zufällige Neustarts unter normaler Last
- Abstürze hören auf, wenn
attachInterrupt()für den Nulldurchgang entfernt wird
Grundursache
ESP32 speichert Programmcode im externen SPI-Flash-Speicher. Im Normalbetrieb wird Flash über den CPU-Cache gelesen — schnell und transparent für den Programmierer.
Einige Operationen erfordern jedoch exklusiven Zugriff auf den Flash-Bus:
- Flash-Schreiben/Löschen (NVS, SPIFFS, LittleFS)
- OTA-Update
- WiFi: bestimmte Stack-Operationen (Zertifikate laden, NVS)
- Andere interne ESP-IDF-Operationen
Während dieser Operationen deaktiviert ESP32 vorübergehend den Flash-Cache und
blockiert die Ausführung jedes Codes, der sich im Flash befindet. Wenn ein Interrupt
auslöst (z.B. Nulldurchgang) und sein Handler (ISR) ebenfalls im Flash liegt —
kann der Prozessor den ISR nicht ausführen und wirft eine „Cache disabled but cached
memory region accessed"-Ausnahme.
ESP-IDF-Lösung: ISR-Code muss sich im IRAM (internes RAM, immer
zugänglich) befinden. Setzen Sie das IRAM_ATTR-Attribut vor die Funktionsdeklaration:
void IRAM_ATTR zeroCrossISR() {
// dieser Handler befindet sich im IRAM, nicht im Flash
// wird auch bei deaktiviertem Flash ausgeführt
}Zusätzlich sollte die Dimmer-Task für zuverlässiges Timing auf Core 0 laufen
(xTaskCreatePinnedToCore(..., 0)). rbdimmerESP32 macht dies automatisch.
Problem mit der alten RBDdimmer: Sie wurde geschrieben, bevor die
ESP32-spezifischen Anforderungen allgemein bekannt waren. Ihre ISR-Handler
haben kein IRAM_ATTR und laufen aus dem Flash. Auf ESP32 ohne WiFi
funktioniert das — Flash wird nicht blockiert. Mit aktiviertem WiFi führt
es zu periodischen Abstürzen.
Lösungen
🟢 Für Einsteiger: DimmerLink — ganz ohne ISR
Möchten Sie sich nicht mit IRAM_ATTR, Interrupts und ESP32-Eigenheiten befassen? DimmerLink handhabt Nulldurchgang und TRIAC-Steuerung intern — Ihr ESP32 sendet nur einen Helligkeitslevel-Befehl. Funktioniert mit jedem ESP32-Modell.
Jeder ESP32 is a separate microcontroller that manages the AC dimmer via I2C or UART. In this setup the ESP32 doesn't handle zero-cross interrupts at all — it simply sends a level value. IRAM_ATTR crashes are impossible.
Wann diese Lösung wählen:
ISR-Bibliothek wird unterstützt)
Verdrahtung:
- DimmerLink → ESP32: SDA → GPIO 21, SCL → GPIO 22, VCC → 3.3V, GND → GND
- DimmerLink → AC-Dimmer: gemäß DimmerLink-Anschlussdiagramm
Code (I2C, empfohlen):
// DimmerLink — AC-Dimmer-Steuerung über I2C
// ESP32 verwendet keine Interrupts — kein Konflikt mit WiFi
// Doku: https://www.rbdimmer.com/docs/dimmerlink-I2CCommunication
#include <Wire.h>
#include <WiFi.h>
#include <PubSubClient.h> // Beispiel: MQTT + Dimmer ohne Abstürze
#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(21, 22); // SDA, SCL
WiFi.begin("ssid", "password");
setLevel(50); // 50% Helligkeit sofort
}
void loop() {
// MQTT, HTTP, OTA — alles funktioniert ohne Konflikte mit dem Dimmer
}Ergebnis: Dimmer + WiFi + MQTT funktionieren ohne Abstürze. Kein IRAM_ATTR, keine Interrupts auf dem ESP32.
🔵 Für Fortgeschrittene: richtige Bibliothek für ESP32
Möchten Sie ISR direkt auf ESP32 ohne DimmerLink verwenden? Sie brauchen die richtige Bibliothek.
Option A: rbdimmerESP32 ✅ Empfohlen
When to use: dual-core ESP32 (standard ESP32, not S2/C3/H2) Library: ESP32 Dual-Core
rbdimmerESP32 wurde speziell für ESP32 geschrieben: sie platziert den ISR
automatisch im IRAM und bindet die Dimmer-Task an Core 0.
Kein manuelles IRAM_ATTR nötig.
// Plattform: Dual-Core ESP32
// Bibliothek: rbdimmerESP32 — https://github.com/robotdyn-dimmer/rbdimmerESP32
// IRAM_ATTR wird automatisch innerhalb der Bibliothek angewendet
#include "rbdimmerESP32.h"
#include <WiFi.h>
#define ZC_PIN 18 // jeder GPIO am ESP32
#define DIM_PIN 19 // jeder GPIO am ESP32
rbdimmer dimmer;
void setup() {
Serial.begin(115200);
dimmer.begin(ZC_PIN, DIM_PIN, 50); // Netz 50 Hz
dimmer.setPower(50); // 50% Helligkeit
// WiFi kann nach oder vor der Dimmer-Initialisierung gestartet werden —
// die Reihenfolge ist mit rbdimmerESP32 nicht kritisch, aber gute Praxis
WiFi.begin("ssid", "password");
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi verbunden, Dimmer stabil");
}
void loop() {
// Sanfter Helligkeitsverlauf — keine Abstürze bei aktivem WiFi
for (int p = 10; p <= 95; p++) {
dimmer.setPower(p);
delay(30);
}
for (int p = 95; p >= 10; p--) {
dimmer.setPower(p);
delay(30);
}
}Erwartetes Ergebnis: Dimmer läuft stabil mit aktivem WiFi, MQTT, HTTP — keine Abstürze. WiFi und Dimmer arbeiten parallel ohne Konflikte.
Häufige Fehler:
- Bibliothek nicht installiert:
rbdimmerESP32ist eine separate Bibliothek — verwechseln Sie sie nicht mit der altenRBDdimmer. Installation über GitHub oder Arduino Library Manager. - Verwendung auf ESP32-S2/C3/H2: Funktioniert nicht. Single-Core ESP32-Varianten unterstützen ISR-Dimming mit keiner Bibliothek — verwenden Sie DimmerLink.
- Falsche Netzfrequenz:
dimmer.begin(ZC_PIN, DIM_PIN, 50)— der dritte Parameter ist die Netzfrequenz (50 oder 60 Hz). Falsche Frequenz = falsches Phasenanschnitt-Timing.
Option B: manueller Patch für die alte RBDdimmer
Wann verwenden: Sie nutzen bereits die alte RBDdimmer und möchten
noch nicht migrieren.
Dies ist eine temporäre Lösung. Migration zu rbdimmerESP32 wird empfohlen.
Suchen Sie die Quelldatei RBDdimmer.cpp (oder .h), finden Sie die
Nulldurchgangs-Interrupt-Handler-Funktion und fügen Sie IRAM_ATTR hinzu:
// In RBDdimmer.cpp — ISR finden und IRAM_ATTR hinzufügen:
// VORHER (falsch für ESP32):
void zeroCrossISR() {
// ...
}
// NACHHER (korrekt):
void IRAM_ATTR zeroCrossISR() {
// ...
}Wenn der ISR als Lambda oder Callback an attachInterrupt() übergeben wird,
stellen Sie sicher, dass der Callback ebenfalls IRAM_ATTR hat. In RBDdimmer
gibt es normalerweise einen Haupt-ISR — finden Sie ihn durch Suche nach
attachInterrupt oder RISING.
Wichtig: Der Patch geht bei einem Bibliotheks-Update verloren. Forken Sie
das Repository oder migrieren Sie zu rbdimmerESP32.
Option C: eigene Implementierung mit IRAM_ATTR
⚠️ Nur zu Lehrzwecken — nicht für den Produktiveinsatz. Zwei absichtliche Vereinfachungen im folgenden Code:
delayMicroseconds()im ISR blockiert andere Interrupts.- Die Timer-API ist für ESP32 Arduino Core 2.x geschrieben — kompiliert nicht auf Core 3.x (neue API:
timerBegin(1000000)).Für den Produktiveinsatz verwenden Sie
rbdimmerESP32(Option A).
Wann verwenden: Sie möchten das Prinzip verstehen, bevor Sie eine eigene Implementierung schreiben.
// ⚠️ NUR ZU LEHRZWECKEN — lesen Sie die Warnung oben
// Plattform: Dual-Core ESP32, Arduino Core 2.x
// IRAM_ATTR ist erforderlich für ALLE Funktionen, die aus einem ISR aufgerufen werden
#define ZC_PIN 18
#define DIM_PIN 19
volatile int brightness = 50;
volatile bool fired = false;
hw_timer_t *timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
if (!fired) {
// ⚠️ delayMicroseconds() im ISR — nur zur Demonstration
// Im Produktivbetrieb: zweiten Timer verwenden, um das TRIAC-Gate zu schließen
digitalWrite(DIM_PIN, HIGH);
delayMicroseconds(100);
digitalWrite(DIM_PIN, LOW);
fired = true;
}
portEXIT_CRITICAL_ISR(&timerMux);
}
void IRAM_ATTR zeroCrossISR() {
portENTER_CRITICAL_ISR(&timerMux);
fired = false;
uint32_t delay_us = (100 - brightness) * 78; // 0–7800 µs
// ⚠️ Core-2.x-API — auf Core 3.x timerAlarm() verwenden
timerAlarmWrite(timer, delay_us, false);
timerAlarmEnable(timer);
portEXIT_CRITICAL_ISR(&timerMux);
}
void setup() {
pinMode(DIM_PIN, OUTPUT);
// ⚠️ Core 2.x: timerBegin(num, divider, countUp)
// Core 3.x: timer = timerBegin(1000000);
timer = timerBegin(0, 80, true); // 1-MHz-Takt
timerAttachInterrupt(timer, &onTimer, true);
attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossISR, RISING);
}
void loop() {
brightness = 50;
delay(100);
}⚠️ Häufige Fallstricke
-
„Funktioniert ohne WiFi, stürzt mit WiFi ab": Dies ist genau das IRAM_ATTR-Problem. Suchen Sie die Ursache nicht im Dimmer-Code — das Problem ist die ISR-Platzierung. Wechseln Sie zu
rbdimmerESP32. -
„IRAM_ATTR hinzugefügt — jetzt ein anderer Fehler": Wenn der ISR Funktionen ohne
IRAM_ATTRaufruft (z.B.Serial.print()), verursachen diese den Absturz. Alle vom ISR aufgerufenen Funktionen müssen ebenfallsIRAM_ATTRhaben oder sich im IRAM befinden. -
„Hat funktioniert, nach SDK-Update kaputt": Nach dem Update des ESP32 Arduino Core oder ESP-IDF wurden die IRAM_ATTR-Anforderungen strenger. Wechseln Sie zu
rbdimmerESP32— sie verfolgt die Kompatibilität mit neuen SDK-Versionen. -
„ESP32-S2 ausprobiert — rbdimmerESP32 funktioniert nicht": ESP32-S2, C3 und H2 sind Single-Core. ISR-Dimming ist auf ihnen mit keiner Bibliothek möglich. Die einzige Lösung ist DimmerLink.
-
„
portDISABLE_INTERRUPTS()ausprobiert — hat vorübergehend geholfen": Das Deaktivieren von Interrupts stört WiFi und andere Systemfunktionen. Das ist keine Lösung.
Schnell-Check
Vor dem Posten im Forum überprüfen Sie:
rbdimmerESP32? Für ESP32 brauchen Sie die neue.
Für S2/C3/H2 — nur DimmerLink.
IRAM (0x400Cxxxx)?
etc.)?
Kompatibilitätstabelle
| Plattform | Plattform | Funktioniert mit WiFi? | RBDdimmer (alt) |
|---|---|---|---|
| ESP32 | ❌ nein | ⚠️ Abstürze | RBDdimmer + manueller Patch |
| ESP32 | ❌ nein | ✅ ja | rbdimmerESP32 |
| ESP32 Dual-Core | ✅ automatisch | ✅ ja | rbdimmerESP32 |
| ESP32 Dual-Core | — | — | DimmerLink |
| Jeder ESP32 | — (kein ISR) | ✅ ja | rbdimmerESP32 |
| Jeder ESP32 | — (kein ISR) | ✅ ja | rbdimmerESP32 |
| Arduino Uno/Mega | — (nicht nötig) | — (kein WiFi) | — (no WiFi) |
Verwandte Probleme
- Nulldurchgang nicht erkannt →
troubleshooting/zero-cross-detection-errors.md - Trailing vs Leading Edge →
load-types/trailing-vs-leading-edge.md - Trailing vs Leading Edge →
load-types/trailing-vs-leading-edge.md
Noch Fragen?
Ask on forum.rbdimmer.com or open a GitHub Issue.