First time setup — dimmer doesn’t respond correctly

Just try to test the dimmer, but doesn’t respond correctly:

Board model: Freenove ESP2-WROOM board FNK0090A
Platform: Arduino IDE Version: 2.3.8
Arduino ESP32 espressif migration to 3.x
Dimmertype: Dimmer Module 16/24A with Temperature-Controlled Active Cooling
Library: rbdimmerESP32 library v2.0.0
What you’re trying to control: heater, for this test lamp 60Watt, without using cooling feature

The code:


#include <Arduino.h>
#include "rbdimmerESP32.h"

/** @name Pin Definitions
 *  @brief GPIO pins used for dimmer control
 *  @{
 */
#define ZERO_CROSS_PIN 2  ///< GPIO pin connected to zero-cross detector output
#define DIMMER_PIN 4      ///< GPIO pin connected to dimmer control input
#define PHASE_NUM 0        ///< Phase number (0 for single-phase systems)
/** @} */

/** @name Configuration Constants
 *  @brief Dimmer configuration parameters
 *  @{
 */
#define INITIAL_BRIGHTNESS 3  ///< Initial brightness level in percent (0-100)
#define MAINS_FREQUENCY 0      ///< Mains frequency: 0=auto-detect, 50=50Hz, 60=60Hz
/** @} */

/**
 * @brief Global dimmer channel handle
 * 
 * This pointer holds the reference to our dimmer channel after creation.
 * It's used to control the dimmer throughout the program lifetime.
 */
rbdimmer_channel_t* dimmer = NULL;

/**
 * @brief Arduino setup function
 * 
 * This function runs once at startup and initializes all components:
 * 1. Serial communication for debugging
 * 2. RBDimmer library initialization
 * 3. Zero-cross detector registration
 * 4. Dimmer channel creation and configuration
 * 5. Setting initial brightness level
 */
void setup() {
    // Initialize serial communication for debug output
    Serial.begin(115200);
    
    // Wait for serial port to connect (needed for native USB boards)
    //while (!Serial && millis() < 3000) {
    //    ; // Wait up to 3 seconds for serial connection
    //}
    
    // Print welcome message
    Serial.println("\n=== RBDimmer Basic Example ===");
    Serial.println("Initializing RBDimmer library...");
    
    // Step 1: Initialize the RBDimmer library
    rbdimmer_err_t err = rbdimmer_init();
    if (err != RBDIMMER_OK) {
        Serial.printf("ERROR: Failed to initialize library (error code: %d)\n", err);
        Serial.println("Check your ESP32 board and ensure enough memory is available");
        while (1) {
            delay(1000); // Halt execution on error
        }
    }
    Serial.println("Library initialized successfully");
    
    // Step 2: Register zero-cross detector
    Serial.printf("Registering zero-cross detector on pin %d...\n", ZERO_CROSS_PIN);
    err = rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, MAINS_FREQUENCY);
    if (err != RBDIMMER_OK) {
        Serial.printf("ERROR: Failed to register zero-cross detector (error code: %d)\n", err);
        Serial.println("Check your wiring and ensure the pin supports interrupts");
        while (1) {
            delay(1000); // Halt execution on error
        }
    }
    Serial.println("Zero-cross detector registered");
    
    // Step 3: Configure and create dimmer channel
    Serial.println("Creating dimmer channel...");
    
    /**
     * @brief Dimmer channel configuration
     * 
     * This structure defines all parameters for the dimmer channel:
     * - gpio_pin: Output pin that controls the TRIAC
     * - phase: Which AC phase this dimmer is connected to
     * - initial_level: Starting brightness (we'll override this)
     * - curve_type: RMS curve is best for incandescent bulbs
     */
    rbdimmer_config_t config = {
        .gpio_pin = DIMMER_PIN,
        .phase = PHASE_NUM,
        .initial_level = 0,  // Start with light off
        .curve_type = RBDIMMER_CURVE_RMS  // RMS curve for incandescent bulbs
    };
    
    err = rbdimmer_create_channel(&config, &dimmer);
    if (err != RBDIMMER_OK) {
        Serial.printf("ERROR: Failed to create dimmer channel (error code: %d)\n", err);
        Serial.println("Check your pin configuration and available timers");
        while (1) {
            delay(1000); // Halt execution on error
        }
    }
    Serial.println("Dimmer channel created successfully");
    
    // Step 4: Set brightness level
    Serial.printf("Setting brightness to %d%%...\n", INITIAL_BRIGHTNESS);
    err = rbdimmer_set_level(dimmer, INITIAL_BRIGHTNESS);
    if (err != RBDIMMER_OK) {
        Serial.printf("WARNING: Failed to set brightness (error code: %d)\n", err);
    } else {
        Serial.printf("Dimmer is now running at %d%% brightness\n", INITIAL_BRIGHTNESS);
    }
    
    // Print status information
    Serial.println("\nSetup complete! Dimmer is active.");
    Serial.printf("Dimmer is now running at %d%% brightness\n", INITIAL_BRIGHTNESS);
    
    // Wait a moment for frequency detection
    delay(500);
    
    // Print detected frequency
    uint16_t frequency = rbdimmer_get_frequency(PHASE_NUM);
    if (frequency > 0) {
        Serial.printf("Detected mains frequency: %d Hz\n", frequency);
    } else {
        Serial.println("Frequency detection in progress...");
    }
}

