diff --git a/include/YaSolR.h b/include/YaSolR.h index be017168..e47a0e6b 100644 --- a/include/YaSolR.h +++ b/include/YaSolR.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ extern Mycila::JSY jsy; extern Mycila::Logger logger; extern Mycila::MQTT mqtt; extern Mycila::PID pidController; +extern Mycila::PulseAnalyzer pulseAnalyzer; extern Mycila::PZEM pzemO1; extern Mycila::PZEM pzemO2; extern Mycila::Relay bypassRelayO1; @@ -106,6 +108,8 @@ extern Mycila::Task mqttPublishConfigTask; extern Mycila::TaskManager pioTaskManager; extern Mycila::Task calibrationTask; extern Mycila::Task carouselTask; +extern Mycila::Task dimmer1Task; +extern Mycila::Task dimmer2Task; extern Mycila::Task displayTask; extern Mycila::Task ds18Task; extern Mycila::Task lightsTask; @@ -113,6 +117,7 @@ extern Mycila::Task pzemO1PairingTask; extern Mycila::Task pzemO2PairingTask; extern Mycila::Task relayTask; extern Mycila::Task routerTask; +extern Mycila::Task zcdTask; extern Mycila::TaskManager jsyTaskManager; extern Mycila::Task jsyTask; diff --git a/include/YaSolRWebsite.h b/include/YaSolRWebsite.h index f71073f4..01673da3 100644 --- a/include/YaSolRWebsite.h +++ b/include/YaSolRWebsite.h @@ -9,9 +9,9 @@ #include #ifdef APP_MODEL_OSS - #define LINE_CHART BAR_CHART - #define AREA_CHART BAR_CHART - #define ENERGY_CARD GENERIC_CARD + #define LINE_CHART BAR_CHART + #define AREA_CHART BAR_CHART + #define ENERGY_CARD GENERIC_CARD #endif namespace YaSolR { @@ -69,6 +69,7 @@ namespace YaSolR { Statistic _udpMessageRateBuffer = Statistic(&dashboard, YASOLR_LBL_157); + Statistic _zcdPulseLength = Statistic(&dashboard, YASOLR_LBL_187); Statistic _zcdPulsePeriod = Statistic(&dashboard, YASOLR_LBL_185); Statistic _time = Statistic(&dashboard, YASOLR_LBL_034); diff --git a/include/i18n/en.h b/include/i18n/en.h index 37c2b2fe..f824631d 100644 --- a/include/i18n/en.h +++ b/include/i18n/en.h @@ -188,9 +188,9 @@ #define YASOLR_LBL_182 "Output 2 Temperature MQTT Topic" #define YASOLR_LBL_183 "Output 1 Dimmer Min/Max Remapping" #define YASOLR_LBL_184 "Output 2 Dimmer Min/Max Remapping" -#define YASOLR_LBL_185 "Zero-Cross Pulse Period" +#define YASOLR_LBL_185 "ZCD: Pulse Period" #define YASOLR_LBL_186 "Resistance Value Detection" -#define YASOLR_LBL_187 +#define YASOLR_LBL_187 "ZCD: Pulse Length" #define YASOLR_LBL_188 #define YASOLR_LBL_189 #define YASOLR_LBL_190 diff --git a/include/i18n/fr.h b/include/i18n/fr.h index f894a546..73b20810 100644 --- a/include/i18n/fr.h +++ b/include/i18n/fr.h @@ -188,9 +188,9 @@ #define YASOLR_LBL_182 "Topic MQTT pour la température sortie 2" #define YASOLR_LBL_183 "Redéfinition Min/Max Variateur Sortie 1" #define YASOLR_LBL_184 "Redéfinition Min/Max Variateur Sortie 2" -#define YASOLR_LBL_185 "Période des Zero-Cross" +#define YASOLR_LBL_185 "ZCD: Période des pulses" #define YASOLR_LBL_186 "Détection valeur résistances" -#define YASOLR_LBL_187 +#define YASOLR_LBL_187 "ZCD: Longueur des pulses" #define YASOLR_LBL_188 #define YASOLR_LBL_189 #define YASOLR_LBL_190 diff --git a/lib/DimmableLight/src/thyristor.cpp b/lib/DimmableLight/src/thyristor.cpp index 95046ab8..da991fc9 100644 --- a/lib/DimmableLight/src/thyristor.cpp +++ b/lib/DimmableLight/src/thyristor.cpp @@ -659,8 +659,12 @@ void Thyristor::begin() { void Thyristor::end() { detachInterrupt(digitalPinToInterrupt(syncPin)); - queue.reset(); +#ifdef NETWORK_FREQ_RUNTIME setFrequency(0); +#endif +#ifdef MONITOR_FREQUENCY + queue.reset(); +#endif } float Thyristor::getFrequency() { diff --git a/lib/DimmableLight/src/thyristor.h b/lib/DimmableLight/src/thyristor.h index 870c5a1f..9457fab2 100644 --- a/lib/DimmableLight/src/thyristor.h +++ b/lib/DimmableLight/src/thyristor.h @@ -42,7 +42,7 @@ #endif // If enabled, you can monitor the actual frequency of the electrical network. -#define MONITOR_FREQUENCY +// #define MONITOR_FREQUENCY #define FILTER_INT_PERIOD diff --git a/lib/MycilaDimmer/MycilaDimmer.cpp b/lib/MycilaDimmer/MycilaDimmer.cpp index e93c9ba8..cc7f2758 100644 --- a/lib/MycilaDimmer/MycilaDimmer.cpp +++ b/lib/MycilaDimmer/MycilaDimmer.cpp @@ -38,10 +38,15 @@ extern Mycila::Logger logger; static const uint16_t TABLE_PHASE_DELAY[TABLE_PHASE_LEN] PROGMEM{0xefea, 0xdfd4, 0xd735, 0xd10d, 0xcc12, 0xc7cc, 0xc403, 0xc094, 0xbd6a, 0xba78, 0xb7b2, 0xb512, 0xb291, 0xb02b, 0xaddc, 0xaba2, 0xa97a, 0xa762, 0xa557, 0xa35a, 0xa167, 0x9f7f, 0x9da0, 0x9bc9, 0x99fa, 0x9831, 0x966e, 0x94b1, 0x92f9, 0x9145, 0x8f95, 0x8de8, 0x8c3e, 0x8a97, 0x88f2, 0x8750, 0x85ae, 0x840e, 0x826e, 0x80cf, 0x7f31, 0x7d92, 0x7bf2, 0x7a52, 0x78b0, 0x770e, 0x7569, 0x73c2, 0x7218, 0x706b, 0x6ebb, 0x6d07, 0x6b4f, 0x6992, 0x67cf, 0x6606, 0x6437, 0x6260, 0x6081, 0x5e99, 0x5ca6, 0x5aa9, 0x589e, 0x5686, 0x545e, 0x5224, 0x4fd5, 0x4d6f, 0x4aee, 0x484e, 0x4588, 0x4296, 0x3f6c, 0x3bfd, 0x3834, 0x33ee, 0x2ef3, 0x28cb, 0x202c, 0x1016}; -void Mycila::Dimmer::begin(const int8_t pin) { +void Mycila::Dimmer::begin(const int8_t pin, const uint8_t nominalFrequency) { if (_dimmer) return; + if (!nominalFrequency) { + LOGE(TAG, "Disable Dimmer on pin %" PRId8 ": Invalid nominal frequency: %d", pin, nominalFrequency); + return; + } + if (GPIO_IS_VALID_OUTPUT_GPIO(pin)) { _pin = (gpio_num_t)pin; } else { @@ -52,6 +57,7 @@ void Mycila::Dimmer::begin(const int8_t pin) { LOGI(TAG, "Enable Dimmer on pin %" PRId8, _pin); + _semiPeriod = 1000000 / 2 / nominalFrequency; pinMode(_pin, OUTPUT); digitalWrite(_pin, LOW); @@ -75,14 +81,13 @@ void Mycila::Dimmer::setDutyCycle(float newDutyCycle) { if (!_dimmer) return; - const uint16_t semiPeriod = _zcd->getNominalSemiPeriod(); - if (semiPeriod == 0) + if (_semiPeriod == 0) return; // ensure newDuty is within bounds _dutyCycle = constrain(newDutyCycle, 0, _dutyCycleLimit); if (_dutyCycle == 0) { - _dimmer->setDelay(semiPeriod); + _dimmer->setDelay(_semiPeriod); } else if (_dutyCycle >= 1) { _dimmer->setDelay(0); @@ -99,8 +104,7 @@ void Mycila::Dimmer::setDutyCycle(float newDutyCycle) { const uint32_t a = TABLE_PHASE_DELAY[index]; const uint32_t b = TABLE_PHASE_DELAY[index + 1]; const uint32_t delay = a - (((a - b) * (slot & 0xffff)) >> 16); - const uint32_t period = semiPeriod; - _dimmer->setDelay((delay * period) >> 16); + _dimmer->setDelay((delay * _semiPeriod) >> 16); } } diff --git a/lib/MycilaDimmer/MycilaDimmer.h b/lib/MycilaDimmer/MycilaDimmer.h index 9fd167cc..38e317d6 100644 --- a/lib/MycilaDimmer/MycilaDimmer.h +++ b/lib/MycilaDimmer/MycilaDimmer.h @@ -4,7 +4,6 @@ */ #pragma once -#include #include #include #include @@ -18,10 +17,9 @@ namespace Mycila { class Dimmer { public: - explicit Dimmer(ZCD& zcd) : _zcd(&zcd) {} ~Dimmer() { end(); } - void begin(const int8_t pin); + void begin(const int8_t pin, const uint8_t nominalFrequency); void end(); gpio_num_t getPin() const { return _pin; } @@ -30,6 +28,8 @@ namespace Mycila { #ifdef MYCILA_JSON_SUPPORT void toJson(const JsonObject& root) const { const float angle = getPhaseAngle(); + root["enabled"] = _dimmer != nullptr; + root["state"] = isOn() ? "on" : "off"; root["angle_d"] = angle * RAD_TO_DEG; root["angle"] = angle; root["delay"] = getFiringDelay(); @@ -37,8 +37,7 @@ namespace Mycila { root["duty_cycle_limit"] = _dutyCycleLimit; root["duty_cycle_min"] = _dutyCycleMin; root["duty_cycle_max"] = _dutyCycleMax; - root["enabled"] = _dimmer != nullptr; - root["state"] = isOn() ? "on" : "off"; + root["semi_period"] = _semiPeriod; } #endif @@ -78,14 +77,13 @@ namespace Mycila { // At 100% power, the phase angle is equal to 0 float getPhaseAngle() const { // angle_rad = PI * delay_us / period_us - uint16_t semiPeriod = _zcd->getNominalSemiPeriod(); - return semiPeriod == 0 ? PI : PI * getFiringDelay() / semiPeriod; + return _semiPeriod == 0 ? PI : PI * getFiringDelay() / _semiPeriod; } private: - ZCD* _zcd; gpio_num_t _pin = GPIO_NUM_NC; Thyristor* _dimmer = nullptr; + uint16_t _semiPeriod = 0; float _dutyCycleMin = 0; float _dutyCycleMax = 1; float _dutyCycleLimit = 1; diff --git a/lib/MycilaDimmer/MycilaZCD.cpp b/lib/MycilaDimmer/MycilaZCD.cpp index 73e20151..13f07f77 100644 --- a/lib/MycilaDimmer/MycilaZCD.cpp +++ b/lib/MycilaDimmer/MycilaZCD.cpp @@ -31,10 +31,15 @@ extern Mycila::Logger logger; // Mycila::ZCD -void Mycila::ZCD::begin(const int8_t pin, const uint8_t frequency) { +void Mycila::ZCD::begin(const int8_t pin, const uint8_t nominalFrequency) { if (_enabled) return; + if (!nominalFrequency) { + LOGE(TAG, "Disable Dimmer on pin %" PRId8 ": Invalid nominal frequency: %d", pin, nominalFrequency); + return; + } + if (GPIO_IS_VALID_GPIO(pin)) { _pin = (gpio_num_t)pin; } else { @@ -43,11 +48,11 @@ void Mycila::ZCD::begin(const int8_t pin, const uint8_t frequency) { return; } - LOGI(TAG, "Enable Zero-Cross Detection on pin %" PRId8 " with frequency %" PRIu8 " Hz", _pin, frequency); + LOGI(TAG, "Enable Zero-Cross Detection on pin %" PRId8 " with frequency %" PRIu8 " Hz", _pin, nominalFrequency); // https://github.com/fabianoriccardi/dimmable-light/wiki/Notes-about-specific-architectures#interrupt-issue Thyristor::semiPeriodShrinkMargin = 400; - Thyristor::setFrequency(frequency); + Thyristor::setFrequency(nominalFrequency); Thyristor::setSyncPin(_pin); Thyristor::setSyncDir(RISING); Thyristor::begin(); @@ -59,11 +64,7 @@ void Mycila::ZCD::end() { if (_enabled) { LOGI(TAG, "Disable Zero-Cross Detection on pin %" PRId8, _pin); _enabled = false; - Thyristor::setFrequency(0); Thyristor::end(); _pin = GPIO_NUM_NC; } } - -uint16_t Mycila::ZCD::getNominalSemiPeriod() const { return Thyristor::getNominalSemiPeriod(); } -uint32_t Mycila::ZCD::getPulsePeriod() const { return _enabled ? Thyristor::getPulsePeriod() : 0; } diff --git a/lib/MycilaDimmer/MycilaZCD.h b/lib/MycilaDimmer/MycilaZCD.h index b9b539fa..c729821f 100644 --- a/lib/MycilaDimmer/MycilaZCD.h +++ b/lib/MycilaDimmer/MycilaZCD.h @@ -19,19 +19,9 @@ namespace Mycila { gpio_num_t getPin() const { return _pin; } bool isEnabled() const { return _enabled; } - // in microseconds - // 50Hz => 10000 - // 60Hz => 8333 - uint16_t getNominalSemiPeriod() const; - - // Zero-Cross pulse information - uint32_t getPulsePeriod() const; - #ifdef MYCILA_JSON_SUPPORT void toJson(const JsonObject& root) const { root["enabled"] = isEnabled(); - root["nominal_semiperiod_us"] = getNominalSemiPeriod(); - root["pulse_period_us"] = getPulsePeriod(); } #endif diff --git a/platformio.ini b/platformio.ini index 7a9303e0..eae4242f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -60,7 +60,7 @@ lib_deps = mathieucarbou/MycilaLogger @ 3.1.2 mathieucarbou/MycilaMQTT @ 4.1.1 mathieucarbou/MycilaNTP @ 4.0.0 - mathieucarbou/MycilaPulseAnalyzer @ 1.0.0 + mathieucarbou/MycilaPulseAnalyzer @ 1.0.1 mathieucarbou/MycilaPZEM004Tv3 @ 4.0.6 mathieucarbou/MycilaRelay @ 4.0.1 mathieucarbou/MycilaSystem @ 2.0.6 diff --git a/src/Website.cpp b/src/Website.cpp index 5ec95e80..f114791f 100644 --- a/src/Website.cpp +++ b/src/Website.cpp @@ -730,7 +730,8 @@ void YaSolR::WebsiteClass::updateCards() { _relay1SwitchCount.set(String(relay1.getSwitchCount()).c_str()); _relay2SwitchCount.set(String(relay2.getSwitchCount()).c_str()); _udpMessageRateBuffer.set((String(udpMessageRateBuffer.rate()) + " msg/s").c_str()); - _zcdPulsePeriod.set((String(zcd.getPulsePeriod()) + " us").c_str()); + _zcdPulsePeriod.set((String(pulseAnalyzer.getPeriod()) + " us").c_str()); + _zcdPulseLength.set((String(pulseAnalyzer.getLength()) + " us").c_str()); _time.set(Mycila::Time::getLocalStr().c_str()); _uptime.set(Mycila::Time::toDHHMMSS(Mycila::System.getUptime()).c_str()); #ifdef APP_MODEL_TRIAL @@ -821,14 +822,14 @@ void YaSolR::WebsiteClass::updateCards() { // Hardware (status) _status(_jsy, KEY_ENABLE_JSY, jsy.isEnabled(), jsy.isConnected(), YASOLR_LBL_110); _status(_mqtt, KEY_ENABLE_MQTT, mqtt.isEnabled(), mqtt.isConnected(), mqtt.getLastError() ? mqtt.getLastError() : YASOLR_LBL_113); - _status(_output1Dimmer, KEY_ENABLE_OUTPUT1_DIMMER, dimmerO1.isEnabled(), zcd.getNominalSemiPeriod() > 0, YASOLR_LBL_179); + _status(_output1Dimmer, KEY_ENABLE_OUTPUT1_DIMMER, dimmerO1.isEnabled(), zcd.isEnabled(), YASOLR_LBL_179); _status(_output1DS18, KEY_ENABLE_OUTPUT1_DS18, ds18O1.isEnabled(), ds18O1.getLastTime() > 0, YASOLR_LBL_114); - _status(_output1PZEM, KEY_ENABLE_OUTPUT1_PZEM, pzemO1.isEnabled(), pzemO1.isConnected() && pzemO1.readAddress() == YASOLR_PZEM_ADDRESS_OUTPUT1, pzemO1.isConnected() ? YASOLR_LBL_110 : YASOLR_LBL_180); - _status(_output2Dimmer, KEY_ENABLE_OUTPUT2_DIMMER, dimmerO2.isEnabled(), zcd.getNominalSemiPeriod() > 0, YASOLR_LBL_179); + _status(_output1PZEM, KEY_ENABLE_OUTPUT1_PZEM, pzemO1.isEnabled(), pzemO1.isConnected() && pzemO1.readAddress() == YASOLR_PZEM_ADDRESS_OUTPUT1, pzemO1.isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110); + _status(_output2Dimmer, KEY_ENABLE_OUTPUT2_DIMMER, dimmerO2.isEnabled(), zcd.isEnabled(), YASOLR_LBL_179); _status(_output2DS18, KEY_ENABLE_OUTPUT2_DS18, ds18O2.isEnabled(), ds18O2.getLastTime() > 0, YASOLR_LBL_114); - _status(_output2PZEM, KEY_ENABLE_OUTPUT2_PZEM, pzemO2.isEnabled(), pzemO2.isConnected() && pzemO2.readAddress() == YASOLR_PZEM_ADDRESS_OUTPUT2, pzemO2.isConnected() ? YASOLR_LBL_110 : YASOLR_LBL_180); + _status(_output2PZEM, KEY_ENABLE_OUTPUT2_PZEM, pzemO2.isEnabled(), pzemO2.isConnected() && pzemO2.readAddress() == YASOLR_PZEM_ADDRESS_OUTPUT2, pzemO2.isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110); _status(_routerDS18, KEY_ENABLE_DS18_SYSTEM, ds18Sys.isEnabled(), ds18Sys.getLastTime() > 0, YASOLR_LBL_114); - _status(_zcd, KEY_ENABLE_ZCD, zcd.isEnabled(), zcd.getPulsePeriod() > 0, YASOLR_LBL_110); + _status(_zcd, KEY_ENABLE_ZCD, true, zcd.isEnabled(), YASOLR_LBL_110); // Hardware (config) _output1PZEMSync.update(!pzemO1PairingTask.isPaused()); diff --git a/src/init/Config.cpp b/src/init/Config.cpp index 9563b47c..52f5b1cd 100644 --- a/src/init/Config.cpp +++ b/src/init/Config.cpp @@ -30,6 +30,7 @@ Mycila::Task initConfigTask("Init Config", [](void* params) { relayTask.setInterval(7 * Mycila::TaskDuration::SECONDS); routerTask.setEnabledWhen([]() { return !router.isCalibrationRunning(); }); routerTask.setInterval(500 * Mycila::TaskDuration::MILLISECONDS); + zcdTask.setInterval(1500 * Mycila::TaskDuration::MILLISECONDS); // mqttTaskManager mqttPublishConfigTask.setEnabledWhen([]() { return mqtt.isConnected(); }); @@ -124,24 +125,6 @@ Mycila::Task initConfigTask("Init Config", [](void* params) { if (config.getBool(KEY_ENABLE_OUTPUT2_DS18)) ds18O2.begin(config.get(KEY_PIN_OUTPUT2_DS18).toInt()); - // Electricity: ZCD - if (config.getBool(KEY_ENABLE_ZCD)) - zcd.begin(config.get(KEY_PIN_ZCD).toInt(), config.get(KEY_GRID_FREQUENCY).toInt() == 60 ? 60 : 50); - - // Electricity: Dimmer 1 - dimmerO1.setDutyCycleMin(config.get(KEY_OUTPUT1_DIMMER_MIN).toFloat() / 100); - dimmerO1.setDutyCycleMax(config.get(KEY_OUTPUT1_DIMMER_MAX).toFloat() / 100); - dimmerO1.setDutyCycleLimit(config.get(KEY_OUTPUT1_DIMMER_LIMIT).toFloat() / 100); - if (config.getBool(KEY_ENABLE_OUTPUT1_DIMMER)) - dimmerO1.begin(config.get(KEY_PIN_OUTPUT1_DIMMER).toInt()); - - // Electricity: Dimmer 2 - dimmerO2.setDutyCycleMin(config.get(KEY_OUTPUT2_DIMMER_MIN).toFloat() / 100); - dimmerO2.setDutyCycleMax(config.get(KEY_OUTPUT2_DIMMER_MAX).toFloat() / 100); - dimmerO2.setDutyCycleLimit(config.get(KEY_OUTPUT2_DIMMER_LIMIT).toFloat() / 100); - if (config.getBool(KEY_ENABLE_OUTPUT2_DIMMER)) - dimmerO2.begin(config.get(KEY_PIN_OUTPUT2_DIMMER).toInt()); - // Electricity: Relays if (config.getBool(KEY_ENABLE_OUTPUT1_RELAY)) bypassRelayO1.begin(config.get(KEY_PIN_OUTPUT1_RELAY).toInt(), config.get(KEY_OUTPUT1_RELAY_TYPE) == YASOLR_RELAY_TYPE_NC ? Mycila::RelayType::NC : Mycila::RelayType::NO); @@ -209,4 +192,9 @@ Mycila::Task initConfigTask("Init Config", [](void* params) { ESPConnect.setAutoRestart(true); ESPConnect.setBlocking(false); ESPConnect.begin(webServer, Mycila::AppInfo.defaultHostname, Mycila::AppInfo.defaultSSID, config.get(KEY_ADMIN_PASSWORD), {config.get(KEY_WIFI_SSID), config.get(KEY_WIFI_PASSWORD), config.getBool(KEY_ENABLE_AP_MODE)}); + + // ZCD + Dimmers + zcdTask.forceRun(); + dimmer1Task.forceRun(); + dimmer2Task.forceRun(); }); diff --git a/src/init/Core.cpp b/src/init/Core.cpp index 39a9d00c..dad82491 100644 --- a/src/init/Core.cpp +++ b/src/init/Core.cpp @@ -32,6 +32,8 @@ Mycila::Task initCoreTask("Init Core", [](void* params) { // pioTaskManager calibrationTask.setManager(pioTaskManager); carouselTask.setManager(pioTaskManager); + dimmer1Task.setManager(pioTaskManager); + dimmer2Task.setManager(pioTaskManager); displayTask.setManager(pioTaskManager); ds18Task.setManager(pioTaskManager); lightsTask.setManager(pioTaskManager); @@ -39,6 +41,7 @@ Mycila::Task initCoreTask("Init Core", [](void* params) { pzemO2PairingTask.setManager(pioTaskManager); relayTask.setManager(pioTaskManager); routerTask.setManager(pioTaskManager); + zcdTask.setManager(pioTaskManager); // jsyTaskManager jsyTask.setManager(jsyTaskManager); diff --git a/src/init/Debug.cpp b/src/init/Debug.cpp index 90fe2970..d457ca34 100644 --- a/src/init/Debug.cpp +++ b/src/init/Debug.cpp @@ -43,6 +43,8 @@ Mycila::Task initLoggingTask("Init Logging", [](void* params) { // Log execution time for some "ONCE" tasks ds18Task.setCallback(debug ? LOG_EXEC_TIME : nullptr); + dimmer1Task.setCallback(debug ? LOG_EXEC_TIME : nullptr); + dimmer2Task.setCallback(debug ? LOG_EXEC_TIME : nullptr); haDiscoveryTask.setCallback(debug ? LOG_EXEC_TIME : nullptr); mqttConfigTask.setCallback(debug ? LOG_EXEC_TIME : nullptr); mqttPublishConfigTask.setCallback(debug ? LOG_EXEC_TIME : nullptr); diff --git a/src/init/Events.cpp b/src/init/Events.cpp index 501393b1..755ffa7e 100644 --- a/src/init/Events.cpp +++ b/src/init/Events.cpp @@ -144,22 +144,16 @@ Mycila::Task initEventsTask("Init Events", [](void* params) { ds18O2.begin(config.get(KEY_PIN_OUTPUT2_DS18).toInt()); } else if (key == KEY_ENABLE_ZCD) { - zcd.end(); - if (config.getBool(KEY_ENABLE_ZCD)) - zcd.begin(config.get(KEY_PIN_ZCD).toInt(), config.get(KEY_GRID_FREQUENCY).toInt() == 60 ? 60 : 50); + zcdTask.requestEarlyRun(); } else if (key == KEY_ENABLE_MQTT) { mqttConfigTask.resume(); } else if (key == KEY_ENABLE_OUTPUT1_DIMMER) { - dimmerO1.end(); - if (config.getBool(KEY_ENABLE_OUTPUT1_DIMMER)) - dimmerO1.begin(config.get(KEY_PIN_OUTPUT1_DIMMER).toInt()); + dimmer1Task.resume(); } else if (key == KEY_ENABLE_OUTPUT2_DIMMER) { - dimmerO2.end(); - if (config.getBool(KEY_ENABLE_OUTPUT2_DIMMER)) - dimmerO2.begin(config.get(KEY_PIN_OUTPUT2_DIMMER).toInt()); + dimmer2Task.resume(); } else if (key == KEY_ENABLE_OUTPUT1_RELAY) { bypassRelayO1.end(); diff --git a/src/init/REST.cpp b/src/init/REST.cpp index 7b09210f..3504ce9d 100644 --- a/src/init/REST.cpp +++ b/src/init/REST.cpp @@ -86,6 +86,7 @@ Mycila::Task initRestApiTask("Init REST API", [](void* params) { ds18O1.toJson(routerJson["router"]["output1"]["ds18"].to()); ds18O2.toJson(routerJson["router"]["output2"]["ds18"].to()); zcd.toJson(routerJson["zcd"].to()); + pulseAnalyzer.toJson(routerJson["zcd"]["stats"].to()); JsonObject systemJson = root["system"].to(); systemInfoToJson(systemJson); diff --git a/src/main.cpp b/src/main.cpp index 4ad35ec3..303c6f1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ Mycila::JSY jsy; Mycila::Logger logger; Mycila::MQTT mqtt; Mycila::PID pidController; +Mycila::PulseAnalyzer pulseAnalyzer; Mycila::PZEM pzemO1; Mycila::PZEM pzemO2; Mycila::Relay bypassRelayO1; @@ -33,8 +34,8 @@ Mycila::TaskManager routingTaskManager("y-router"); Mycila::Router router(pidController, jsy); -Mycila::Dimmer dimmerO1(zcd); -Mycila::Dimmer dimmerO2(zcd); +Mycila::Dimmer dimmerO1; +Mycila::Dimmer dimmerO2; Mycila::RouterOutput output1("output1", dimmerO1, bypassRelayO1, pzemO1); Mycila::RouterOutput output2("output2", dimmerO2, bypassRelayO2, pzemO2); @@ -42,9 +43,9 @@ Mycila::RouterOutput output2("output2", dimmerO2, bypassRelayO2, pzemO2); Mycila::RouterRelay routerRelay1(relay1); Mycila::RouterRelay routerRelay2(relay2); +AsyncUDP udp; Mycila::CircularBuffer udpMessageRateBuffer; -AsyncUDP udp; AsyncWebServer webServer(80); AsyncWebSocket wsDebugPID("/ws/pid/csv"); ESPDash dashboard = ESPDash(&webServer, "/dashboard", false); diff --git a/src/tasks/Dimmers.cpp b/src/tasks/Dimmers.cpp new file mode 100644 index 00000000..ed74b96c --- /dev/null +++ b/src/tasks/Dimmers.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * Copyright (C) 2023-2024 Mathieu Carbou + */ +#include + +Mycila::Task dimmer1Task("Dimmer 1", Mycila::TaskType::ONCE, [](void* params) { + dimmerO1.end(); + dimmerO1.setDutyCycleMin(config.get(KEY_OUTPUT1_DIMMER_MIN).toFloat() / 100); + dimmerO1.setDutyCycleMax(config.get(KEY_OUTPUT1_DIMMER_MAX).toFloat() / 100); + dimmerO1.setDutyCycleLimit(config.get(KEY_OUTPUT1_DIMMER_LIMIT).toFloat() / 100); + if (config.getBool(KEY_ENABLE_OUTPUT1_DIMMER)) { + dimmerO1.begin(config.get(KEY_PIN_OUTPUT1_DIMMER).toInt(), config.get(KEY_GRID_FREQUENCY).toInt() == 60 ? 60 : 50); + } +}); + +Mycila::Task dimmer2Task("Dimmer 2", Mycila::TaskType::ONCE, [](void* params) { + dimmerO2.end(); + dimmerO2.setDutyCycleMin(config.get(KEY_OUTPUT2_DIMMER_MIN).toFloat() / 100); + dimmerO2.setDutyCycleMax(config.get(KEY_OUTPUT2_DIMMER_MAX).toFloat() / 100); + dimmerO2.setDutyCycleLimit(config.get(KEY_OUTPUT2_DIMMER_LIMIT).toFloat() / 100); + if (config.getBool(KEY_ENABLE_OUTPUT2_DIMMER)) { + dimmerO2.begin(config.get(KEY_PIN_OUTPUT2_DIMMER).toInt(), config.get(KEY_GRID_FREQUENCY).toInt() == 60 ? 60 : 50); + } +}); diff --git a/src/tasks/ZCD.cpp b/src/tasks/ZCD.cpp new file mode 100644 index 00000000..569cd127 --- /dev/null +++ b/src/tasks/ZCD.cpp @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * Copyright (C) 2023-2024 Mathieu Carbou + */ +#include + +Mycila::Task zcdTask("ZCD", [](void* params) { + const bool zcdSwitchedOn = config.getBool(KEY_ENABLE_ZCD); + const Mycila::PulseAnalyzer::State analyzerState = pulseAnalyzer.getState(); + + // if the user is asking to disable ZCD, and it is starting or running, we stop it + if (!zcdSwitchedOn) { + if (zcd.isEnabled() || analyzerState != Mycila::PulseAnalyzer::State::IDLE) { + logger.warn(TAG, "ZCD disabled, stopping ZCD and PulseAnalyzer"); + zcd.end(); + pulseAnalyzer.end(); + } + return; + } + + switch (analyzerState) { + // ZCD switched on, and no analysis is in progress, we start a new one if ZCD is not enabled yet + // Otherwise if ZCD switch is on and ZCd is enabled, system is running and we do nothing + case Mycila::PulseAnalyzer::State::IDLE: + if (!zcd.isEnabled()) { + logger.info(TAG, "ZCD enabled, starting PulseAnalyzer"); + pulseAnalyzer.record(config.get(KEY_PIN_ZCD).toInt()); + } + return; + + // if an analysis is in progress, we wait for it to finish + case Mycila::PulseAnalyzer::State::RECORDING: + logger.warn(TAG, "PulseAnalyzer is waiting for ZCD pulses..."); + return; + + case Mycila::PulseAnalyzer::State::RECORDED: + logger.info(TAG, "PulseAnalyzer analysis finished, starting ZCD"); + pulseAnalyzer.analyze(); + pulseAnalyzer.end(); + zcd.begin(config.get(KEY_PIN_ZCD).toInt(), config.get(KEY_GRID_FREQUENCY).toInt() == 60 ? 60 : 50); + return; + + default: + return; + } +});