Ir al contenido

← Guía Arduino y ejemplos | Contenido | Siguiente: Componente ESPHome →

Guía ESP-IDF y ejemplos

Biblioteca universal de dimmer para ESP32. Guía del framework ESP-IDF en C y ejemplos.

Antes de comenzar, lea la descripción general de la biblioteca: Biblioteca universal para ESP32

Info
Descargar la biblioteca desde GitHub: rbdimmerESP32



Instalación


Con CMake y ESP-IDF

  1. Descargue la biblioteca rbdimmerESP32 desde el repositorio GitHub:
bash
git clone https://github.com/your-username/rbdimmerESP32 components/rbdimmer
  1. Configure your project's CMakeLists.txt to include the library:
cmake
# Main project CMakeLists.txt
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(your_project_name)
  1. Add component dependency in your application's CMakeLists.txt:
cmake
# App CMakeLists.txt
idf_component_register(
    SRCS "main.c"
    INCLUDE_DIRS "."
    REQUIRES rbdimmer
)
  1. Cree un archivo CMakeLists.txt en el directorio del componente rbdimmer:
cmake
# components/rbdimmer/CMakeLists.txt
idf_component_register(
    SRCS "rbdimmerESP32.c"
    INCLUDE_DIRS "include"
    REQUIRES driver esp_timer freertos
)



Conexión de hardware

Instrucciones para conectar el dimmer al microcontrolador y la carga AC:

  • Conecte el pin Zero-Cross a cualquier GPIO con funcionalidad ISR. Consulte la documentación de su chip ESP32
  • Conecte el pin Dimmer a cualquier GPIO
  • VCC a 3,3 V (para ESP32, VCC = 3,3 V)
  • GND a GND
Info
Para guías detalladas de conexión de hardware, consulte: Conexión de hardware



Ejemplo básico (ESP-IDF / C)

c
#include 
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "rbdimmerESP32.h"

static const char *TAG = "DIMMER_EXAMPLE";

// Pins
#define ZERO_CROSS_PIN  18   // Zero-Cross pin
#define DIMMER_PIN      19   // Dimming control pin
#define PHASE_NUM       0    // Phase N (0 for single phase)

// Global variables. Dimmer object
rbdimmer_channel_t* dimmer_channel = NULL;

void app_main(void)
{
    ESP_LOGI(TAG, "AC Dimmer Test");

    // Dimmer lib init
    if (rbdimmer_init() != RBDIMMER_OK) {
        ESP_LOGE(TAG, "Failed to initialize AC Dimmer library");
        return;
    }

    // Zero-cross detector and phase registry
    if (rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0) != RBDIMMER_OK) {
        ESP_LOGE(TAG, "Failed to register zero-cross detector");
        return;
    }

    // Dimmer channel. Configuration data structure.
    rbdimmer_config_t config_channel = {
        .gpio_pin = DIMMER_PIN,
        .phase = PHASE_NUM,
        .initial_level = 50,  // Initial dimming level 50%
        .curve_type = RBDIMMER_CURVE_RMS  // Level Curve Selection. RMS-curve
    };

    if (rbdimmer_create_channel(&config_channel, &dimmer_channel) != RBDIMMER_OK) {
        ESP_LOGE(TAG, "Failed to create dimmer channel");
        return;
    }

    ESP_LOGI(TAG, "AC Dimmer initialized successfully");

    // Main loop
    while (1) {
        // dimming from 10% to 90% with step 10
        for (int brightness = 10; brightness <= 90; brightness += 10) {
            ESP_LOGI(TAG, "Setting brightness to %d%%", brightness);
            rbdimmer_set_level(dimmer_channel, brightness);
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        // Smooth transition from current level to 0 level in 5 sec
        ESP_LOGI(TAG, "Smooth transition to 0%%");
        rbdimmer_set_level_transition(dimmer_channel, 0, 5000);
        vTaskDelay(6000 / portTICK_PERIOD_MS); // delay 6 sec

        // Smooth transition from current level (0) to 100 level in 5 sec
        ESP_LOGI(TAG, "Smooth transition to 100%%");
        rbdimmer_set_level_transition(dimmer_channel, 100, 5000);
        vTaskDelay(6000 / portTICK_PERIOD_MS); // delay 6 sec
    }
}



