TL;DR:
rbdimmerESP32is designed for dual-core ESP32 (original and S3). ESP32-S2, C3, C6, and H2 are single-core. The library tries to isolate the dimmer task on a dedicated core — which is impossible on single-core chips. The ISR competes with the WiFi stack, timing is disrupted, and the result is flickering, random glitches, and unstable brightness. The solution is DimmerLink (hardware ZC + I2C) or switching to a dual-core chip.
Problem Description
You bought an ESP32-C3 or ESP32-S2 — compact, WiFi/BLE capable, affordable.
You connect an AC dimmer directly, use rbdimmerESP32 — and see:
- The load flickers at any brightness level
- Timing is unstable —
setPower(50)gives 30% one moment, 70% the next - Glitches with active WiFi — works better without WiFi, chaos with it
- Code compiles without errors, instability only at runtime
Or the compiler warns about xTaskCreatePinnedToCore with a non-existent core.
Typical forum messages:
- "ESP32-C3 + dimmer — flickering at any power level"
- "rbdimmerESP32 works on ESP32 but not on ESP32-S2"
- "setPower(50) gives random brightness on C3"
- "Works fine without WiFi, breaks as soon as WiFi connects"
Root Cause
Which ESP32 chips are dual-core and which are not
| Chip | Cores | Architecture | Direct ISR dimming |
|---|---|---|---|
| ESP32 (original) | 2 | Xtensa LX6 | ✅ rbdimmerESP32 |
| ESP32-S3 | 2 | Xtensa LX7 | ✅ rbdimmerESP32 |
| ESP32-S2 | 1 | Xtensa LX7 | ⚠️ DimmerLink only |
| ESP32-C3 | 1 | RISC-V | ⚠️ DimmerLink only |
| ESP32-C6 | 1 | RISC-V | ⚠️ DimmerLink only |
| ESP32-H2 | 1 | RISC-V | ⚠️ DimmerLink only |
⚠️ ESP32-C6 note: C6 has an additional LP core (Low Power core), but it's not suitable for ISR dimming — it runs at a reduced clock speed without full FreeRTOS support. The main core is single.
⚠️ Common confusion: ESP32-S3 is dual-core (works). ESP32-S2 is single-core (doesn't work with direct ISR). These are different chips.
Why phase-cut dimming requires core isolation
TRIAC gate timing: from zero-cross to the gate pulse — a delay of 100–9000 µs, accuracy ±10–20 µs. Any delay means wrong brightness.
rbdimmerESP32 achieves this precision on dual-core like this:
- Core 0 — dimmer task (timing loop, ZC handling, TRIAC firing)
- Core 1 — WiFi stack, TCP/IP, user code
They run in parallel and don't block each other.
On single-core (C3, S2, H2) — both threads share one core. The WiFi stack regularly blocks the CPU for 1–5 ms to process packets. During that time the dimmer ISR misses its timing window → the lamp flickers.
WiFi DMA interrupts have a fixed high hardware priority in ESP-IDF —
they are not preempted by user-level IRAM_ATTR ISR on single-core.
portDISABLE_INTERRUPTS() doesn't help either: WiFi DMA runs at NMI level.
This cannot be solved in software.
Solutions
🟢 For everyone: DimmerLink — hardware solution
The only reliable solution for single-core ESP32 with WiFi. DimmerLink detects zero-cross and controls the TRIAC itself — your MCU only sends the brightness level over I2C.
DimmerLink has its own controller. Your ESP32-C3 only sets the brightness — no ISR, no timing-critical code on the MCU side.
Wiring for ESP32-C3:
- VCC → 3.3V
- GND → GND
- SDA → GPIO 8 (default SDA on ESP32-C3)
- SCL → GPIO 9 (default SCL on ESP32-C3)
⚠️ On some ESP32-C3 modules GPIO 8/9 are used by the external flash. If
Wire.begin()doesn't work — specify pins explicitly:Wire.begin(SDA_PIN, SCL_PIN)with free GPIOs (e.g., 1, 10).
Wiring for ESP32-S2:
- SDA → GPIO 3 (verify your board's pinout)
- SCL → GPIO 4
Code (same for all platforms):
// DimmerLink on ESP32-C3 / S2 / H2 — no ISR on the MCU
// Stable operation with active WiFi
#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(); // default SDA/SCL for your board
setLevel(50); // 50% brightness
}
void loop() {
// WiFi can be used freely — the dimmer is unaffected
}Result: Stable operation with WiFi/BLE on any single-core ESP32.
🔵 Want direct ISR — switch to a dual-core ESP32
Need the direct ISR architecture? Use the original ESP32 or S3.
The original ESP32 and ESP32-S3 are dual-core, rbdimmerESP32 works normally:
// ESP32 original / ESP32-S3 — dual-core
// rbdimmerESP32 works as intended
#include "rbdimmerESP32.h"
#define ZC_PIN 18
#define DIM_PIN 19
rbdimmer dimmer;
void setup() {
Serial.begin(115200);
dimmer.begin(ZC_PIN, DIM_PIN, 50); // ZC_PIN, DIM_PIN, 50 Hz
dimmer.setPower(50);
}
void loop() {}When to choose switching the chip:
the project
⚠️ Common pitfalls
-
"ESP32-C3 is cheaper — I'll use it for AC dimming": ESP32-C3 is a great chip for most IoT tasks, but direct TRIAC dimmer control with WiFi is its weak spot. DimmerLink solves this.
-
"Works without WiFi on C3, flickers with WiFi": Classic symptom of single-core ISR conflict. Well-documented behavior. Solution: DimmerLink.
-
"
rbdimmerESP32compiled on C3 — should work": It compiles because Core 0 exists even on single-core chips.xTaskCreatePinnedToCore(..., 0)will create the task, but without isolation from WiFi. Timing is unstable — that's not "working". -
"Using ESPHome / Tasmota on ESP32-C3 — dimmer doesn't work": ESPHome and Tasmota on single-core ESP32 have the same limitations as custom code. The WiFi stack competes with the dimmer ISR. Use DimmerLink with ESPHome via I2C (
i2c:+sensor:or a customcustom_component). -
"ESP32-S3 also has S — is it single-core too?": No. ESP32-S3 is dual-core (like the original ESP32, just LX7). People confuse it with ESP32-S2. Check the chip marking on your board.
-
"I'm using FreeRTOS manually and can pin tasks myself": On single-core
xTaskCreatePinnedToCore(..., 0)creates the task, but the WiFi stack is also on Core 0. AnyvTaskDelay(1)in the dimmer task yields the CPU to WiFi — timing breaks. And WiFi DMA interrupts have a hardware priority above user ISR. This can't be solved in software. DimmerLink is the only reliable solution.
Quick Check
Related Issues
- Wrong library for ESP32 →
troubleshooting/wrong-library-esp32.md - ESP32 + TRIAC: Guru Meditation Error →
troubleshooting/esp32-iram-attr.md - Zero-cross not detected →
troubleshooting/zero-cross-detection-errors.md
Still have questions?
Ask on forum.rbdimmer.com or open a GitHub Issue.