← Biblioteca Universal para ESP32 | Contenidos | Siguiente: - Guía de framework ESP-IDF y ejemplos en C. →
Guía de Arduino y Ejemplos
Antes de comenzar, lea la descripción general de la biblioteca: Biblioteca universal para ESP32
Novedades en v2.0.0
- Arquitectura interna modular -- los internos de la biblioteca se han reorganizado en módulos separados. La API pública no ha cambiado; los sketches existentes se compilan y ejecutan sin modificación.
- Compuerta de ruido de detección de cruce por cero -- elimina los re-disparos falsos de picos TRIAC que anteriormente causaban parpadeo en algunas cargas.
- ISR de dos pasos para sincronización multicanal -- todos los canales en la misma fase ahora se ordenan y se disparan en un único semi-ciclo, eliminando el jitter de temporización entre canales.
- IRAM_ATTR en todas las rutas críticas de temporización -- los manejadores ISR y las funciones relacionadas se colocan en IRAM para evitar fallos de caché flash durante operaciones sensibles al tiempo.
- Cuatro nuevos parámetros Kconfig -- configurables a través de
idf.py menuconfigpara proyectos ESP-IDF. Las compilaciones de Arduino utilizan valores predeterminados en tiempo de compilación (no se requiere acción).
Requisitos
- Arduino ESP32 Core 3.x (probado con 3.0+)
- Arduino IDE 2.x
- Los ejemplos se reestructuran en directorios por sketch para compatibilidad con Arduino IDE 2.x
Instalación de la Biblioteca
Arduino IDE
- Descargue la biblioteca
rbdimmerESP32como un archivo ZIP - En Arduino IDE, seleccione: Sketch > Include Library > Add .ZIP Library
- Seleccione el archivo ZIP descargado
- Reinicie Arduino IDE para completar la instalación
Explicación de la Operación de la Biblioteca
Preparación:
- La biblioteca se inicializa utilizando
rbdimmer_init() - El detector de cruce por cero se registra utilizando
rbdimmer_register_zero_cross() - El canal del regulador se crea utilizando
rbdimmer_create_channel()
Control de Regulación:
- Establecimiento del nivel de regulación con
rbdimmer_set_level(). El nivel de regulación se establece en el rango de 0(APAGADO) ~ 100(ENCENDIDO) - Transición suave del nivel de regulación con
rbdimmer_set_level_transition(). Transición suave desde el nivel actual al nivel establecido durante un período de tiempo (en milisegundos, 1s=1000ms)
Estructuras de Datos
rbdimmer_config_t
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:
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 funciones de la biblioteca:
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:
#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 100 // Minimum delay (us) — raised from 50 in v2.0.0RBDIMMER_DEFAULT_PULSE_WIDTH_US, ya que esto está relacionado con las características de hardware del regulador.Funciones
Inicialización y Configuración
// 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 Regulación
// 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
// 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);Directriz Paso a Paso
Pasos de implementación para inicialización de biblioteca, registro de fase, detector de cruce por cero, canal de regulación y control de regulación:
1. Definir biblioteca y pines
#include "rbdimmerESP32.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)2. Crear objeto regulador
Para cada regulador, cree un objeto:
rbdimmer_channel_t* dimmer_channel = NULL;3. Inicialización de la biblioteca reguladora
La función devuelve un estado:
rbdimmer_init();4. Detector de cruce por cero y registro de fase
La función devuelve un estado:
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);5. Configuración y creación del canal regulador
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);6. Operación de regulación
rbdimmer_set_level(dimmer_channel, level);7. Transiciones suaves de regulación
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);Soluciones
Sistemas Reguladores Multicanal
La biblioteca admite múltiples canales de regulación independientes. El número de canales está limitado en la configuración de la biblioteca en el archivo rbdimmerESP32.h. Cada canal de regulación debe tener un pin de salida separado.
Ejemplo de creación de un sistema de dos canales:
#define ZERO_CROSS_PIN 18
#define DIMMER_PIN_1 19
#define DIMMER_PIN_2 21
#define PHASE_NUM 0
rbdimmer_channel_t* channel1 = NULL;
rbdimmer_channel_t* channel2 = NULL;
void setup() {
// 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);
}
void loop() {
// Control channels independently
rbdimmer_set_level(channel1, 75);
rbdimmer_set_level(channel2, 25);
delay(2000);
rbdimmer_set_level(channel1, 25);
rbdimmer_set_level(channel2, 75);
delay(2000);
}Uso de Funciones de Devolución de Llamada de Interrupción
Las funciones de devolución de llamada le permiten sincronizar su código con eventos de cruce por cero. Esto es útil para tareas que requieren sincronización precisa con la red AC.
Ejemplo de registro y controlador de tarea FreeRTOS:
// Callback function for zero-cross events
void zero_cross_callback(void* arg) {
// process zero-cross events
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Flashing with ZC frequency
// Any code
}
void setup() {
// ... Lib inits ...
// callback-function registry
rbdimmer_set_callback(PHASE_NUM, zero_cross_callback, NULL);
// ... any code ...
}Sistemas Trifásicos
Para sistemas trifásicos, se debe registrar un detector de cruce por cero separado para cada fase:
#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
void setup() {
// Lib init
rbdimmer_init();
// ZC detect registry 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 dimming channels for each phase
// ... dimming code ...
}Monitoreo de Operación
Para depuración, puede usar las funciones integradas de la biblioteca:
void printDimmerStatus(rbdimmer_channel_t* channel) {
Serial.println("=== Dimmer Status ===");
Serial.printf("Mains frequency: %d Hz\n", rbdimmer_get_frequency(0));
Serial.printf("Brightness: %d%%\n", rbdimmer_get_level(channel));
Serial.printf("Active: %s\n", rbdimmer_is_active(channel) ? "Yes" : "No");
Serial.printf("Curve type: %d\n", rbdimmer_get_curve(channel));
Serial.printf("Delay: %d us\n", rbdimmer_get_delay(channel));
Serial.println("====================");
}Solución de Problemas: Parpadeo
v2.0.0 incluye varias correcciones específicas para problemas comunes de parpadeo:
| Síntoma | Causa | Corrección en v2.0.0 |
|---|---|---|
| Parpadeo general / picos aleatorios | El pico de encendido TRIAC vuelve a disparar el detector de cruce por cero | Compuerta de ruido de cruce por cero: ZC_DEBOUNCE_US = 3000 us ventana de blanqueo después de cada cruce por cero, ignorando bordes falsos |
| Parpadeo al 100% de brillo | El retraso de disparo es demasiado cercano al siguiente cruce por cero | MIN_DELAY_US elevado a 100 us; los niveles >= 100% se asignan internamente al 99% para que el TRIAC siempre reciba un pulso de puerta adecuado |
| Parpadeo por debajo del 3% de brillo | El TRIAC no puede sostener la corriente a ángulos de conducción muy pequeños | Los niveles por debajo de LEVEL_MIN se tratan como completamente APAGADOS, evitando disparos parciales no confiables |
| Jitter multicanal / parpadeo | Los canales se disparan con temporizadores independientes causando conflictos de programación | ISR de dos pasos: todos los canales en la misma fase se ordenan por retraso y se disparan secuencialmente en una cadena de temporizador dentro de cada semi-ciclo |
Si sigue experimentando parpadeo después de actualizar, verifique que la señal de hardware de cruce por cero sea limpia (sin zumbido) y que su carga sea compatible con regulación de fase de corte de borde de inicio (TRIAC).
Ejemplos Básicos para la Biblioteca de Regulador AC
Los sketches de ejemplo se encuentran en directorios por sketch bajo examples/arduino/:
| Ejemplo | Ruta |
|---|---|
| Control Básico del Regulador | examples/arduino/BasicDimmer/BasicDimmer.ino |
| Transición de Brillo | examples/arduino/BasicTransition/BasicTransition.ino |
| Múltiples Canales Reguladores | examples/arduino/MultiDimmer/MultiDimmer.ino |
| Devolución de Llamada de Cruce por Cero | examples/arduino/ZCCallBack/ZCCallBack.ino |
Control Básico del Regulador
Descripción: El ejemplo más simple que muestra cómo controlar un regulador AC con un nivel de brillo fijo.
Archivo: examples/arduino/BasicDimmer/BasicDimmer.ino
#include
#include "rbdimmerESP32.h"
#define ZERO_CROSS_PIN 18
#define DIMMER_PIN 19
#define PHASE_NUM 0
rbdimmer_channel_t* dimmer = NULL;
void setup() {
Serial.begin(115200);
delay(1000);
// Initialize the library
rbdimmer_init();
// Register zero-cross detector
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
// Create dimmer channel with RMS curve (best for incandescent bulbs)
rbdimmer_config_t config = {
.gpio_pin = DIMMER_PIN,
.phase = PHASE_NUM,
.initial_level = 50, // Start at 50% brightness
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_create_channel(&config, &dimmer);
Serial.println("Dimmer initialized at 50% brightness");
}
void loop() {
// Nothing needed in the loop - dimmer maintains its state
delay(1000);
} Transición de Brillo
Muestra cómo crear transiciones suaves entre niveles de brillo.
Archivo: examples/arduino/BasicTransition/BasicTransition.ino
#include
#include "rbdimmerESP32.h"
#define ZERO_CROSS_PIN 18
#define DIMMER_PIN 19
#define PHASE_NUM 0
rbdimmer_channel_t* dimmer = NULL;
void setup() {
Serial.begin(115200);
delay(1000);
// Initialize dimmer
rbdimmer_init();
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
rbdimmer_config_t config = {
.gpio_pin = DIMMER_PIN,
.phase = PHASE_NUM,
.initial_level = 0, // Start with light off
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_create_channel(&config, &dimmer);
Serial.println("Dimmer initialized");
}
void loop() {
// Fade up to 100% over 3 seconds
Serial.println("Fading up...");
rbdimmer_set_level_transition(dimmer, 100, 3000);
delay(4000); // Wait for transition + 1 second
// Fade down to 10% over 3 seconds
Serial.println("Fading down...");
rbdimmer_set_level_transition(dimmer, 10, 3000);
delay(4000); // Wait for transition + 1 second
} Múltiples Canales Reguladores
Controla dos canales reguladores separados de forma independiente.
Archivo: examples/arduino/MultiDimmer/MultiDimmer.ino
#include
#include "rbdimmerESP32.h"
#define ZERO_CROSS_PIN 18
#define DIMMER_PIN_1 19
#define DIMMER_PIN_2 21
#define PHASE_NUM 0
rbdimmer_channel_t* dimmer1 = NULL;
rbdimmer_channel_t* dimmer2 = NULL;
void setup() {
Serial.begin(115200);
delay(1000);
// Initialize dimmer library
rbdimmer_init();
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
// Create first dimmer channel (for incandescent bulb)
rbdimmer_config_t config1 = {
.gpio_pin = DIMMER_PIN_1,
.phase = PHASE_NUM,
.initial_level = 50,
.curve_type = RBDIMMER_CURVE_RMS
};
rbdimmer_create_channel(&config1, &dimmer1);
// Create second dimmer channel (for LED light)
rbdimmer_config_t config2 = {
.gpio_pin = DIMMER_PIN_2,
.phase = PHASE_NUM,
.initial_level = 50,
.curve_type = RBDIMMER_CURVE_LOGARITHMIC // Better for LEDs
};
rbdimmer_create_channel(&config2, &dimmer2);
Serial.println("Two dimmer channels initialized");
}
void loop() {
// Alternate level between channels
Serial.println("Channel 1: 80%, Channel 2: 20%");
rbdimmer_set_level(dimmer1, 80);
rbdimmer_set_level(dimmer2, 20);
delay(3000);
Serial.println("Channel 1: 20%, Channel 2: 80%");
rbdimmer_set_level(dimmer1, 20);
rbdimmer_set_level(dimmer2, 80);
delay(3000);
} Devolución de Llamada de Cruce por Cero
Demuestra el uso de una función de devolución de llamada con una tarea FreeRTOS para procesar de forma segura eventos de cruce por cero, que se pueden usar para sincronizar con la forma de onda AC sin agregar retraso de ejecución de código al controlador de interrupción.
Archivo: examples/arduino/ZCCallBack/ZCCallBack.ino
#include
#include "rbdimmerESP32.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#define ZERO_CROSS_PIN 18
#define DIMMER_PIN 19
#define LED_PIN 2 // Onboard LED for zero-cross visualization
#define PHASE_NUM 0
rbdimmer_channel_t* dimmer = NULL;
uint32_t zeroCount = 0;
// FreeRTOS components
QueueHandle_t zeroCrossQueue;
TaskHandle_t zeroCrossTaskHandle;
// Simple message type for our queue
typedef struct {
uint32_t timestamp;
} ZeroCrossEvent_t;
// Callback function for zero-cross events (runs in ISR context)
void zeroCrossCallback(void* arg) {
// Create event
ZeroCrossEvent_t event;
event.timestamp = millis();
// Send to queue from ISR
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(zeroCrossQueue, &event, &xHigherPriorityTaskWoken);
// If a higher priority task was woken, request context switch
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
// Task to process zero-cross events
void zeroCrossProcessingTask(void* parameter) {
ZeroCrossEvent_t event;
// Task loop - will run forever
for (;;) {
// Wait for an item from the queue
if (xQueueReceive(zeroCrossQueue, &event, portMAX_DELAY)) {
// Process the event (now we're in task context, not ISR)
// Toggle LED to visualize zero-crossing
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
// Count zero-crossing events
zeroCount++;
// Additional processing can be done here safely
// This doesn't affect the zero-cross interrupt timing
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
// Setup LED for visual zero-cross indication
pinMode(LED_PIN, OUTPUT);
// Create the queue to send events from ISR to task
zeroCrossQueue = xQueueCreate(10, sizeof(ZeroCrossEvent_t));
if (zeroCrossQueue == NULL) {
Serial.println("Error creating the queue");
while (1); // Stop execution on error
}
// Create the task to process zero-cross events
BaseType_t xReturned = xTaskCreate(
zeroCrossProcessingTask, // Task function
"ZeroCrossTask", // Task name
2048, // Stack size (bytes)
NULL, // No parameters needed
5, // Medium priority
&zeroCrossTaskHandle // Task handle
);
if (xReturned != pdPASS) {
Serial.println("Error creating the task");
while (1); // Stop execution on error
}
// Initialize dimmer
rbdimmer_init();
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
// Register zero-cross callback
rbdimmer_set_callback(PHASE_NUM, zeroCrossCallback, 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);
Serial.println("Dimmer with zero-cross callback and task processing initialized");
}
void loop() {
// Print zero-cross statistics every second
static unsigned long lastPrint = 0;
if (millis() - lastPrint >= 1000) {
uint16_t frequency = rbdimmer_get_frequency(PHASE_NUM);
Serial.printf("Zero-cross count: %u, Detected frequency: %u Hz\n",
zeroCount, frequency);
lastPrint = millis();
}
delay(10);
}
- Mantener el ISR (Interrupt Service Routine) extremadamente corto - solo envía un mensaje a una cola
- Mover toda la lógica de procesamiento a una tarea FreeRTOS dedicada
- Usar mecanismos apropiados de FreeRTOS para una comunicación segura entre ISR y tarea
- Prevenir cualquier problema de temporización en la detección de cruce por cero separando el manejo de interrupciones del procesamiento
Este enfoque sigue mejores prácticas para sistemas en tiempo real donde los manejadores de interrupción deben ser lo más cortos posible para evitar afectar la temporización del sistema y la capacidad de respuesta.
Ver CHANGELOG.md para la lista completa de cambios en cada versión.
← Biblioteca Universal para ESP32 | Contenidos | Siguiente: - Guía de framework ESP-IDF y ejemplos en C. →