Referencia de API


Funcionamiento de la biblioteca

Preparación:

  1. Inicializar la biblioteca con rbdimmer_init()
  2. Registrar el detector de cruce por cero con rbdimmer_register_zero_cross()
  3. Crear un canal dimmer con rbdimmer_create_channel()

Control de atenuación:

  • Ajustar el nivel de atenuación con rbdimmer_set_level(). El nivel se establece en el rango 0 (apagado) a 100 (encendido)
  • Transición suave con rbdimmer_set_level_transition(). Transición gradual del nivel actual al nivel establecido en un período de tiempo (en milisegundos, 1 s = 1000 ms)
Info
Para una explicación detallada del funcionamiento de los dimmers, visite: Principios de funcionamiento del dimmer AC


Estructuras de datos

rbdimmer_config_t

c
typedef struct {
    uint8_t gpio_pin;                 // Dimmer GPIO
    uint8_t phase;                    // Phase number
    uint8_t initial_level;            // Initial dimming level
    rbdimmer_curve_t curve_type;      // Level Curve type
} rbdimmer_config_t;


Enumeraciones

rbdimmer_curve_t

Tipos de curvas de nivel:

c
typedef enum {
    RBDIMMER_CURVE_LINEAR,      // Linear curve
    RBDIMMER_CURVE_RMS,         // RMS-compensated curve (for incandescent bulbs)
    RBDIMMER_CURVE_LOGARITHMIC  // Logarithmic curve (for dimmable LED)
} rbdimmer_curve_t;

rbdimmer_err_t

Respuestas de las funciones de la biblioteca:

c
typedef enum {
    RBDIMMER_OK = 0,            // Successful execution
    RBDIMMER_ERR_INVALID_ARG,   // Invalid argument
    RBDIMMER_ERR_NO_MEMORY,     // Not enough memory
    RBDIMMER_ERR_NOT_FOUND,     // Object not found
    RBDIMMER_ERR_ALREADY_EXIST, // Object already exists
    RBDIMMER_ERR_TIMER_FAILED,  // Timer initialization error
    RBDIMMER_ERR_GPIO_FAILED    // GPIO initialization error
} rbdimmer_err_t;


Constantes y macros

Constantes en el archivo rbdimmerESP32.h. Puede modificar estos parámetros:

c
#define RBDIMMER_MAX_PHASES 4                 // Maximum number of phases
#define RBDIMMER_MAX_CHANNELS 8               // Maximum number of channels
#define RBDIMMER_DEFAULT_PULSE_WIDTH_US 50    // Pulse width (us)
#define RBDIMMER_MIN_DELAY_US 50              // Minimum delay (us)
Warning
No recomendamos modificar RBDIMMER_DEFAULT_PULSE_WIDTH_US, ya que está relacionado con las características de hardware del dimmer.


Funciones

Inicialización y configuración

c
// Initialize the library
rbdimmer_err_t rbdimmer_init(void);

// Register a zero-cross detector
rbdimmer_err_t rbdimmer_register_zero_cross(uint8_t pin, uint8_t phase, uint16_t frequency);

// Create a dimmer channel
rbdimmer_err_t rbdimmer_create_channel(rbdimmer_config_t* config, rbdimmer_channel_t** channel);

// Set callback function for zero-cross events
rbdimmer_err_t rbdimmer_set_callback(uint8_t phase, void (*callback)(void*), void* user_data);

Control de atenuación

c
// Set dimming level
rbdimmer_err_t rbdimmer_set_level(rbdimmer_channel_t* channel, uint8_t level_percent);

