Se rendre au contenu

← Bibliothèque universelle pour ESP32 | Contenu | Suivant : - Guide Arduino ESP-IDF framework C et exemples. →

Guide Arduino et Exemples

Avant de commencer, lisez l'aperçu de la bibliothèque : Bibliothèque universelle pour ESP32

Nouveautés en v2.0.0

  • Architecture interne modulaire -- les internes de la bibliothèque ont été réorganisées en modules séparés. L'API publique est inchangée ; les croquis existants se compilent et s'exécutent sans modification.
  • Porte de bruit de détection du passage à zéro -- élimine les retriggers de pics TRIAC faux qui causaient auparavant du scintillement sur certaines charges.
  • ISR à deux passes pour la synchronisation multi-canal -- tous les canaux sur la même phase sont maintenant triés et activés en un seul demi-cycle, éliminant la gigue de synchronisation inter-canal.
  • IRAM_ATTR sur tous les chemins critiques en temps -- les gestionnaires ISR et les fonctions associées sont placés en IRAM pour éviter les erreurs de cache flash pendant les opérations sensibles au temps.
  • Quatre nouveaux paramètres Kconfig -- configurables via idf.py menuconfig pour les projets ESP-IDF. Les constructions Arduino utilisent les valeurs par défaut au moment de la compilation (aucune action requise).

Exigences

  • Arduino ESP32 Core 3.x (testé avec 3.0+)
  • Arduino IDE 2.x
  • Les exemples sont restructurés en répertoires par croquis pour la compatibilité Arduino IDE 2.x

Installation de la bibliothèque

Arduino IDE

  1. Téléchargez la bibliothèque rbdimmerESP32 en tant qu'archive ZIP
  2. Dans Arduino IDE, sélectionnez : Sketch > Include Library > Add .ZIP Library
  3. Sélectionnez le fichier ZIP téléchargé
  4. Redémarrez Arduino IDE pour terminer l'installation

Explication de l'opération de la bibliothèque

Préparation :

  1. La bibliothèque est initialisée à l'aide de rbdimmer_init()
  2. Le détecteur de passage à zéro est enregistré à l'aide de rbdimmer_register_zero_cross()
  3. Le canal de variateur est créé à l'aide de rbdimmer_create_channel()

Contrôle de l'atténuation :

  • Définition du niveau d'atténuation avec rbdimmer_set_level(). Le niveau d'atténuation est défini dans la plage de 0 (ARRÊT) ~ 100 (MARCHE)
  • Transition fluide du niveau d'atténuation avec rbdimmer_set_level_transition(). Transition fluide du niveau actuel au niveau défini sur une période de temps (en millisecondes, 1s=1000ms)

Structures de données

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;

Énumérations

rbdimmer_curve_t

Types de courbes de niveau :

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

Réponses des fonctions de la bibliothèque :

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 et macros

Constantes dans le fichier rbdimmerESP32.h. Vous pouvez modifier ces paramètres :

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 100             // Minimum delay (us) — raised from 50 in v2.0.0

Fonctions

Initialisation et configuration

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);

Contrôle de l'atténuation

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);

Requêtes d'information

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);

Guide étape par étape

Étapes d'implémentation pour l'initialisation de la bibliothèque, l'enregistrement de la phase, le détecteur de passage à zéro, le canal d'atténuation et le contrôle de l'atténuation :

1. Définir la bibliothèque et les broches

cpp
#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. Créer un objet variateur

Pour chaque variateur, créez un objet :

cpp
rbdimmer_channel_t* dimmer_channel = NULL;

3. Initialisation de la bibliothèque variateur

La fonction retourne un statut :

cpp
rbdimmer_init();

4. Détecteur de passage à zéro et enregistrement de phase

La fonction retourne un statut :

cpp
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);

5. Configuration et création du canal de variateur

cpp
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. Opération d'atténuation

cpp
rbdimmer_set_level(dimmer_channel, level);

7. Transitions fluides d'atténuation

cpp
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);

Solutions

Systèmes de variateur multi-canal

La bibliothèque supporte plusieurs canaux d'atténuation indépendants. Le nombre de canaux est limité dans les paramètres de la bibliothèque dans le fichier rbdimmerESP32.h. Chaque canal d'atténuation doit avoir une broche de sortie séparée.

Exemple de création d'un système à deux canaux :

cpp
#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);
}

Utilisation de fonctions de rappel d'interruption

