7#define MYCILA_DIMMER_VERSION "2.1.1"
8#define MYCILA_DIMMER_VERSION_MAJOR 2
9#define MYCILA_DIMMER_VERSION_MINOR 1
10#define MYCILA_DIMMER_VERSION_REVISION 1
12#ifdef MYCILA_JSON_SUPPORT
13 #include <ArduinoJson.h>
17#include <esp32-hal-gpio.h>
24 virtual void begin() = 0;
25 virtual void end() = 0;
26 virtual const char* type()
const = 0;
38 _dutyCycleLimit = _contrain(limit, 0, 1);
39 if (_dutyCycle > _dutyCycleLimit)
50 _dutyCycleMin = _contrain(min, 0, _dutyCycleMax);
61 _dutyCycleMax = _contrain(max, _dutyCycleMin, 1);
98 _powerLUTEnabled =
false;
102 if (semiPeriod > 0) {
104 _semiPeriod = semiPeriod;
107 if (_semiPeriod == 0) {
108 ESP_LOGE(
"MycilaDimmer",
"enablePowerLUT: semiPeriod must be provided or must be already set when enabling power LUT");
110 assert(_semiPeriod > 0);
112 _powerLUTEnabled =
true;
137 bool isOnline()
const {
return _enabled && _online; }
146 _dutyCycleFire = 0.0f;
191 _dutyCycle = _contrain(dutyCycle, 0, _dutyCycleLimit);
195 if (_powerLUTEnabled) {
197 _dutyCycleFire = 0.0f;
198 }
else if (mapped == 1) {
199 _dutyCycleFire = 1.0f;
201 _dutyCycleFire = 1.0f -
static_cast<float>(_lookupFiringDelay(mapped, _semiPeriod)) /
static_cast<float>(_semiPeriod);
204 _dutyCycleFire = mapped;
222 float getDutyCycleMapped()
const {
return _dutyCycleMin + _dutyCycle * (_dutyCycleMax - _dutyCycleMin); }
242 bool calculateHarmonics(
float* array,
size_t n)
const {
243 if (array ==
nullptr || n == 0)
247 if (!
isOnline() || _dutyCycleFire <= 0.0f) {
248 for (
size_t i = 0; i < n; i++) {
254 if (_dutyCycleFire >= 1.0f) {
256 for (
size_t i = 1; i < n; i++) {
263 for (
size_t i = 0; i < n; i++) {
267 return _calculateHarmonics(array, n);
270#ifdef MYCILA_JSON_SUPPORT
276 virtual void toJson(
const JsonObject& root)
const {
277 root[
"type"] = type();
278 root[
"enabled"] = _enabled;
279 root[
"online"] = _online;
280 root[
"state"] =
isOn() ?
"on" :
"off";
281 root[
"duty_cycle"] = _dutyCycle;
283 root[
"duty_cycle_fire"] = _dutyCycleFire;
284 root[
"duty_cycle_limit"] = _dutyCycleLimit;
285 root[
"duty_cycle_min"] = _dutyCycleMin;
286 root[
"duty_cycle_max"] = _dutyCycleMax;
287 root[
"power_lut"] = _powerLUTEnabled;
288 root[
"power_lut_semi_period"] = _semiPeriod;
289 JsonObject harmonics = root[
"harmonics"].to<JsonObject>();
291 if (calculateHarmonics(output, 11)) {
292 for (
size_t i = 0; i < 11; i++) {
293 if (!std::isnan(output[i])) {
295 snprintf(key,
sizeof(key),
"H%d",
static_cast<int>(2 * i + 1));
296 harmonics[key] = output[i];
304 bool _enabled =
false;
305 bool _online =
false;
307 float _dutyCycle = 0.0f;
308 float _dutyCycleFire = 0.0f;
309 float _dutyCycleLimit = 1.0f;
310 float _dutyCycleMin = 0.0f;
311 float _dutyCycleMax = 1.0f;
313 bool _powerLUTEnabled =
false;
314 uint16_t _semiPeriod = 0;
316 virtual bool _apply() = 0;
317 virtual bool _calculateHarmonics(
float* array,
size_t n)
const = 0;
319 static uint16_t _lookupFiringDelay(
float dutyCycle, uint16_t semiPeriod);
321 static inline float _contrain(
float amt,
float low,
float high) {
322 return (amt < low) ? low : ((amt > high) ? high : amt);
325 static bool _calculatePhaseControlHarmonics(
float dutyCycleFire,
float* array,
size_t n) {
329 const float firingAngle = M_PI * (1.0f - dutyCycleFire);
333 const float sin_2a = sinf(2.0f * firingAngle);
334 const float i1_rms = sqrtf((2.0f / M_PI) * (M_PI - firingAngle + 0.5f * sin_2a));
336 if (i1_rms <= 0.001f)
342 const float scale_factor = (2.0f / M_PI) * 0.70710678f * 100.0f / i1_rms;
348 for (
size_t i = 1; i < n; i++) {
349 const float n_f =
static_cast<float>(2 * i + 1);
350 const float n_minus_1 = n_f - 1.0f;
351 const float n_plus_1 = n_f + 1.0f;
354 const float coeff = cosf(n_minus_1 * firingAngle) / n_minus_1 -
355 cosf(n_plus_1 * firingAngle) / n_plus_1;
358 array[i] = fabsf(coeff) * scale_factor;
369 virtual void begin() { _enabled =
true; }
370 virtual void end() { _enabled =
false; }
371 virtual const char* type()
const {
return "virtual"; }
374 virtual bool _apply() {
return true; }
375 virtual bool _calculateHarmonics(
float* array,
size_t n)
const {
376 for (
size_t i = 0; i < n; i++) {
384#include "MycilaDimmerDFRobot.h"
385#include "MycilaDimmerPWM.h"
386#include "MycilaDimmerThyristor.h"
void off()
Turn off the dimmer.
float getDutyCycleFire() const
Get the real firing duty cycle applied to the dimmer in the range [0, 1].
bool isOnline() const
Returns true if the dimmer is marked online.
uint16_t getPowerLUTSemiPeriod() const
Get the semi-period in us used for the power LUT calculations. If LUT is disabled,...
float getDutyCycleMax() const
Get the remapped "1" of the dimmer duty cycle.
bool isOn() const
Check if the dimmer is on.
bool isEnabled() const
Check if the dimmer is enabled (if it was able to initialize correctly)
void setDutyCycleMin(float min)
Duty remapping (equivalent to Shelly Dimmer remapping feature). Useful to calibrate the dimmer when u...
float getDutyCycle() const
Get the power duty cycle configured for the dimmer by teh user.
void setOnline(bool online)
Set the online status of the dimmer.
void on()
Turn on the dimmer at full power.
bool isPowerLUTEnabled() const
Check if the power LUT is enabled.
float getDutyCycleLimit() const
Get the power duty cycle limit of the dimmer.
float getDutyCycleMapped() const
Get the remapped power duty cycle from the currently user set duty cycle.
bool setDutyCycle(float dutyCycle)
Set the power duty.
bool isOff() const
Check if the dimmer is off.
bool isOnAtFullPower() const
Check if the dimmer is on at full power.
void enablePowerLUT(bool enable, uint16_t semiPeriod=0)
Enable or disable the use of power LUT for dimmer curve The power LUT provides a non-linear dimming c...
float getDutyCycleMin() const
Get the remapped "0" of the dimmer duty cycle.
void setDutyCycleMax(float max)
Duty remapping (equivalent to Shelly Dimmer remapping feature). Useful to calibrate the dimmer when u...
void setDutyCycleLimit(float limit)
Set the power duty cycle limit of the dimmer. The duty cycle will be clamped to this limit.