Dimmer flickers at low brightness — 10-20% range unstable

I’ve been investigating a flickering issue for a couple of days now and I think I’ve narrowed it down to zero-cross jitter.

My setup: ESP32 DevKit v1, rbdimmer 1CH 4A module, rbdimmerESP32 library, driving a 40W dimmable LED ceiling fixture at 230V/50Hz.

At 50–100% power the light is perfectly stable. But below about 20% it starts flickering — random and irregular. I measured the ZC signal with my oscilloscope:

  • ZC pulse edges have jitter of approximately ±200 µs
  • At 50% power: ±200 µs error = ~4% brightness variation — barely visible
  • At 10% power: same ±200 µs error = ~20% variation — very visible flicker

Small absolute timing errors become large relative errors at low duty cycles. With an incandescent bulb the filament’s thermal inertia hides it, but LEDs respond near-instantaneously so every jitter pulse is visible.

Has anyone found a reliable way to reduce ZC jitter or get stable dimming below 20%?

From a circuit perspective the ZC jitter you’re measuring is likely caused by mains noise coupling into the zero-cross detection circuit on the module.

The typical ZC detector uses an optocoupler with a resistive divider on the AC side. Near the zero crossing the voltage is very low — and that’s exactly where any high-frequency noise on the mains can cause the optocoupler to trigger slightly early or late.

A simple fix that’s worked for me in similar situations: add an RC low-pass filter between the module’s ZC output and your ESP32 GPIO:

Components: 100 Ω resistor + 100 nF ceramic capacitor

Connection:

  • ZC pin → 100 Ω resistor → ESP32 GPIO (interrupt pin)
  • At the ESP32 GPIO side: 100 nF cap to GND

The time constant is: τ = 100 Ω × 100 nF = 10 µs

This should filter out the high-frequency noise spikes without significantly delaying the ZC edge (10 µs delay is negligible compared to the 10 ms half-period). It should reduce your ±200 µs jitter to maybe ±50–80 µs.

It won’t eliminate the problem entirely at very low brightness but it should push the usable range down from 20% to maybe 12–15%.

Thanks Vincent — that makes sense. The mains in my workshop isn’t the cleanest — there’s a welder and a compressor on the same circuit which probably adds significant high-frequency noise.

The RC time constant of 10 µs is nicely chosen — fast enough to not shift the ZC edge meaningfully but slow enough to attenuate noise spikes. I have 100 Ω resistors and 100 nF MLCCs in my parts drawer so I’ll solder up the filter tonight and measure the ZC jitter again with the scope.

Will report back with before/after waveform comparison.

Different approach but possibly complementary to Vincent’s hardware fix:

Software workaround: set a minimum power floor.

If your use case doesn’t need dimming below 25% you can simply tell the library to clamp the minimum:

#include <rbdimmerESP32.h>

rbdimmer dimmer(25, 26);  // outputPin, zcPin

void setup() {
  dimmer.begin();
  dimmer.setMinPower(25);  // never go below 25%
}

void loop() {
  int brightness = map(analogRead(34), 0, 4095, 0, 100);
  dimmer.setPower(brightness);  // library clamps to 25-100 range
}

Here’s my reasoning: if the unstable range is 0–20% and you clamp to 25% minimum, you completely avoid the jitter-sensitive zone. The user sees a smooth dimming curve from 25% to 100%.

I’ve been running this for 3 weeks with no issues on my hallway lights. For most home lighting 25% is already quite dim — nobody notices the missing bottom range.

Obviously this doesn’t fix the root cause so if you need the full 0–100% range you’ll need Vincent’s hardware approach or something else. But for practical everyday use the clamp works great.

Good pragmatic approach Hank. For my living room lights 25% minimum would actually be fine — that’s already dimmer than I’d normally use.

But I also have a project where I need smooth dimming from 5% to 100% for a photography lighting setup. So I need to solve the root cause for that one.

I’ll test both approaches.

Interesting — I hadn’t considered DimmerLink for this. If the timing is fully offloaded to a dedicated MCU then the jitter from the ESP32 side is completely eliminated. That’s the cleanest solution architecturally.

I’ve ordered a DimmerLink module. In the meantime I’ll test the RC filter and min_power approaches tonight and report back.

Test results — all three approaches (40W dimmable LED, 230V/50Hz):

Baseline: ZC jitter ±200 µs. Visible flicker at 10%: YES. At 20%: slight. At 30%+: none.

Fix 1 — RC filter (100 Ω + 100 nF): Jitter reduced to ±80 µs (~60% improvement). Flicker at 10%: reduced but present. At 15%: barely noticeable. At 20%+: none. Verdict: helps but doesn’t fully solve the low end.

Fix 2 — setMinPower(25): Jitter unchanged but you never enter the unstable zone. Visible flicker: NONE. Verdict: perfect workaround if you don’t need <25%.

Fix 3 — DimmerLink: Flicker at 10%: NONE. At 5%: very slight. Smooth 5–100% range. Verdict: best result by far — rock solid.

The DimmerLink is clearly in a different league for low-brightness stability. The dedicated hardware timer makes all the difference.

Great comparison Ola! For anyone finding this later here’s a quick decision guide:

Your Situation Recommended Fix
Home lighting, don’t need <25% setMinPower(25) — simplest, zero cost
Want slightly better range (~15%+) Add RC filter (100 Ω + 100 nF) — ~$0.10 in parts
Need smooth 5–100% (pro lighting, photography) DimmerLink — hardware solution, best results
Incandescent bulbs Probably fine without any fix — filament inertia hides jitter

I’ll stick with my min_power approach for my home setup. But if I ever build that studio lighting controller I’ve been dreaming about — DimmerLink for sure.

[SOLVED] — all three approaches tested and documented above.

My final configuration:

  • Living room lights: setMinPower(25) — simple and effective, no hardware changes needed
  • Photography lighting rig: DimmerLink via I2C — rock solid from 5% to 100%
  • I also kept the RC filter on the photography setup as an extra layer of noise filtering

Thanks to Vincent for the RC filter idea, Hank for the pragmatic software clamp, and rbdimmer team for suggesting DimmerLink. This thread covers pretty much everything you need to know about low-brightness flicker with TRIAC dimmers.