MycilaDimmer
A comprehensive ESP32/Arduino library for controlling AC power devices including TRIACs, SSRs, and voltage regulators through multiple dimming methods.

Table of Contents
- Overview
- Features
- Hardware Support
- Installation
- Quick Start
- Dimmer Types
- Understanding AC Dimming Methods
- API Reference
- Configuration
- Examples
- Build Configuration
- Troubleshooting
- Contributing
- License
- Disclaimer
Overview
MycilaDimmer provides a unified interface for controlling AC power devices through different hardware implementations. The library uses a polymorphic architecture that allows you to switch between different dimming methods without changing your application code.
Key Benefits:
- Unified API - Same interface for all dimmer types
- IRAM Safe - Interrupt handlers work during flash operations
- Hardware Agnostic - Supports multiple hardware approaches
- Production Ready - Used in YaSolR Solar Router
Features
- β¨ Flicker-Free Dimming: Progressive dimming without flickering using precise DAC control or zero-cross detection with quality ZCD circuits
- ποΈ Multiple Control Methods: Zero-cross detection, PWM, and I2C DAC
- β‘ High Performance: IRAM-safe interrupt handlers with lookup table optimization
- π‘ Selectable Dimming Curves: Choose between linear dimming or Power LUT for perceptual brightness matching - switch at runtime based on your needs
- οΏ½ Flexible Configuration: Duty cycle remapping, calibration, and user-selectable dimming modes
- π Rich Telemetry: Duty cycle measurements, firing ratios, and online status
- π‘οΈ Safety Features: Duty cycle limits and grid connection detection
- π± JSON Integration: Optional ArduinoJson support for telemetry
- π Real-time Control: Microsecond-precision timing control
Hardware Support
Supported Platforms
- ESP32 (all variants: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2)
- Arduino Framework via PlatformIO or Arduino IDE
Supported Hardware
- TRIACs with zero-cross detection circuits
- Random Solid State Relays (SSRs)
- Voltage Regulators with 0-10V control (LSA, LCTC, etc)
- DFRobot DAC Modules (GP8211S, GP8413, GP8403)
Installation
PlatformIO
[env:myproject]
lib_deps =
mathieucarbou/MycilaDimmer
Arduino IDE
- Go to Sketch β Include Library β Manage Libraries
- Search for βMycilaDimmerβ
- Install the library by Mathieu Carbou
Quick Start
#include <MycilaDimmer.h>
// Create a PWM dimmer instance
Mycila::PWMDimmer dimmer;
void setup() {
Serial.begin(115200);
// Configure the dimmer
dimmer.setPin(GPIO_NUM_26);
dimmer.begin();
// Set 50% power
dimmer.setDutyCycle(0.5);
}
void loop() {
// Gradually increase power
for (float power = 0.0; power <= 1.0; power += 0.1) {
dimmer.setDutyCycle(power);
delay(1000);
}
}
Dimmer Types
Zero-Cross Dimmer
Perfect for TRIAC and Random SSR control with flicker-free progressive dimming. Achieves smooth, continuous power control through precise phase angle control when paired with quality zero-cross detection circuits.
#include <MycilaDimmer.h>
Mycila::ZeroCrossDimmer dimmer;
void setup() {
dimmer.setPin(GPIO_NUM_26);
dimmer.setSemiPeriod(10000); // 50Hz AC (10ms semi-period)
dimmer.begin();
// Register with external zero-cross detector
pulseAnalyzer.onZeroCross(Mycila::ZeroCrossDimmer::onZeroCross);
}
Features:
- Flicker-free dimming: Smooth progressive control without visible flickering
- Microsecond-precision phase angle control
- Lookup table with linear interpolation for seamless transitions
- IRAM-safe interrupt handlers
- Supports several zero-cross detection circuits:
- Zero-Cross Detector from Daniel S
- JSY-MK-194G
- RobotDyn ZCD
- BM1Z102FJ
- Any other ZCD circuit providing a clean digital pulse at each zero-crossing
The Zero-Cross Detector mounted on DIN Rail mount from Daniel is is very good and reliable. I sometimes buy it in bulk from PCBWay. If you want one, you can have a look at the YaSolR Pro page for the stock status.
PWM Dimmer
Standard PWM output for SSRs and other PWM-controlled devices.
#include <MycilaDimmer.h>
Mycila::PWMDimmer dimmer;
void setup() {
dimmer.setPin(GPIO_NUM_26);
dimmer.setFrequency(1000); // 1kHz PWM
dimmer.setResolution(12); // 12-bit resolution
dimmer.begin();
}
Features:
- Configurable frequency (default: 1kHz)
- Configurable resolution (default: 12-bit)
- Automatic PWM channel management
For example, this dimmer can be used with a 3.3V to 0-5V/0-10V signal converter
DFRobot DAC Dimmer
Flicker-free progressive dimming using precision I2C DAC modules. Perfect for voltage regulator controlled devices (LSA, LCTC, etc) with 0-10V input, providing ultra-smooth dimming without any visible flickering thanks to high-resolution DAC output.
#include <MycilaDimmer.h>
Mycila::DFRobotDimmer dimmer;
void setup() {
dimmer.setSKU(Mycila::DFRobotDimmer::SKU::DFR1071_GP8211S);
dimmer.setDeviceAddress(0x59);
dimmer.setOutput(Mycila::DFRobotDimmer::Output::RANGE_0_10V);
dimmer.begin();
}
Supported Models:
- DFR0971 (GP8403): 12-bit, dual channel, 0-5V/10V
- DFR1071 (GP8211S): 15-bit, single channel, 0-5V/10V
- DFR1073 (GP8413): 15-bit, dual channel, 0-5V/10V
Here is a comparison table of the different DFRobot DAC modules.
Understanding AC Dimming Methods
When controlling AC power devices like TRIACs, SSRs, and voltage regulators, there are several dimming approaches available. Each method has specific characteristics, advantages, and limitations that affect precision, grid compatibility, and regulatory compliance.
Dimming Method Comparison
Phase Control
How it works: Controls power by delaying the firing angle within each AC semi-period (twice per cycle). The TRIAC/SSR activates at a specific point in the sine wave, βchoppingβ the waveform to deliver partial power.
Advantages:
- β High Precision: Can adjust power at each semi-period (every 10ms for 50Hz), enabling watt-level control
- β Fast Response: Instant power adjustment with no delay
- β Accurate Power Control: With Power LUT, achieves predictable power output matching the desired level
- β Regulatory Compliant: Keeps grid balanced (no DC component) when properly implemented
- β Widely Used: Standard method in commercial dimmers and variable speed drives
Limitations:
- β οΈ Harmonics: Generates harmonic distortion, especially at 50% power (90Β° phase angle)
- Harmonics are regulated by CEI 61000-3-2 (Class A devices)
- H3 (3rd harmonic) is the most significant, exceeds limits at 50% dimming with ~1700W nominal load
- H15 (15th harmonic) first to exceed limits at ~760W nominal load (but less significant than H3)
- Maximum compliant load: ~800W without mitigation
- Maximum significant load: ~1700W
- β οΈ Mitigation may be required: May need RC snubbers, proper wiring, load management, or limiter settings
MycilaDimmer Implementation:
- All three implementations (ZeroCrossDimmer, PWMDimmer, DFRobotDimmer) use phase control
- ZeroCrossDimmer: Direct TRIAC/Random SSR control with zero-cross detection
- PWMDimmer: Generates PWM signal for converter for voltage regulators (LSA, LCTC) which perform internal phase control
- DFRobotDimmer: Outputs 0-10V analog signal to voltage regulators (LSA, LCTC) which perform internal phase control
Burst Fire on Full Period
How it works: Rapidly switches complete AC cycles on/off (20ms periods for 50Hz). For example, to achieve 50% power, alternates full power for 1 second, then off for 1 second.
Advantages:
- β No Harmonics: Preserves complete sine waves, generates minimal harmonic distortion
- β Simple Implementation: Basic on/off switching logic
- β Compatible with Zero-Cross SSRs: Can use simpler, cheaper SSRs
Limitations:
- β Flickering: Visible light flicker and voltage fluctuations affecting nearby devices
- Caused by sudden high current draw creating voltage drops
- Impacts micro-inverters and other sensitive electronics
- β Slow Response: Limited precision due to coarse time slots
- 50 slots in 1-second window for 50Hz = 60W resolution for 3000W load
- Longer windows reduce precision; shorter windows worsen flickering
- β Heat Dissipation: Rapid switching generates more heat in SSR
- β Poor Accuracy: Cannot achieve watt-level control precision
- β Delayed Corrections: By the time adjustment is applied, conditions may have changed
Burst Fire on Semi-Period
How it works: Switches at semi-period level (every 10ms for 50Hz) to double the control slots and improve response time.
Advantages:
- β Better Resolution: Twice as many control slots compared to full-period burst fire
- β Faster Response: 2x quicker than full-period burst fire
Limitations:
- β All Full-Period Limitations: Still suffers from flickering, heat, and inaccuracy
- β DC Component - FORBIDDEN: Critical regulatory violation
- At 50% power, may turn off negative semi-periods (1 second), then positive semi-periods (1 second)
- Creates dangerous DC components on AC grid (current asymmetry drawn only one side of the waveform for a short time)
- Unbalances grid network by drawing current asymmetrically
- Violates electrical regulations - this method should not be used
- β Grid Instability: Causes phase imbalance and potential equipment damage
Regulatory Note: Creating DC components on AC power grids is explicitly forbidden by electrical regulations. Many routers implement this method unknowingly, creating compliance and safety issues.
Stochastic Burst Fire (Coming Soon)
How it works: Probabilistic full-wave switching at each zero-cross. At each AC cycle, generates a random number (0-100) and compares it to the desired power level percentage. If the random number is lower, the full wave is allowed through; otherwise, itβs blocked.
Advantages:
- β No DC Component: Always switches at zero-cross on full waves, maintains grid balance
- β Eliminates Periodic Flickering: Random distribution prevents visible periodic patterns
- β Minimal EMF Interference: Zero-cross switching reduces electromagnetic interference
- β Multi-Channel Safe: Random switching prevents simultaneous current spikes across channels
- β Even Distribution: Over time, produces statistically accurate power output
- β No Harmonics: Preserves complete sine waves like traditional burst fire
Limitations:
- β οΈ Less Precise: Statistical accuracy over time, not instant watt-level precision
- β οΈ Requires More Cycles: Needs multiple AC cycles to reach target power level
- β οΈ Not for Fast-Response Systems: Better suited for thermal/heating applications with slower dynamics
Use Cases:
- Multi-channel heating systems
- Temperature control with PID regulation
- Applications where eliminating flicker is more important than instant precision
- Systems sensitive to EMF interference
Regulatory Note: Creating DC components on AC power grids is explicitly forbidden by electrical regulations. Many routers implement this method unknowingly, creating compliance and safety issues.
Recommendations for Harmonic Mitigation (Phase Control)
When using phase control, harmonics can be reduced through several approaches:
- Power Limiter: Limit dimmer (in example to 40% of nominal load)
- Reduced Load: Use lower wattage resistance (e.g., 1000W @ 53Ξ© instead of 3000W @ 18Ξ©)
- Proper Wiring: Minimize cable length, use appropriate wire gauge
- Strategic Placement: Position router close to grid entry/exit point
- Stepped Loads: Use multiple resistances with relays (e.g., 3x 800W elements)
- RC Snubbers: 100Ξ© 100nF snubbers can help with sensitive equipment
- Power LUT: Use perceptual power curve to reduce time spent at problematic phase angles
Current MycilaDimmer Support
Currently Supported (Phase Control):
- β ZeroCrossDimmer - TRIAC/Random SSR with zero-cross detection
- β PWMDimmer - PWM output for voltage regulators (LSA, LCTC)
- β DFRobotDimmer - I2C DAC for voltage regulators (LSA, LCTC)
Coming Soon:
- π§ Stochastic Burst Fire (Full Period) - For Zero-Cross SSR compatibility
- Novel probabilistic approach: At each zero-cross, generates random number (0-100) and compares to desired power level
- Smart distribution: If random number < desired power, relay switches on; otherwise switches off
- Key advantages over traditional burst fire:
- Eliminates periodic flickering through randomized switching pattern
- Prevents simultaneous high current draw across multiple channels
- Evenly distributed full-wave switching over time
- No DC component - maintains grid balance
- Reduces EMF interference by switching only at zero-cross
- Trade-offs: Less precise than phase control but safer and more grid-friendly
- Ideal for: Multi-channel heating applications, temperature control systems
- Based on proven StochasticHeatController implementation
Choosing the Right Method
For Solar Routers & High-Precision Applications:
- Use Phase Control with appropriate harmonic mitigation
- Provides best accuracy and response time
- Essential for PV routing where precise power matching is critical
For Simple On/Off Control:
- Consider Zero-Cross SSR with simple relay control
- Suitable when precise dimming is not required
- No harmonics, but no variable power control
Resources:
- CEI 61000-3-2 Harmonic Standards
- YaSolR Overview - Detailed Analysis
- Harmonic Studies and Mitigation
API Reference
Doxygen documentation is available here.
Common API (All Dimmer Types)
// Lifecycle
void begin(); // Initialize dimmer
void end(); // Cleanup and disable
const char* type() const; // Get dimmer type name
// Power Control
void on(); // Full power
void off(); // Turn off
bool setDutyCycle(float dutyCycle); // Set power (0.0-1.0)
// Status & State
bool isEnabled() const; // Is configured
bool isOnline() const; // Ready for operation (enabled + online)
void setOnline(bool online); // Set online status (grid connection)
bool isOn() const; // Currently active (online + duty > 0)
bool isOff() const; // Currently inactive
bool isOnAtFullPower() const; // Check if at max power
// Calibration & Remapping
void setDutyCycleMin(float min); // Remap 0% point (hardware calibration)
void setDutyCycleMax(float max); // Remap 100% point
void setDutyCycleLimit(float limit); // Clamp max allowed power
float getDutyCycle() const; // Current power setting
float getDutyCycleMapped() const; // Get mapped/calibrated duty cycle
float getDutyCycleLimit() const; // Get current limit
float getDutyCycleMin() const; // Get current min
float getDutyCycleMax() const; // Get current max
// Dimming Curve (Power LUT)
void enablePowerLUT(bool enable, uint16_t semiPeriod = 0); // Enable/disable perceptual LUT (default: false)
bool isPowerLUTEnabled() const; // Check if LUT is enabled
uint16_t getPowerLUTSemiPeriod() const; // Get LUT semi-period (us)
// Measurements
float getDutyCycleFire() const; // Actual firing ratio (0-1)
#ifdef MYCILA_JSON_SUPPORT
void toJson(const JsonObject& root) const; // Serialize to JSON
#endif
Zero-Cross Dimmer Specific
void setPin(gpio_num_t pin); // Set output GPIO pin
void setSemiPeriod(uint16_t semiPeriod); // Set grid semi-period (us)
static void onZeroCross(int16_t delayUntilZero, void* arg); // Zero-cross callback
PWM Dimmer Specific
void setPin(gpio_num_t pin); // Set output GPIO pin
void setFrequency(uint32_t frequency); // Set PWM frequency (default: 1000 Hz)
void setResolution(uint8_t resolution); // Set PWM resolution (default: 12-bit)
DFRobot DAC Dimmer Specific
void setWire(TwoWire& wire); // Set I2C bus (default: Wire)
void setSKU(SKU sku); // Set module SKU (DFR0971/1071/1073)
void setOutput(Output output); // Set voltage range (0-5V or 0-10V)
void setDeviceAddress(uint8_t addr); // Set I2C address (default: 0x58)
void setChannel(uint8_t channel); // Set DAC channel (0 or 1 for dual-channel)
Advanced Features
// Duty Cycle Remapping (Hardware Calibration)
dimmer.setDutyCycleMin(0.1); // 0% now maps to 10%
dimmer.setDutyCycleMax(0.9); // 100% now maps to 90%
// Safety Limiting
dimmer.setDutyCycleLimit(0.8); // Never exceed 80% power
// Power LUT - Selectable Dimming Curve
// Choose between LINEAR or POWER LUT dimming at runtime!
//
// LINEAR MODE (default, disabled):
// - Direct phase angle control
// - 50% duty cycle = 50% phase delay
// - Non-linear relationship between duty cycle and actual power output
// - Use when you need direct phase control or working with non-resistive loads
//
// POWER LUT MODE (enabled):
// - Non-linear curve that matches real power output of resistive loads
// - 50% duty cycle β 50% actual power consumption
// - Also provides more natural dimming that matches human brightness perception
// - Best for resistive loads (heating elements, incandescent bulbs) where
// you want predictable power control
//
dimmer.enablePowerLUT(true, 10000); // Enable Power LUT mode (semi-period: 10000us for 50Hz, 8333us for 60Hz)
dimmer.enablePowerLUT(false); // Switch to linear phase angle mode
bool isUsing = dimmer.isPowerLUTEnabled(); // Check current mode
// Online Status Control
dimmer.setOnline(false); // Temporarily disable dimmer (e.g., when grid disconnected)
dimmer.setOnline(true); // Re-enable dimmer
// Telemetry (with MYCILA_JSON_SUPPORT)
#ifdef MYCILA_JSON_SUPPORT
JsonDocument doc;
dimmer.toJson(doc.to<JsonObject>());
serializeJson(doc, Serial);
#endif
Configuration
Build Flags
For Zero-Cross Dimmer (IRAM safety):
build_flags =
-D CONFIG_ARDUINO_ISR_IRAM=1
-D CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=1
-D CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=1
-D CONFIG_GPTIMER_ISR_IRAM_SAFE=1
-D CONFIG_GPIO_CTRL_FUNC_IN_IRAM=1
lib_deps =
mathieucarbou/MycilaPulseAnalyzer
For JSON Support:
build_flags =
-D MYCILA_JSON_SUPPORT
lib_deps =
bblanchon/ArduinoJson
Examples
The library includes comprehensive examples:
- DAC Example - DFRobot DAC module control
- JSON Example - Telemetry and JSON integration
- PWM Example - Basic PWM dimming
- ZeroCross Example - TRIAC control with zero-cross detection
- ZeroCrossAuto Example - Automatic detection of frequency and semi-period
- ZeroCrossWithFS Example - File system integration
Build Configuration
PlatformIO Configuration
[platformio]
lib_dir = .
src_dir = examples/ZeroCross ; or DAC, PWM, etc.
[env]
framework = arduino
board = esp32dev
build_flags =
-D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-Wall -Wextra
; Zero-Cross specific flags
-D CONFIG_ARDUINO_ISR_IRAM=1
-D CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=1
-D CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=1
-D CONFIG_GPTIMER_ISR_IRAM_SAFE=1
-D CONFIG_GPIO_CTRL_FUNC_IN_IRAM=1
lib_deps =
bblanchon/ArduinoJson
mathieucarbou/MycilaPulseAnalyzer
Dependencies
- ESP32 Arduino Core (any recent version)
- ArduinoJson (optional, for JSON support)
- MycilaPulseAnalyzer (optional, for zero-cross examples)
Troubleshooting
Common Issues
Zero-Cross Dimmer Not Working
- Ensure IRAM build flags are set
- Check semi-period is configured (
setSemiPeriod()) - Verify zero-cross signal is connected and working
PWM Output Not Visible
- Check GPIO pin configuration
- Verify PWM frequency and resolution settings
- Use oscilloscope or LED to test output
DFRobot Module Not Responding
- Verify I2C wiring (SDA, SCL, power, ground)
- Check device address with I2C scanner
- Ensure correct SKU is configured
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
git clone https://github.com/mathieucarbou/MycilaDimmer.git
cd MycilaDimmer
pio run
License
This project is licensed under the MIT License - see the LICENSE file for details.
Disclaimer
This website is provided for informational purposes only. By accessing this site and using the information contained herein, you accept the terms set forth in this disclaimer.
-
Accuracy of Information: We strive to provide accurate and up-to-date information on this site, but we cannot guarantee the completeness or accuracy of this information. The information provided is subject to change without notice.
-
Use of Information: Use of the information provided on this site is at your own risk. *We decline all responsibility for the consequences arising from the use of this information. It is recommended that you consult a competent professional for advice specific to your situation.
-
External Links: This site may contain links to external websites which are provided for your reference and convenience. We have no control over the content of these external sites and we accept no responsibility for their content and their use.
-
Limitation of Liability: To the fullest extent permitted by applicable law, we disclaim all liability for any direct, indirect, incidental, consequential or special damages arising out of the use of, or inability to use, this website, even if we have been advised of the possibility of such damage.
By using this site, you agree to hold harmless the owners, administrators and authors of this site from any claims arising from your use of this website. If you do not agree to these terms, please do not use this site.
Author: Mathieu Carbou
Used in: YaSolR Solar Router