// Set brightness with smooth transition
rbdimmer_err_t rbdimmer_set_level_transition(rbdimmer_channel_t* channel, uint8_t level_percent, uint32_t transition_ms);

// Set brightness curve type
rbdimmer_err_t rbdimmer_set_curve(rbdimmer_channel_t* channel, rbdimmer_curve_t curve_type);

// Activate/deactivate channel
rbdimmer_err_t rbdimmer_set_active(rbdimmer_channel_t* channel, bool active);

Consultas de información

c
// Get current channel brightness
uint8_t rbdimmer_get_level(rbdimmer_channel_t* channel);

// Get measured mains frequency for the specified phase
uint16_t rbdimmer_get_frequency(uint8_t phase);

// Check if channel is active
bool rbdimmer_is_active(rbdimmer_channel_t* channel);

// Get channel curve type
rbdimmer_curve_t rbdimmer_get_curve(rbdimmer_channel_t* channel);

// Get current channel delay
uint32_t rbdimmer_get_delay(rbdimmer_channel_t* channel);



Guía paso a paso


Estructura del proyecto

text
your_project/
├── CMakeLists.txt
├── main/
│   ├── CMakeLists.txt
│   └── main.c
└── components/
    └── rbdimmer/
        ├── CMakeLists.txt
        ├── include/
        │   └── rbdimmer.h
        └── rbdimmerESP32.c


Pasos de implementación

  1. Definir la biblioteca y los pines en el archivo main.c:
c
#include "rbdimmer.h"

// Pins
#define ZERO_CROSS_PIN  18   // Zero-Cross pin
#define DIMMER_PIN      19   // Dimming control pin
#define PHASE_NUM       0    // Phase N (0 for single phase)
  1. Crear el objeto dimmer (uno por cada dimmer):
c
rbdimmer_channel_t* dimmer_channel = NULL;
  1. Inicializar la biblioteca dimmer:
c
rbdimmer_init();
  1. Registrar el detector de cruce por cero y la fase:
c
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
  1. Configurar y crear el canal dimmer:
c
rbdimmer_config_t config_channel = {
    .gpio_pin = DIMMER_PIN,
    .phase = PHASE_NUM,
    .initial_level = 50,  // Initial dimming level 50%
    .curve_type = RBDIMMER_CURVE_RMS  // Level Curve Selection. RMS-curve
};

rbdimmer_create_channel(&config_channel, &dimmer_channel);
  1. Controlar la atenuación:
c
// Set specific level
rbdimmer_set_level(dimmer_channel, level);

// Smooth transition
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);
Tip
La función de transición suave crea una transición dividiéndola en múltiples pasos pequeños mediante una tarea FreeRTOS. Durante la transición, el código principal continúa ejecutándose.



Ejemplos avanzados


Sistemas dimmer multicanal

c
#include 
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "rbdimmer.h"

#define ZERO_CROSS_PIN  18
#define DIMMER_PIN_1    19
#define DIMMER_PIN_2    21
#define PHASE_NUM       0

static const char *TAG = "DIMMER_EXAMPLE";
rbdimmer_channel_t* channel1 = NULL;
rbdimmer_channel_t* channel2 = NULL;

void app_main(void)
{
    // Initialize library
    rbdimmer_init();

    // Register zero-cross detector (one per phase)
    rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);

    // Create first channel (incandescent bulbs)
    rbdimmer_config_t config1 = {
        .gpio_pin = DIMMER_PIN_1,
        .phase = PHASE_NUM,
        .initial_level = 50,
        .curve_type = RBDIMMER_CURVE_RMS
    };
    rbdimmer_create_channel(&config1, &channel1);

    // Create second channel (dimmable LED lighting)
    rbdimmer_config_t config2 = {
        .gpio_pin = DIMMER_PIN_2,
        .phase = PHASE_NUM,
        .initial_level = 50,
        .curve_type = RBDIMMER_CURVE_LOGARITHMIC
    };
    rbdimmer_create_channel(&config2, &channel2);

    // Main control loop
    while (1) {
        // Control channels independently
        rbdimmer_set_level(channel1, 75);
        rbdimmer_set_level(channel2, 25);
        vTaskDelay(2000 / portTICK_PERIOD_MS);

        rbdimmer_set_level(channel1, 25);
        rbdimmer_set_level(channel2, 75);
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}


