TL;DR: 2CH 8A and 4CH 10A modules share one ZC pin across all channels. Each channel has its own DIM pin. One interrupt call in code covers all channels; brightness is set independently per channel handle. Use
rbdimmerESP32on dual-core ESP32,RBDdimmeron Arduino/ESP8266.
What Multi-Channel Modules Provide
| Module | Channels | Max per channel | Shared |
|---|---|---|---|
| 2CH 8A | 2 | 8A / ~1 300W at 220V | ZC pin, AC-N |
| 4CH 10A | 4 | 10A / ~1 700W at 220V | ZC pin, AC-N |
Each channel controls one AC load independently. The zero-cross detector is shared — one MCU interrupt services all channels.
Wiring
2CH 8A — pin assignments
Module → MCU (ESP32 example)
────────────────────────────────
ZC-OUT → GPIO 4 (interrupt, any GPIO on ESP32)
DIM1 → GPIO 16 (channel 1)
DIM2 → GPIO 17 (channel 2)
VCC → 3.3V
GND → GNDAC side: both channels share the neutral (AC-N). Each channel has its own AC L-IN and AC L-OUT connected in series with its load.
Full wiring diagrams: Hardware Connection Guide
4CH 10A — pin assignments
Module → MCU (ESP32 example)
────────────────────────────────
ZC-OUT → GPIO 4
DIM1 → GPIO 16
DIM2 → GPIO 17
DIM3 → GPIO 18
DIM4 → GPIO 19
VCC → 3.3V
GND → GND⚠️ One ZC pin for all channels. Connect ZC-OUT to a single MCU interrupt pin. In code, register the zero-cross once — it applies to every channel on the module.
Code: ESP32 (rbdimmerESP32)
Use rbdimmerESP32 on dual-core ESP32 and ESP32-S3. It handles
IRAM_ATTR and core pinning automatically — the ISR runs on Core 0
while WiFi runs on Core 1, preventing crashes.
2-channel example
// rbdimmerESP32 — 2 independent channels, shared zero-cross
// Docs: rbdimmer.com/docs/universal-library-for-esp32
#include "rbdimmerESP32.h"
#define ZC_PIN 4
#define DIM1_PIN 16
#define DIM2_PIN 17
rbdimmer_channel_t ch1, ch2;
void setup() {
rbdimmer_init();
rbdimmer_register_zero_cross(ZC_PIN, RBDIMMER_PHASE_DEFAULT);
rbdimmer_config_t cfg1 = {
.gpio_pin = DIM1_PIN,
.phase = RBDIMMER_PHASE_DEFAULT,
.initial_level = 0,
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_config_t cfg2 = {
.gpio_pin = DIM2_PIN,
.phase = RBDIMMER_PHASE_DEFAULT,
.initial_level = 0,
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_create_channel(&cfg1, &ch1);
rbdimmer_create_channel(&cfg2, &ch2);
// Set each channel independently
rbdimmer_set_level(ch1, 80); // Channel 1 → 80%
rbdimmer_set_level(ch2, 40); // Channel 2 → 40%
}
void loop() {
// Change levels at runtime as needed
// rbdimmer_set_level(ch1, newLevel);
}4-channel example
#include "rbdimmerESP32.h"
#define ZC_PIN 4
const uint8_t DIM_PINS[4] = {16, 17, 18, 19};
rbdimmer_channel_t channels[4];
void setup() {
rbdimmer_init();
rbdimmer_register_zero_cross(ZC_PIN, RBDIMMER_PHASE_DEFAULT);
for (int i = 0; i < 4; i++) {
rbdimmer_config_t cfg = {
.gpio_pin = DIM_PINS[i],
.phase = RBDIMMER_PHASE_DEFAULT,
.initial_level = 0,
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_create_channel(&cfg, &channels[i]);
}
// Example: staggered brightness
rbdimmer_set_level(channels[0], 100);
rbdimmer_set_level(channels[1], 75);
rbdimmer_set_level(channels[2], 50);
rbdimmer_set_level(channels[3], 25);
}Dimming curves
| Curve | Constant | Best for |
|---|---|---|
| Linear | RBDIMMER_CURVE_LINEAR |
Heating elements |
| RMS | RBDIMMER_CURVE_RMS |
Incandescent bulbs |
| Logarithmic | RBDIMMER_CURVE_LOGARITHMIC |
Dimmable LED bulbs |
Code: Arduino / ESP8266 (RBDdimmer)
Use RBDdimmer for ATmega-based Arduino boards and ESP8266.
⚠️ Do not use
RBDdimmeron ESP32 with WiFi active — the ISR lacksIRAM_ATTRand causes crashes. UserbdimmerESP32instead. See: Wrong Library: RBDdimmer vs rbdimmerESP32
Arduino Uno/Nano — 2 channels
// RBDdimmer — 2 channels on Arduino Uno/Nano
// ZC pin MUST be pin 2 or pin 3 on Uno/Nano (INT0/INT1)
#include <RBDdimmer.h>
#define ZC_PIN 2 // Hardware interrupt pin
#define DIM1_PIN 5
#define DIM2_PIN 6
// First dimmer registers the zero-cross pin
dimmerLamp dimmer1(DIM1_PIN, ZC_PIN);
// Subsequent dimmers share the ZC signal from dimmer1
dimmerLamp dimmer2(DIM2_PIN);
void setup() {
dimmer1.begin(NORMAL_MODE, ON);
dimmer2.begin(NORMAL_MODE, ON);
dimmer1.setPower(80); // Channel 1 → 80%
dimmer2.setPower(40); // Channel 2 → 40%
}
void loop() {
// No special call needed in NORMAL_MODE
}Arduino Mega — ZC pin options
On the Mega, ZC can be pins 2, 3, 18, 19, 20, or 21. The 4CH module works the same way — declare each channel after the first:
dimmerLamp dimmer1(DIM1_PIN, ZC_PIN); // ZC set here
dimmerLamp dimmer2(DIM2_PIN);
dimmerLamp dimmer3(DIM3_PIN);
dimmerLamp dimmer4(DIM4_PIN);ESP8266 — multi-channel
ESP8266 uses RBDdimmer with the same pattern: the first dimmerLamp
sets the ZC pin; subsequent ones share it automatically.
// RBDdimmer — 2 channels on ESP8266 (NodeMCU)
#include <RBDdimmer.h>
#define ZC_PIN 5 // D1
#define DIM1_PIN 4 // D2
#define DIM2_PIN 14 // D5
dimmerLamp dimmer1(DIM1_PIN, ZC_PIN);
dimmerLamp dimmer2(DIM2_PIN); // shares ZC from dimmer1
void setup() {
dimmer1.begin(NORMAL_MODE, ON);
dimmer2.begin(NORMAL_MODE, ON);
dimmer1.setPower(80);
dimmer2.setPower(40);
}
void loop() {}Runtime Control
Both libraries allow runtime brightness changes:
// rbdimmerESP32
rbdimmer_set_level(ch1, 60);
// With smooth transition (rbdimmerESP32)
// signature: (channel, target_level_pct, duration_ms)
rbdimmer_set_level_transition(ch1, 60, 2000); // → 60% over 2 000 ms
// RBDdimmer
dimmer1.setPower(60);Common Mistakes
| Mistake | Soluzione |
|---|---|
| Separate ZC pins per channel | Use one ZC pin, one registration |
Using RBDdimmer on ESP32+WiFi |
Use rbdimmerESP32 |
| ZC on non-interrupt pin (Uno) | Use pin 2 or 3 on Uno/Nano |
| Overloading a channel | Check module rating: 8A or 10A per ch |
| DIM pins wired in wrong order | Loads swap channels; re-map GPIO |
Related Articles
- Which dimmer to buy → Complete Buyer's Guide
- Wrong library on ESP32 → RBDdimmer vs rbdimmerESP32
- ESP32 crashes with WiFi → IRAM_ATTR Causes and Fix
- TRIAC overheating → AC Dimmer Runs Hot or Burns Out
Still have questions?
Ask on forum.rbdimmer.com or open a GitHub Issue.