← - Guida e esempi Arduino. | Contenuti | Successivo: Componente ESPHome →
Guida ESP-IDF e Esempi
Libreria universale per dimmer ESP32. Guida e esempi del framework ESP-IDF in C.
Prima di iniziare, leggi la panoramica della libreria: Libreria universale per ESP32
Requisiti e Compatibilità
- Versione minima ESP-IDF: 5.3
- Chip supportati: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6
- La libreria utilizza lo standard
Kconfigper la configurazione della build (rinominato daKconfig.txtnella v2.0.0)
Novità in v2.0.0
La versione 2.0.0 è una riscrittura importante dell'architettura interna. L'API pubblica è completamente compatibile con le versioni precedenti — non sono necessarie modifiche al codice dell'applicazione.
Miglioramenti interni:
- Architettura modulare — il codice è suddiviso in 7 moduli interni (motore di fase, gestore di canali, tabelle di curve, nucleo ISR, ecc.). L'intestazione pubblica singola
rbdimmerESP32.hrimane l'unico include necessario. - Tutti gli ISR utilizzano
IRAM_ATTRe i timer utilizzano il dispatchESP_TIMER_ISRper il timing deterministico sub-microsecondo. - Filtro di rumore zero-cross — una finestra di debounce configurabile rifiuta il rumore elettrico e i falsi trigger sull'ingresso zero-cross. Default: 3000 us.
- ISR a due passate per sincronizzazione multi-canale — quando più canali condividono una fase, l'ISR pre-ordina i canali per ritardo e accende gli impulsi TRIAC in un singolo passaggio consolidato, eliminando il jitter di timing tra i canali.
- Configurazione build
Kconfig— il file è ora denominatoKconfig(convenzione standard ESP-IDF; precedentementeKconfig.txt).
Nuovi parametri Kconfig:
| Parametro | Default | Descrizione |
|---|---|---|
CONFIG_RBDIMMER_ZC_DEBOUNCE_US |
3000 | Finestra di debounce zero-cross in microsecondi |
CONFIG_RBDIMMER_MIN_DELAY_US |
100 | Ritardo minimo di accensione TRIAC in microsecondi |
CONFIG_RBDIMMER_LEVEL_MIN |
3 | Livello di dimming minimo (%). I valori al di sotto di questo vengono trattati come OFF |
CONFIG_RBDIMMER_LEVEL_MAX |
99 | Livello di dimming massimo (%) |
Installazione
Utilizzo di CMake con ESP-IDF
- Scarica la libreria
rbdimmerESP32dal repository GitHub:
git clone https://github.com/your-username/rbdimmerESP32 components/rbdimmer- Configure your project's
CMakeLists.txtto include the library:
# Main project CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(your_project_name)- Add component dependency in your application's
CMakeLists.txt:
# App CMakeLists.txt
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES rbdimmer
)- The library's
CMakeLists.txtandKconfigare included automatically when placed incomponents/rbdimmer/. The componentCMakeLists.txtregisters sources, includes, and dependencies:
# components/rbdimmer/CMakeLists.txt
idf_component_register(
SRCS "rbdimmerESP32.c"
INCLUDE_DIRS "include"
REQUIRES driver esp_timer freertos
)Kconfig (convenzione standard ESP-IDF). Se stai effettuando l'upgrade da una versione precedente che utilizzava Kconfig.txt, rinominalo in Kconfig.Configurazione Kconfig
La libreria espone i parametri di tuning attraverso il sistema ESP-IDF menuconfig. Per modificarli:
idf.py menuconfig
# Navigate to: Component config → RBDimmer ConfigurationOpzioni disponibili:
- Zero-Cross Debounce (us) —
CONFIG_RBDIMMER_ZC_DEBOUNCE_US(default 3000). Aumenta se vedi falsi trigger zero-cross dal rumore elettrico. - Ritardo TRIAC Minimo (us) —
CONFIG_RBDIMMER_MIN_DELAY_US(default 100). Impedisce al TRIAC di accendersi troppo vicino allo zero-cross, che può causare sfarfallio ad alta luminosità. - Livello Min (%) —
CONFIG_RBDIMMER_LEVEL_MIN(default 3). I livelli al di sotto di questa soglia vengono trattati come OFF per evitare comportamenti instabili del TRIAC. - Livello Max (%) —
CONFIG_RBDIMMER_LEVEL_MAX(default 99). Limita l'angolo di accensione massimo.
Connessione Hardware
Istruzioni per collegare il dimmer al microcontroller e al carico AC:
- Collega il Pin Zero-Cross a qualsiasi GPIO che abbia funzionalità ISR. Consulta la documentazione del tuo chip ESP32
- Collega il Pin Dimmer a qualsiasi GPIO
- VCC a 3.3V (per ESP32, VCC = 3.3V)
- GND a GND
Esempio Base (ESP-IDF / 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
}
} Riferimento API
Operazione della Libreria
Preparazione:
- Inizializza la libreria usando
rbdimmer_init() - Registra il rilevatore di attraversamento zero usando
rbdimmer_register_zero_cross() - Crea un canale dimmer usando
rbdimmer_create_channel()
Controllo Dimming:
- Imposta il livello di dimming con
rbdimmer_set_level(). Il livello di dimming è impostato nell'intervallo 0(OFF) ~ 100(ON) - Transizione fluida del livello di dimming con
rbdimmer_set_level_transition(). Transizione fluida dal livello corrente al livello impostato in un periodo di tempo (in millisecondi, 1s=1000ms)
Strutture Dati
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;Enumerazioni
rbdimmer_curve_t
Tipi di curve di livello:
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
Risposte della funzione libreria:
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;Costanti e Macro
Nella v2.0.0, la maggior parte dei parametri di tuning sono stati spostati in Kconfig (vedi Configurazione Kconfig sopra). I seguenti costanti rimangono in rbdimmerESP32.h:
#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)I seguenti sono ora configurabili tramite idf.py menuconfig:
// Kconfig defaults (override via menuconfig):
// CONFIG_RBDIMMER_ZC_DEBOUNCE_US = 3000 // Zero-cross debounce (us)
// CONFIG_RBDIMMER_MIN_DELAY_US = 100 // Minimum TRIAC delay (us)
// CONFIG_RBDIMMER_LEVEL_MIN = 3 // Minimum level (%)
// CONFIG_RBDIMMER_LEVEL_MAX = 99 // Maximum level (%)RBDIMMER_DEFAULT_PULSE_WIDTH_US, poiché questo è correlato alle caratteristiche hardware del dimmer.Funzioni
Inizializzazione e Setup
// 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);Controllo Dimming
// 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);Interrogazione Informazioni
// 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);Guida Passo dopo Passo
Struttura del Progetto
your_project/
├── CMakeLists.txt
├── main/
│ ├── CMakeLists.txt
│ └── main.c
└── components/
└── rbdimmer/
├── CMakeLists.txt
├── Kconfig
├── include/
│ └── rbdimmer.h
└── rbdimmerESP32.cPassaggi di Implementazione
- Definisci la libreria e i pin nel tuo file
main.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)- Crea l'oggetto dimmer (uno per ogni dimmer):
rbdimmer_channel_t* dimmer_channel = NULL;- Inizializza la libreria dimmer:
rbdimmer_init();- Registra il rilevatore zero-cross e la fase:
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);- Configura il canale dimmer e crealo:
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);- Controlla il dimming:
// Set specific level
rbdimmer_set_level(dimmer_channel, level);
// Smooth transition
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);Esempi Avanzati
Sistemi Dimmer Multi-Canale
#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);
}
} Utilizzo di Funzioni Callback di Interruzione Zero-Cross
#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);
}
} Sistemi Multi-Fase
#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);
}
} Monitoraggio Operativo e Debug
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, "====================");
}Risoluzione dei Problemi
Generale
- If the dimmer doesn't work correctly, check your hardware connections, especially the zero-cross detector
- Assicurati che il pin zero-cross sia collegato a un GPIO che supporta gli interrupt
- Utilizza le funzioni
ESP_LOGper monitorare l'operazione in tempo reale - Per i sistemi multi-canale, assicurati che ogni canale dimmer abbia un pin GPIO separato
- La libreria supporta il rilevamento automatico della frequenza. Se conosci la frequenza della rete nel tuo territorio (tipicamente 50Hz o 60Hz), puoi impostarla esplicitamente per migliori prestazioni iniziali
Problemi di sfarfallio e stabilità (fix in v2.0.0)
Sfarfallio casuale o falsi trigger: Il filtro di rumore zero-cross (CONFIG_RBDIMMER_ZC_DEBOUNCE_US, default 3000 us) filtra il rumore elettrico sulla linea zero-cross. Se continui a vedere sfarfallio casuale, prova ad aumentare il valore di debounce tramite idf.py menuconfig.
Sfarfallio al 100% (luminosità massima): Il ritardo TRIAC minimo (CONFIG_RBDIMMER_MIN_DELAY_US, default 100 us) impedisce al TRIAC di accendersi troppo vicino al bordo zero-cross. Il default v2.0.0 di 100 us risolve lo sfarfallio che si verificava con il precedente default di 50 us.
Comportamento instabile al di sotto del 3%: I livelli al di sotto di CONFIG_RBDIMMER_LEVEL_MIN (default 3%) sono ora trattati come OFF. Il TRIAC non può mantenere in modo affidabile la conduzione ad angoli di accensione molto bassi, quindi la libreria limita a off piuttosto che produrre un output incoerente.
Jitter multi-canale: Quando più canali condividono la stessa fase, v2.0.0 utilizza un ISR a due passate che pre-ordina i canali per ritardo e li accende in sequenza all'interno di un'unica interruzione. Questo elimina il jitter di timing che potrebbe verificarsi quando i canali avevano valori di ritardo simili nelle versioni precedenti.
Integrazione Continua
La libreria è testata in CI rispetto alla seguente matrice:
- Versioni ESP-IDF: v5.3, v5.4, v5.5
- Chip target: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6
Questo garantisce che ogni commit si compila correttamente in tutta la gamma di configurazioni supportate.
Changelog
Per un elenco completo delle modifiche, vedi CHANGELOG.md.
← - Guida e esempi Arduino. | Contenuti | Successivo: Componente ESPHome →