/**
 * @brief Arduino main loop function
 * 
 * In this basic example, the loop function simply maintains the set
 * brightness level. The dimmer operates autonomously using hardware
 * timers and interrupts, so no continuous updates are needed.
 * 
 * The loop prints status information every 5 seconds to show
 * that the system is running.
 */
void loop() {
    // Static variable to track last print time
    static unsigned long lastPrintTime = 0;
    
    // Print status every 5 seconds
    if (millis() - lastPrintTime >= 5000) {
        lastPrintTime = millis();
        
        // Get current dimmer status
        uint8_t currentLevel = rbdimmer_get_level(dimmer);
        uint16_t frequency = rbdimmer_get_frequency(PHASE_NUM);
        bool isActive = rbdimmer_is_active(dimmer);
        
        // Print status information
        Serial.println("\n--- Dimmer Status ---");
        Serial.printf("Brightness: %d%%\n", currentLevel);
        Serial.printf("Frequency: %d Hz\n", frequency);
        Serial.printf("Active: %s\n", isActive ? "Yes" : "No");
        Serial.printf("Uptime: %lu seconds\n", millis() / 1000);
        Serial.println("-------------------");
    }
    
    // Small delay to prevent watchdog issues
    delay(10);
}

/**
 * @example BasicDimmer.ino
 * 
 * This example demonstrates basic AC dimmer control with the rbdimmerESP32
 * library. It's the simplest starting point for understanding how to:
 * 
 * 1. Initialize the library
 * 2. Register a zero-cross detector  
 * 3. Create a dimmer channel
 * 4. Set a fixed brightness level
 * 
 * The dimmer will maintain the set brightness level indefinitely,
 * demonstrating the autonomous operation of the library using
 * hardware resources.
 * 
 * @note This example uses RMS curve type, which is optimal for
 *       resistive loads like incandescent bulbs. For LED loads,
 *       consider using RBDIMMER_CURVE_LOGARITHMIC instead.
 */

The monitor: everything seems normal, but the lamp can not be dimmed totally:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)

configsip: 0, SPIWP:0xee

clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00

mode:DIO, clock div:1

load:0x3fff0030,len:4876

ho 0 tail 12 room 4

load:0x40078000,len:16560

load:0x40080400,len:3500

entry 0x400805b4

=== RBDimmer Basic Example ===

Initializing RBDimmer library…

Library initialized successfully

Registering zero-cross detector on pin 2…

Zero-cross detector registered

Creating dimmer channel…

Dimmer channel created successfully

Setting brightness to 3%…

Dimmer is now running at 3% brightness

Setup complete! Dimmer is active.

Dimmer is now running at 3% brightness

Detected mains frequency: 50 Hz

-– Dimmer Status —

Brightness: 3%

Frequency: 50 Hz

Active: Yes

Uptime: 5 seconds

-------------------

Waveform at 3 %

waveform 1: Z/C, waveform 2: dimmer

This is not correct! It should be appear at the end.

Waveform at 99%or 100% looks correct (can not insert it, as it gives an error!)

What can be the problem?

Hi Willy,

Thank you for the detailed report with oscilloscope waveforms — this makes diagnosis much easier.

The issue is clear: at 3% brightness, the TRIAC gate pulse fires at the beginning of the half-cycle instead of at the end. This means the zero-cross signal is being detected incorrectly.

The most likely cause: GPIO2 is a strapping pin on ESP32-WROOM.

GPIO2 is used internally during the boot process and must be held LOW at boot. Many ESP32 boards (including Freenove) have an onboard LED or pull-up/pull-down resistors connected to GPIO2. This can interfere with the zero-cross detection signal — causing inverted or shifted edge detection, which results in the phase angle being calculated from the wrong reference point.

Solution: Change the zero-cross pin to a different GPIO. Avoid strapping pins (GPIO0, GPIO2, GPIO12, GPIO15). Good choices are:

cpp

#define ZERO_CROSS_PIN 5   // or 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27
#define DIMMER_PIN 4       // this one is fine

Reconnect the ZC wire from the dimmer to the new GPIO pin and test again.

Also, for a heater load you may want to use RBDIMMER_CURVE_LINEAR instead of RBDIMMER_CURVE_RMS, since RMS curve is optimized for incandescent lamp brightness perception, while for a heater you want linear power control.

Let us know if changing the pin resolves the issue.

Hi admin,

thanks for the fast response.
It’s not the pin 2, but I followed your suggestion to use pin 5 for Z/C. But same problem.

It is the RBDIMMER_CURVE_RMS that causes the problem. Changed to RBDIMMER_CURVE_LINEAR and problem solved. I had a wrong perception of the RMS: linear light-increment is not the same as linear power-increment. Maybe, this should be clarified more. I measured for both curves the delay [msec] from rising edge of Z/C to rising edge of Dimmeroutput:

%dimming RMS Linear
0-2 no pulse
3 4.04 9.72
20 3.54 8.04
40 2.82 6.06
60 2.20 4.04
80 1.50 2.02
99-100 0.3 0.14

Quite a difference.

OK, this will do for me. Thanks again,

Willy