← Universelle Bibliothek für ESP32 | Inhaltsverzeichnis | Weiter: ESP-IDF Framework C-Leitfaden und Beispiele →
Arduino-Leitfaden & Beispiele
Vor dem Start bitte die Bibliotheksübersicht lesen: Universelle Bibliothek für ESP32
Installation der Bibliothek
Arduino IDE
- Die Bibliothek
rbdimmerESP32als ZIP-Archiv herunterladen - In der Arduino IDE auswählen: Sketch > Bibliothek einbinden > .ZIP-Bibliothek hinzufügen
- Die heruntergeladene ZIP-Datei auswählen
- Arduino IDE neu starten, um die Installation abzuschließen
PlatformIO
- Ein neues Projekt erstellen oder ein vorhandenes öffnen
- Die Bibliothek in der
platformio.iniaus dem GitHub-Repository oder lokalem Pfad hinzufügen:
lib_deps =
# GitHub repository
https://github.com/robotdyn-dimmer/rbdimmerESP32
# or local path
# rbdimmer=file:///path/to/rbdimmerESP32
- PlatformIO installiert die Bibliothek beim nächsten Build automatisch
Hardware-Anschluss
Anweisungen zum Anschließen des Dimmers an den Mikrocontroller und die AC-Last:
- Den Nulldurchgang-Pin mit einem GPIO verbinden, der ISR-Funktionalität unterstützt; die ESP32-Chip-Dokumentation prüfen
- Den Dimmer-Pin mit einem beliebigen GPIO verbinden
- VCC an 3,3 V. Für ESP32: VCC = 3,3 V
- GND an GND
Grundlegendes Beispiel
#include
#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)
// Global variables. Dimmer object
rbdimmer_channel_t* dimmer_channel = NULL;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("AC Dimmer Test");
// Dimmer lib init
if (rbdimmer_init() != RBDIMMER_OK) {
Serial.println("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) {
Serial.println("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) {
Serial.println("Failed to create dimmer channel");
return;
}
Serial.println("AC Dimmer initialized successfully");
}
void loop() {
// dimming from 10% to 90% with step 10
for (int brightness = 10; brightness <= 90; brightness += 10) {
Serial.printf("Setting brightness to %d%%\n", brightness);
rbdimmer_set_level(dimmer_channel, brightness);
delay(2000);
}
// Smooth transition from current level to 0 level in 5 sec
Serial.println("Smooth transition to 0%");
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);
delay(6000); // delay 6 sec
// Smooth transition from current level (0) to 100 level in 5 sec
Serial.println("Smooth transition to 100%");
rbdimmer_set_level_transition(dimmer_channel, 100, 5000);
delay(6000); // delay 6 sec
}
API-Referenz
Erläuterung des Bibliotheksbetriebs
Vorbereitung:
- Die Bibliothek wird mit
rbdimmer_init()initialisiert - Der Nulldurchgang-Detektor wird mit
rbdimmer_register_zero_cross()registriert - Der Dimmer-Kanal wird mit
rbdimmer_create_channel()erstellt
Dimmsteuerung:
- Dimmstufe setzen mit
rbdimmer_set_level(). Die Dimmstufe wird im Bereich 0 (AUS) ~ 100 (EIN) festgelegt - Sanfter Übergang der Dimmstufe mit
rbdimmer_set_level_transition(). Sanfter Übergang vom aktuellen Pegel zum eingestellten Pegel über einen Zeitraum (in Millisekunden, 1 s = 1000 ms)
Datenstrukturen
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;
Enumerationen
rbdimmer_curve_t
Kurventypen:
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ückgabewerte der Bibliotheksfunktionen:
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;
Konstanten und Makros
Konstanten in der Datei rbdimmerESP32.h. Diese Parameter können geändert werden:
#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)
RBDIMMER_DEFAULT_PULSE_WIDTH_US zu ändern, da dies mit den Hardware-Eigenschaften des Dimmers zusammenhängt.Funktionen
Initialisierung und Einrichtung
// 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);
Dimmsteuerung
// 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);
Informationsabfragen
// 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);
Schritt-für-Schritt-Anleitung
Implementierungsschritte für Bibliothek, Phasenregistrierung, Nulldurchgang-Detektor, Dimmkanal und Dimmsteuerung:
1. Bibliothek einbinden und Pins definieren
#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. Dimmer-Objekt erstellen
Für jeden Dimmer ein Objekt erstellen:
rbdimmer_channel_t* dimmer_channel = NULL;
3. Dimmer-Bibliothek initialisieren
Die Funktion gibt einen Status zurück:
rbdimmer_init();
4. Nulldurchgang-Detektor und Phase registrieren
Die Funktion gibt einen Status zurück:
rbdimmer_register_zero_cross(ZERO_CROSS_PIN, PHASE_NUM, 0);
5. Dimmer-Kanal konfigurieren und erstellen
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. Dimmvorgang
rbdimmer_set_level(dimmer_channel, level);
7. Sanfte Dimmübergänge
rbdimmer_set_level_transition(dimmer_channel, 0, 5000);
Lösungen
Mehrkanalige Dimmsysteme
Die Bibliothek unterstützt mehrere unabhängige Dimmkanäle. Die Anzahl der Kanäle ist in den Bibliothekseinstellungen in der Datei rbdimmerESP32.h begrenzt. Jeder Dimmkanal muss einen separaten Ausgangspin haben.
Beispiel für ein Zweikanal-System:
#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);
}
Verwendung von Interrupt-Callback-Funktionen
Callback-Funktionen ermöglichen die Synchronisation des Codes mit Nulldurchgangsereignissen. Dies ist nützlich für Aufgaben, die eine präzise Synchronisation mit dem AC-Netz erfordern.
Beispiel für Registrierung und FreeRTOS-Task-Handler:
// 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 ...
}
Mehrphasige Systeme
Für Dreiphasensysteme muss für jede Phase ein separater Nulldurchgang-Detektor registriert werden:
#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 ...
}
Betriebsüberwachung
Zur Fehlersuche können die eingebauten Bibliotheksfunktionen verwendet werden:
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("====================");
}
Grundlegende Beispiele für die AC-Dimmer-Bibliothek
Grundlegende Dimmersteuerung
Beschreibung: Einfachstes Beispiel zur Steuerung eines AC-Dimmers mit einer festen Helligkeit.
#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);
}
Helligkeitsübergang
Zeigt, wie sanfte Übergänge zwischen Helligkeitsstufen erzeugt werden.
#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
}
Mehrere Dimmer-Kanäle
Steuert zwei separate Dimmer-Kanäle unabhängig.
#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);
}
Nulldurchgang-Callback
Demonstriert die Verwendung einer Callback-Funktion mit einem FreeRTOS-Task zur sicheren Verarbeitung von Nulldurchgangsereignissen, die zur Synchronisation mit der AC-Wellenform verwendet werden kann, ohne dem Interrupt-Handler Codeausführungsverzögerung hinzuzufügen.
#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);
}
- Einen extrem kurzen ISR (Interrupt Service Routine) – er sendet nur eine Nachricht an eine Warteschlange
- Verlagerung der gesamten Verarbeitungslogik in einen dedizierten FreeRTOS-Task
- Verwendung korrekter FreeRTOS-Mechanismen für sichere Kommunikation zwischen ISR und Task
- Vermeidung von Timing-Problemen bei der Nulldurchgangserkennung durch Trennung der Interrupt-Behandlung von der Verarbeitung
Dieser Ansatz folgt Best Practices für Echtzeitsysteme, bei denen Interrupt-Handler so kurz wie möglich sein sollten, um das System-Timing und die Reaktionsfähigkeit nicht zu beeinträchtigen.
← Universelle Bibliothek für ESP32 | Inhaltsverzeichnis | Weiter: ESP-IDF Framework C-Leitfaden und Beispiele →