Dimmer output clips at 95% — can't reach full brightness

setPower(100) does not give full brightness. Lamp is noticeably dimmer than when connected directly to mains without the module.

Setup:

  • rbdimmer 1CH 8A module (new one after T011 upgrade)
  • Arduino Mega
  • 200W incandescent bulb, 230V

Test: I measured brightness with a lux meter at 30cm distance.

  • Lamp directly in mains socket: 1850 lux
  • Lamp through dimmer at setPower(100): 1720 lux

That is about 7% less. Is this normal or is the module limiting output? I need full brightness for a specific application.

This is expected behavior for any phase-cut dimmer — not a defect in the module.

I measured the ZC signal with my oscilloscope on the same rbdimmer module. The zero-cross detection pulse has a finite width — typically 200-400µs depending on the optocoupler response time. After the ZC pulse, the library needs a minimum delay before firing the TRIAC gate.

At setPower(100), the firing angle is approximately 5-8° after each zero-cross — not 0°. This means the first 3-5% of each half-cycle is always cut off. You cannot fire the TRIAC at the exact zero-cross point because the ZC detection has propagation delay, the MCU needs time to process the interrupt, and firing too close to ZC risks misfiring in the wrong half-cycle.

The result: setPower(100) delivers approximately 95-97% of true uncontrolled AC power. Your 7% measurement aligns with this — some additional loss comes from the TRIAC’s on-state voltage drop (~1.2V for a BTA16).

This is a fundamental limitation of all phase-cut dimmers, not specific to rbdimmer.

Workaround for true 100%: Add a relay or SSR in parallel with the dimmer module. At full brightness, energize the relay to bypass the TRIAC entirely — zero loss. For dimming, de-energize the relay and use the TRIAC as normal. I use this approach in my workshop lighting.

Ola’s explanation is spot on. This is expected behavior and applies to all TRIAC/phase-cut dimmers on the market.

Quick clarification on the numbers:

  • setPower(100) = maximum firing angle the library can achieve safely = ~95-97% of true AC power
  • The max_power parameter in the library does not change this — it sets the upper software limit but cannot override the hardware timing constraint
  • The remaining 3-5% loss is due to ZC detection delay + minimum safe firing time

For applications requiring true 100% output, the relay bypass approach Ola described is the recommended solution. A simple 5V relay module controlled by a spare GPIO works well:

#define RELAY_PIN 7
#define DIMMER_PIN 5
#define ZC_PIN 2

dimmerLamp dimmer(DIMMER_PIN, ZC_PIN);

void setLight(int power) {
  if (power >= 100) {
    dimmer.setState(OFF);          // stop TRIAC firing
    digitalWrite(RELAY_PIN, HIGH); // bypass via relay
  } else {
    digitalWrite(RELAY_PIN, LOW);  // relay off
    dimmer.setState(ON);           // resume dimming
    dimmer.setPower(power);
  }
}

Important: always turn off the TRIAC (setState(OFF)) before energizing the relay, and de-energize the relay before resuming TRIAC dimming. Running both simultaneously can cause current conflicts.

More details on wiring and relay bypass in our documentation:

— rbdimmer support team

Understood. Not a bug — hardware limitation of phase-cut dimming.

The relay bypass is a good solution. For my application I will add an SSR (solid-state relay) in parallel — faster switching and no mechanical wear. I already have a Fotek SSR-40 DA in stock.

Logic will be: setPower < 100 → use TRIAC dimmer, setPower == 100 → switch to SSR bypass.

Thank you Ola for the oscilloscope data and detailed explanation. And rbdimmer team for the code example — the setState(OFF) before relay activation is an important detail I would have missed.