Uso de funciones callback de interrupción de cruce por cero

c
#include 
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "rbdimmer.h"

#define ZERO_CROSS_PIN  18
#define DIMMER_PIN      19
#define LED_PIN         2  // Built-in LED for zero-cross visualization
#define PHASE_NUM       0

static const char *TAG = "DIMMER_CALLBACK";
rbdimmer_channel_t* dimmer = NULL;
QueueHandle_t zero_cross_queue;

// Simple message for our queue
typedef struct {
    uint32_t timestamp;
} ZeroCrossEvent_t;

// Callback function for zero-cross events
void zero_cross_callback(void* arg)
{
    ZeroCrossEvent_t event;
    event.timestamp = esp_timer_get_time() / 1000; // Current time in ms

    // Send to queue from ISR
    BaseType_t higher_priority_task_woken = pdFALSE;
    xQueueSendFromISR(zero_cross_queue, &event, &higher_priority_task_woken);

    if (higher_priority_task_woken) {
        portYIELD_FROM_ISR();
    }
}

// Task to process zero-cross events
void zero_cross_processing_task(void *pvParameters)
{
    ZeroCrossEvent_t event;

    while (1) {
        if (xQueueReceive(zero_cross_queue, &event, portMAX_DELAY)) {
            // Toggle LED to visualize zero-crossing
            gpio_set_level(LED_PIN, !gpio_get_level(LED_PIN));

            // Additional processing can be done here safely
            ESP_LOGI(TAG, "Zero-cross event at time: %lu ms", event.timestamp);
        }
    }
}

void app_main(void)
{
    // Setup LED
    gpio_reset_pin(LED_PIN);
    gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);

    // Create the queue
    zero_cross_queue = xQueueCreate(10, sizeof(ZeroCrossEvent_t));
    if (zero_cross_queue == NULL) {
        ESP_LOGE(TAG, "Failed to create queue");
        return;
    }

    // Create the task to process zero-cross events
    BaseType_t task_created = xTaskCreate(
        zero_cross_processing_task,
        "ZeroCrossTask",
        2048,
        NULL,
        5,
        NULL
    );

    if (task_created != pdPASS) {
        ESP_LOGE(TAG, "Failed to create task");
        return;
    }

    // Initialize dimmer
    rbdimmer_init();
    rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);

    // Register callback
    rbdimmer_set_callback(PHASE_NUM, zero_cross_callback, NULL);

    // Create dimmer channel
    rbdimmer_config_t config = {
        .gpio_pin = DIMMER_PIN,
        .phase = PHASE_NUM,
        .initial_level = 60,
        .curve_type = RBDIMMER_CURVE_RMS
    };

    rbdimmer_create_channel(&config, &dimmer);
    ESP_LOGI(TAG, "Dimmer with callback initialized");

    // Main loop - print frequency information
    while (1) {
        uint16_t frequency = rbdimmer_get_frequency(PHASE_NUM);
        ESP_LOGI(TAG, "Detected frequency: %u Hz", frequency);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}


Sistemas multifásicos

c
#include 
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "rbdimmer.h"

#define ZERO_CROSS_PIN_PHASE_A  18
#define ZERO_CROSS_PIN_PHASE_B  19
#define ZERO_CROSS_PIN_PHASE_C  21

#define DIMMER_PIN_PHASE_A      22
#define DIMMER_PIN_PHASE_B      23
#define DIMMER_PIN_PHASE_C      25

#define PHASE_A  0
#define PHASE_B  1
#define PHASE_C  2

