diff --git a/include/YaSolR.h b/include/YaSolR.h index a3cf949..8c194b9 100644 --- a/include/YaSolR.h +++ b/include/YaSolR.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -35,17 +36,17 @@ #include #ifdef APP_MODEL_TRIAL -#include + #include #endif #ifdef APP_MODEL_PRO -#include -#include -#include + #include + #include + #include #else -#include -#include -#include + #include + #include + #include #endif #include @@ -127,3 +128,5 @@ extern Mycila::Task initLoggingTask; extern Mycila::Task initMqttSubscribersTask; extern Mycila::Task initRestApiTask; extern Mycila::Task initWebTask; + +extern Mycila::CircularBuffer jsyRemoteUdpRate; diff --git a/include/YaSolRWebsite.h b/include/YaSolRWebsite.h index 155db78..e4ae2f0 100644 --- a/include/YaSolRWebsite.h +++ b/include/YaSolRWebsite.h @@ -67,6 +67,8 @@ namespace YaSolR { Statistic _relay1SwitchCount = Statistic(&dashboard, YASOLR_LBL_032); Statistic _relay2SwitchCount = Statistic(&dashboard, YASOLR_LBL_033); + Statistic _jsyRemoteUdpRate = Statistic(&dashboard, YASOLR_LBL_157); + Statistic _time = Statistic(&dashboard, YASOLR_LBL_034); Statistic _uptime = Statistic(&dashboard, YASOLR_LBL_035); #ifdef APP_MODEL_TRIAL diff --git a/include/i18n/en.h b/include/i18n/en.h index 885db86..baa6c2e 100644 --- a/include/i18n/en.h +++ b/include/i18n/en.h @@ -160,7 +160,7 @@ #define YASOLR_LBL_154 "Invalid" #define YASOLR_LBL_155 "Input Only" #define YASOLR_LBL_156 "I/O" -#define YASOLR_LBL_157 +#define YASOLR_LBL_157 "JSY Remote UDP: Reception Rate" #define YASOLR_LBL_158 #define YASOLR_LBL_159 #define YASOLR_LBL_160 diff --git a/include/i18n/fr.h b/include/i18n/fr.h index 3a8540e..32c91c1 100644 --- a/include/i18n/fr.h +++ b/include/i18n/fr.h @@ -160,7 +160,7 @@ #define YASOLR_LBL_154 "Invalide" #define YASOLR_LBL_155 "Entrée uniquement" #define YASOLR_LBL_156 "E/S" -#define YASOLR_LBL_157 +#define YASOLR_LBL_157 "JSY Remote UDP: Vitesse de réception" #define YASOLR_LBL_158 #define YASOLR_LBL_159 #define YASOLR_LBL_160 diff --git a/lib/DimmableLight/src/thyristor.cpp b/lib/DimmableLight/src/thyristor.cpp index 260d318..1f284aa 100644 --- a/lib/DimmableLight/src/thyristor.cpp +++ b/lib/DimmableLight/src/thyristor.cpp @@ -471,7 +471,7 @@ void Thyristor::setFrequency(float frequency) { uint16_t Thyristor::getPulseWidth() { noInterrupts(); uint32_t diff = micros() - lastTime; - if (diff > semiPeriodLength) { + if (semiPeriodLength && diff > semiPeriodLength) { pulses.reset(); } uint16_t avg = pulses.avg(); @@ -482,7 +482,7 @@ uint16_t Thyristor::getPulseWidth() { uint16_t Thyristor::getMaxPulseWidth() { noInterrupts(); uint32_t diff = micros() - lastTime; - if (diff > semiPeriodLength) { + if (semiPeriodLength && diff > semiPeriodLength) { pulses.reset(); } uint16_t max = pulses.max(); @@ -493,7 +493,7 @@ uint16_t Thyristor::getMaxPulseWidth() { uint16_t Thyristor::getMinPulseWidth() { noInterrupts(); uint32_t diff = micros() - lastTime; - if (diff > semiPeriodLength) { + if (semiPeriodLength && diff > semiPeriodLength) { pulses.reset(); } uint16_t min = pulses.min(); @@ -504,7 +504,7 @@ uint16_t Thyristor::getMinPulseWidth() { uint16_t Thyristor::getLastPulseWidth() { noInterrupts(); uint32_t diff = micros() - lastTime; - if (diff > semiPeriodLength) { + if (semiPeriodLength && diff > semiPeriodLength) { pulses.reset(); } uint16_t last = pulses.last(); diff --git a/lib/MycilaCircularBuffer/MycilaCircularBuffer.h b/lib/MycilaCircularBuffer/MycilaCircularBuffer.h index e59c90b..9c8f98a 100644 --- a/lib/MycilaCircularBuffer/MycilaCircularBuffer.h +++ b/lib/MycilaCircularBuffer/MycilaCircularBuffer.h @@ -19,15 +19,19 @@ namespace Mycila { size_t count() const { return _count; }; T avg() const { return _count == 0 ? 0 : _sum / _count; } - T last() const { return _last; } + T first() const { return _buffer[_index]; } + T last() const { return _buffer[_index == 0 ? N - 1 : _index - 1]; } T sum() const { return _sum; } T max() const { return _max; } T min() const { return _min; } + T rate() const { + T diff = last() - first(); + return diff == 0 ? 0 : _count / diff; + } T add(T value) { T current = _buffer[_index]; _buffer[_index++] = value; - _last = value; if (value > _max) _max = value; if (value < _min) @@ -49,7 +53,6 @@ namespace Mycila { void reset() { _sum = 0; - _last = 0; _min = std::numeric_limits::max(); _max = std::numeric_limits::min(); _index = 0; @@ -73,7 +76,6 @@ namespace Mycila { private: T _buffer[N]; T _sum; - T _last; T _min; T _max; size_t _index = 0; diff --git a/lib/MycilaDimmer/MycilaDimmer.h b/lib/MycilaDimmer/MycilaDimmer.h index fb93523..fbc6a1a 100644 --- a/lib/MycilaDimmer/MycilaDimmer.h +++ b/lib/MycilaDimmer/MycilaDimmer.h @@ -39,7 +39,7 @@ namespace Mycila { gpio_num_t getPin() const { return _pin; } bool isEnabled() const { return _enabled; } - bool isConnected() const { return _zcd->isConnected(); } + bool isConnected() const { return _enabled && _zcd->isConnected(); } void toJson(const JsonObject& root) const { const float angle = getPhaseAngle(); @@ -74,7 +74,7 @@ namespace Mycila { // At 100% power, the phase angle is equal to 0 float getPhaseAngle() const { // angle_rad = PI * delay_s / period_s - return PI * _zcd->getFrequency() * getFiringDelay() / 1000000; + return PI * _zcd->getPulseFrequency() * getFiringDelay() / 1000000; } private: diff --git a/lib/MycilaDimmer/MycilaZCD.cpp b/lib/MycilaDimmer/MycilaZCD.cpp index 8704549..f9d0ba8 100644 --- a/lib/MycilaDimmer/MycilaZCD.cpp +++ b/lib/MycilaDimmer/MycilaZCD.cpp @@ -51,7 +51,7 @@ void Mycila::ZCD::begin(const int8_t pin, const uint8_t frequency) { Thyristor::setSyncPin(_pin); Thyristor::begin(); - // attachInterrupt(digitalPinToInterrupt(_pin), onEdge, CHANGE); + _frequencyUpdater.attach(1, +[](ZCD* instance) { instance->_measuredGridFrequency = Thyristor::getDetectedFrequency(); }, this); _enabled = true; } @@ -59,6 +59,7 @@ void Mycila::ZCD::begin(const int8_t pin, const uint8_t frequency) { void Mycila::ZCD::end() { if (_enabled) { LOGI(TAG, "Disable Zero-Cross Detection..."); + _frequencyUpdater.detach(); _enabled = false; Thyristor::setFrequency(0); Thyristor::end(); @@ -67,8 +68,6 @@ void Mycila::ZCD::end() { } uint16_t Mycila::ZCD::getSemiPeriod() const { return _enabled ? Thyristor::getSemiPeriod() : 0; } -float Mycila::ZCD::getFrequency() const { return getCurrentFrequency() * 2; } -float Mycila::ZCD::getCurrentFrequency() const { return _enabled ? Thyristor::getDetectedFrequency() : 0; } uint16_t Mycila::ZCD::getAvgPulseWidth() const { return _enabled ? Thyristor::getPulseWidth() : 0; } uint16_t Mycila::ZCD::getMaxPulseWidth() const { return _enabled ? Thyristor::getMaxPulseWidth() : 0; } uint16_t Mycila::ZCD::getLastPulseWidth() const { return _enabled ? Thyristor::getLastPulseWidth() : 0; } diff --git a/lib/MycilaDimmer/MycilaZCD.h b/lib/MycilaDimmer/MycilaZCD.h index 2acfe38..75b59fe 100644 --- a/lib/MycilaDimmer/MycilaZCD.h +++ b/lib/MycilaDimmer/MycilaZCD.h @@ -5,6 +5,8 @@ #pragma once #include +#include + #include namespace Mycila { @@ -15,20 +17,18 @@ namespace Mycila { gpio_num_t getPin() const { return _pin; } bool isEnabled() const { return _enabled; } - bool isConnected() const { return getFrequency() > 0; } + bool isConnected() const { return _measuredGridFrequency > 0; } // in microseconds // 50Hz => 10000 // 60Hz => 8333 uint16_t getSemiPeriod() const; - // Detect the Zero-Cross pulse frequency in Hz - float getFrequency() const; - // Grid frequency in Hz based on the Zero-Cross pulse frequency - float getCurrentFrequency() const; + float getCurrentFrequency() const { return _enabled ? _measuredGridFrequency : 0; } - // Detect the Zero-Cross pulse width in microseconds + // Zero-Cross pulse information + float getPulseFrequency() const { return _enabled ? _measuredGridFrequency * 2 : 0; } uint16_t getAvgPulseWidth() const; uint16_t getLastPulseWidth() const; uint16_t getMaxPulseWidth() const; @@ -36,7 +36,7 @@ namespace Mycila { void toJson(const JsonObject& root) const { root["enabled"] = isEnabled(); - root["pulse_freq"] = getFrequency(); + root["pulse_freq"] = getPulseFrequency(); root["pulse_width_avg"] = getAvgPulseWidth(); root["pulse_width_last"] = getLastPulseWidth(); root["pulse_width_max"] = getMaxPulseWidth(); @@ -46,6 +46,8 @@ namespace Mycila { private: bool _enabled = false; + volatile float _measuredGridFrequency = 0; + Ticker _frequencyUpdater; gpio_num_t _pin = GPIO_NUM_NC; }; } // namespace Mycila diff --git a/platformio.ini b/platformio.ini index d20ede5..29d9a70 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,11 +47,12 @@ lib_deps = Update bblanchon/ArduinoJson @ 7.0.4 olikraus/U8g2 @ 2.35.19 + robtillaart/CRC @ 1.0.3 mathieucarbou/Async TCP @ 3.1.4 mathieucarbou/ESP Async WebServer @ 2.10.8 mathieucarbou/MycilaConfig @ 3.0.1 mathieucarbou/MycilaDS18 @ 3.0.2 - mathieucarbou/MycilaESPConnect @ 4.3.1 + mathieucarbou/MycilaESPConnect @ 4.3.2 mathieucarbou/MycilaEasyDisplay @ 3.0.1 mathieucarbou/MycilaHADiscovery @ 2.2.1 mathieucarbou/MycilaJSY @ 9.0.8 diff --git a/src/Website.cpp b/src/Website.cpp index bb45a8f..bd36abd 100644 --- a/src/Website.cpp +++ b/src/Website.cpp @@ -551,6 +551,7 @@ void YaSolR::WebsiteClass::updateCards() { _networkWiFiSSID.set(ESPConnect.getWiFiSSID().c_str()); _relay1SwitchCount.set(String(relay1.getSwitchCount()).c_str()); _relay2SwitchCount.set(String(relay2.getSwitchCount()).c_str()); + _jsyRemoteUdpRate.set((String(jsyRemoteUdpRate.rate()) + " msg/s").c_str()); _time.set(Mycila::Time::getLocalStr().c_str()); _uptime.set(Mycila::Time::toDHHMMSS(Mycila::System.getUptime()).c_str()); #ifdef APP_MODEL_TRIAL diff --git a/src/init/Events.cpp b/src/init/Events.cpp index 26f171b..fee3b19 100644 --- a/src/init/Events.cpp +++ b/src/init/Events.cpp @@ -346,25 +346,35 @@ Mycila::Task initEventsTask("Init Events", [](void* params) { }); udp.onPacket([](AsyncUDPPacket packet) { + // [0] uint8_t == message type + // [1] sizeof(Mycila::JSYData) + // [sizeOfBody] uint32_t == CRC32 + constexpr size_t sizeOfJSYData = sizeof(Mycila::JSYData); + constexpr size_t sizeOfBody = 1 + sizeOfJSYData; + constexpr size_t sizeOfCRC32 = sizeof(uint32_t); + constexpr size_t sizeOfUDP = sizeOfBody + sizeOfCRC32; + size_t len = packet.length(); - uint8_t* data = packet.data(); - if (!len) + if (len != sizeOfUDP) return; + + uint8_t* data = packet.data(); const uint8_t type = data[0]; - data++; - len--; - switch (type) { - case YASOLR_UDP_MSG_TYPE_JSY_DATA: - if (len == sizeof(Mycila::JSYData)) { - Mycila::JSYData jsyData; - memcpy(&jsyData, data, sizeof(Mycila::JSYData)); - if (grid.updateRemoteJSYData(jsyData)) { - routingTask.resume(); - } - } - break; - default: - break; + + if (type != YASOLR_UDP_MSG_TYPE_JSY_DATA) + return; + + FastCRC32 crc32; + crc32.add(data, sizeOfBody); + uint32_t crc = crc32.calc(); + if (memcmp(&crc, data + sizeOfBody, sizeOfCRC32) != 0) + return; + + Mycila::JSYData jsyData; + memcpy(&jsyData, data + 1, sizeOfJSYData); + jsyRemoteUdpRate.add(millis() / 1000.0f); + if (grid.updateRemoteJSYData(jsyData)) { + routingTask.resume(); } }); }); diff --git a/src/main.cpp b/src/main.cpp index ac00a57..d424a12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,6 +40,8 @@ Mycila::RouterOutput output2("output2", dimmerO2, bypassRelayO2, ds18O2, grid, p Mycila::RouterRelay routerRelay1(relay1); Mycila::RouterRelay routerRelay2(relay2); +Mycila::CircularBuffer jsyRemoteUdpRate; + AsyncWebServer webServer(80); AsyncUDP udp; ESPDash dashboard = ESPDash(&webServer, "/dashboard", false);