Hello, I have a question about zero-cross detection and the rbdimmer module.
I understand the concept — the microcontroller needs to know when the AC waveform crosses zero volts so it can fire the TRIAC at the correct phase angle. That part makes sense for me.
But I’ve been reading various tutorials online and many of them show an external zero-cross detector circuit using an optocoupler (like the MOC3021 or H11AA1) with a bridge rectifier and current-limiting resistors. Some schematics are quite involved.
My question is: do I need to build this external ZC detection circuit myself, or does the rbdimmer module already include it on the board?
I looked at my module and I see four pins on the low-voltage side: VCC, GND, DIM, and Z-C. I assume the Z-C pin outputs something but I’m not sure if it’s the actual zero-cross signal or if it’s an input where I’m supposed to feed in my own detection circuit.
I’m using an Arduino Mega for now but planning to switch to ESP32 later. Any clarification would be very helpful.
Good question Marcus. Short answer: no, you do not need an external zero-cross circuit. The rbdimmer module has the full ZC detection built into the board.
Here’s what’s happening on the module:
- The AC mains side has an optocoupler-based zero-cross detection circuit already integrated
- Every time the AC waveform crosses zero the circuit generates a short pulse
- That pulse is output on the Z-C pin on the low-voltage header
- You connect Z-C to an interrupt-capable GPIO on your microcontroller
- The RBDDimmer library attaches an ISR (interrupt service routine) to that pin and handles all the timing internally
So the Z-C pin is an output from the module — not an input. You don’t feed anything into it. You just read from it.
I measured the ZC signal with my oscilloscope on one of these modules and it outputs a clean ~5V pulse at every zero crossing. On a 50 Hz mains supply that’s a pulse every 10 ms (100 pulses per second — two per full cycle). If you’re on 60 Hz mains it would be every 8.33 ms (120 pulses per second).
The tutorials you found with external MOC3021 circuits are for building a dimmer from scratch using a bare TRIAC. Since the rbdimmer module is a complete solution you can skip all of that. Just wire Z-C to your interrupt pin and the library does the rest:
#include <RBDdimmer.h>
#define outputPin 5
#define zerocross 2 // must be interrupt-capable!
dimmerLamp dimmer(outputPin, zerocross);
void setup() {
dimmer.begin(NORMAL_MODE, ON); // library attaches ZC interrupt here
}
void loop() {
dimmer.setPower(50); // 50% brightness
}
After dimmer.begin() the library automatically handles the zero-cross interrupt — you don’t need to write your own ISR.
Thank you both — that clears it up completely. I was overthinking this and almost ordered a bag of MOC3021s for nothing!
One follow-up question: I’m in Germany so we have 50 Hz mains. If I later share my project code with someone in the US (60 Hz) — does the library handle the frequency difference automatically or do I need to configure it somewhere?
I assume the timing changes because at 50 Hz the half-period is 10 ms and at 60 Hz it is 8.33 ms. Does the library detect which frequency is present or does it need to be told?
The library handles it automatically. It doesn’t need to be told the mains frequency.
Here’s why: the library doesn’t use a fixed timing table. Instead it measures the actual interval between consecutive zero-cross interrupts in real time. So the flow is:
- ZC interrupt fires → library records timestamp
- Next ZC interrupt fires → library calculates the delta (≈10 ms for 50 Hz or ≈8.33 ms for 60 Hz)
- When you call
setPower(50) the library computes a delay of 50% of that measured half-period
- It fires the TRIAC after that delay
So whether you’re on 50 Hz or 60 Hz — or even if the frequency drifts slightly under heavy grid load — the library adapts automatically because it’s always measuring the real interval.
This means your code is fully portable between 50 Hz and 60 Hz countries without any changes. I’ve tested the same firmware on both 230V/50Hz here in Norway and on a 120V/60Hz bench supply — works identically.
[SOLVED]
Perfect — that is exactly what I wanted to hear. The adaptive timing approach makes a lot of sense from an engineering perspective.
To summarize for anyone finding this thread later:
- No external zero-cross circuit needed — the rbdimmer module has it built in
- The Z-C pin is an output — connect it to an interrupt-capable GPIO on your MCU
- The library handles ZC interrupts and TRIAC timing automatically after
dimmer.begin()
- 50 Hz / 60 Hz is auto-detected — the library measures the real interval between zero crossings so code is portable worldwide
Thank you Ola and rbdimmer team for the clear explanations!