Les fonctions de rappel vous permettent de synchroniser votre code avec des événements de passage à zéro. Ceci est utile pour les tâches nécessitant une synchronisation précise avec le réseau AC.

Exemple d'enregistrement et gestionnaire de tâche FreeRTOS :

cpp
// 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 ...
}

Systèmes triphasés

Pour les systèmes triphasés, un détecteur de passage à zéro séparé doit être enregistré pour chaque phase :

cpp
#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 ...
}

Surveillance des opérations

Pour le débogage, vous pouvez utiliser les fonctions de bibliothèque intégrées :

cpp
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("====================");
}

Dépannage : Scintillement

v2.0.0 inclut plusieurs correctifs ciblés pour les problèmes de scintillement courants :

Symptôme Cause Correction v2.0.0
Scintillement général / pics aléatoires Le pic de commutation TRIAC retrigger le détecteur de passage à zéro Porte de bruit de passage à zéro : ZC_DEBOUNCE_US = fenêtre de blanking de 3000 us après chaque passage à zéro, ignorant les faux bords
Scintillement à 100% de luminosité Délai de déclenchement trop proche du prochain passage à zéro MIN_DELAY_US augmenté à 100 us ; les niveaux >= 100% sont mappés en interne à 99% pour que le TRIAC reçoive toujours une impulsion de porte appropriée
Scintillement en dessous de 3% de luminosité Le TRIAC ne peut pas maintenir le courant à très petits angles de conduction Les niveaux inférieurs à LEVEL_MIN sont traités comme complètement ARRÊT, évitant le déclenchement partiel peu fiable
Gigue multi-canal / scintillement Les canaux activés avec des minuteurs indépendants causant des conflits d'ordonnancement ISR à deux passes : tous les canaux sur la même phase sont triés par délai et activés séquentiellement dans une chaîne de minuteurs unique au sein de chaque demi-cycle

Si vous rencontrez toujours du scintillement après la mise à niveau, vérifiez que votre signal matériel de passage à zéro est propre (pas de sonnerie) et que votre charge est compatible avec l'atténuation de phase découpée au début (TRIAC).

Exemples basiques pour la bibliothèque AC Dimmer

Les croquis d'exemple sont situés dans les répertoires par croquis sous examples/arduino/ :

Exemple Chemin
Contrôle basique du variateur examples/arduino/BasicDimmer/BasicDimmer.ino
Transition de luminosité examples/arduino/BasicTransition/BasicTransition.ino
Canaux de variateur multiples examples/arduino/MultiDimmer/MultiDimmer.ino
Rappel de passage à zéro examples/arduino/ZCCallBack/ZCCallBack.ino

Contrôle basique du variateur

Description : L'exemple le plus simple montrant comment contrôler un variateur AC avec un niveau de luminosité fixe.

Fichier : examples/arduino/BasicDimmer/BasicDimmer.ino

cpp
#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);
}

Transition de luminosité

Montre comment créer des transitions fluides entre les niveaux de luminosité.

Fichier : examples/arduino/BasicTransition/BasicTransition.ino

cpp
#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
}

Canaux de variateur multiples

Contrôle deux canaux de variateur séparés indépendamment.

Fichier : examples/arduino/MultiDimmer/MultiDimmer.ino

cpp
#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);
}

Rappel de passage à zéro

Démontre l'utilisation d'une fonction de rappel avec une tâche FreeRTOS pour traiter en toute sécurité les événements de passage à zéro, qui peuvent être utilisés pour synchroniser avec la forme d'onde AC sans ajouter de délai d'exécution du code au gestionnaire d'interruption.

Fichier : examples/arduino/ZCCallBack/ZCCallBack.ino

cpp
#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);
}
  • Gardant l'ISR (Routine de Service d'Interruption) extrêmement court - il envoie seulement un message à une file d'attente
  • Déplaçant toute la logique de traitement vers une tâche FreeRTOS dédiée
  • Utilisant les mécanismes FreeRTOS appropriés pour la communication sûre entre ISR et tâche
  • Évitant tout problème de synchronisation dans la détection du passage à zéro en séparant la gestion des interruptions du traitement

Cette approche suit les meilleures pratiques pour les systèmes en temps réel où les gestionnaires d'interruption doivent être aussi courts que possible pour éviter d'affecter la synchronisation du système et la réactivité.


Consultez CHANGELOG.md pour la liste complète des modifications dans chaque version.

← Bibliothèque universelle pour ESP32 | Contenu | Suivant : - Guide Arduino ESP-IDF framework C et exemples. →