static const char *TAG = "DIMMER_MULTIPHASE";
rbdimmer_channel_t* channel_a = NULL;
rbdimmer_channel_t* channel_b = NULL;
rbdimmer_channel_t* channel_c = NULL;

void app_main(void)
{
    // Initialize library
    rbdimmer_init();

    // Register zero-cross detectors for each phase
    rbdimmer_register_zero_cross(ZERO_CROSS_PIN_PHASE_A, PHASE_A, 0);
    rbdimmer_register_zero_cross(ZERO_CROSS_PIN_PHASE_B, PHASE_B, 0);
    rbdimmer_register_zero_cross(ZERO_CROSS_PIN_PHASE_C, PHASE_C, 0);

    // Create channels for each phase
    rbdimmer_config_t config_a = {
        .gpio_pin = DIMMER_PIN_PHASE_A,
        .phase = PHASE_A,
        .initial_level = 50,
        .curve_type = RBDIMMER_CURVE_RMS
    };
    rbdimmer_create_channel(&config_a, &channel_a);

    rbdimmer_config_t config_b = {
        .gpio_pin = DIMMER_PIN_PHASE_B,
        .phase = PHASE_B,
        .initial_level = 50,
        .curve_type = RBDIMMER_CURVE_RMS
    };
    rbdimmer_create_channel(&config_b, &channel_b);

    rbdimmer_config_t config_c = {
        .gpio_pin = DIMMER_PIN_PHASE_C,
        .phase = PHASE_C,
        .initial_level = 50,
        .curve_type = RBDIMMER_CURVE_RMS
    };
    rbdimmer_create_channel(&config_c, &channel_c);

    ESP_LOGI(TAG, "Multi-phase dimmer system initialized");

    // Main control loop
    while (1) {
        // Control phases with different levels
        ESP_LOGI(TAG, "Setting phase A: 75%%, phase B: 50%%, phase C: 25%%");
        rbdimmer_set_level(channel_a, 75);
        rbdimmer_set_level(channel_b, 50);
        rbdimmer_set_level(channel_c, 25);
        vTaskDelay(3000 / portTICK_PERIOD_MS);

        ESP_LOGI(TAG, "Setting phase A: 25%%, phase B: 50%%, phase C: 75%%");
        rbdimmer_set_level(channel_a, 25);
        rbdimmer_set_level(channel_b, 50);
        rbdimmer_set_level(channel_c, 75);
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}



Monitoreo del funcionamiento y depuración

c
void print_dimmer_status(rbdimmer_channel_t* channel, uint8_t phase)
{
    ESP_LOGI(TAG, "=== Dimmer Status ===");
    ESP_LOGI(TAG, "Mains frequency: %d Hz", rbdimmer_get_frequency(phase));
    ESP_LOGI(TAG, "Brightness: %d%%", rbdimmer_get_level(channel));
    ESP_LOGI(TAG, "Active: %s", rbdimmer_is_active(channel) ? "Yes" : "No");
    ESP_LOGI(TAG, "Curve type: %d", rbdimmer_get_curve(channel));
    ESP_LOGI(TAG, "Delay: %d us", rbdimmer_get_delay(channel));
    ESP_LOGI(TAG, "====================");
}



Solución de problemas

  • If the dimmer doesn't work correctly, check your hardware connections, especially the zero-cross detector
  • Asegúrese de que el pin de cruce por cero esté conectado a un GPIO que soporte interrupciones
  • Use las funciones ESP_LOG para monitorear el funcionamiento en tiempo real
  • Para sistemas multicanal, asegúrese de que cada canal dimmer tenga un pin GPIO independiente
  • La biblioteca admite la detección automática de frecuencia. Si conoce la frecuencia de red de su región (normalmente 50 Hz o 60 Hz), puede establecerla explícitamente para un mejor rendimiento inicial

← Guía Arduino y ejemplos | Contenido | Siguiente: Componente ESPHome →