Skip to the content.

MycilaDimmer

License: MIT Continuous Integration PlatformIO Registry

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

Table of Contents

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:

Features

Hardware Support

Supported Platforms

Supported Hardware

Installation

PlatformIO

[env:myproject]
lib_deps =
    mathieucarbou/MycilaDimmer

Arduino IDE

  1. Go to Sketch β†’ Include Library β†’ Manage Libraries
  2. Search for β€œMycilaDimmer”
  3. 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:

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:

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:

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:

Limitations:

MycilaDimmer Implementation:

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:

Limitations:

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:

Limitations:

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:

Limitations:

Use Cases:

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:

  1. Power Limiter: Limit dimmer (in example to 40% of nominal load)
  2. Reduced Load: Use lower wattage resistance (e.g., 1000W @ 53Ξ© instead of 3000W @ 18Ξ©)
  3. Proper Wiring: Minimize cable length, use appropriate wire gauge
  4. Strategic Placement: Position router close to grid entry/exit point
  5. Stepped Loads: Use multiple resistances with relays (e.g., 3x 800W elements)
  6. RC Snubbers: 100Ξ© 100nF snubbers can help with sensitive equipment
  7. Power LUT: Use perceptual power curve to reduce time spent at problematic phase angles

Current MycilaDimmer Support

Currently Supported (Phase Control):

Coming Soon:

Choosing the Right Method

For Solar Routers & High-Precision Applications:

For Simple On/Off Control:

Resources:

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:

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

Troubleshooting

Common Issues

Zero-Cross Dimmer Not Working

PWM Output Not Visible

DFRobot Module Not Responding

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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.

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