From a69a66ec518ea5b57f1f802d62eb90be99b13775 Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 08:20:51 +0200 Subject: [PATCH 1/8] Migrace na ESP32 --- .gitignore | 1 + LSSensor.ino | 289 ++++++++++++++++++++++++++++++----------------- ota.cpp | 78 +++++++++++++ ota.h | 3 + secret_default.h | 13 +++ sketch.yaml | 13 ++- 6 files changed, 289 insertions(+), 108 deletions(-) create mode 100644 ota.cpp create mode 100644 ota.h diff --git a/.gitignore b/.gitignore index 0e56cf2..53526fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ config.h +secret.h diff --git a/LSSensor.ino b/LSSensor.ino index b607bdc..9bd2fa9 100644 --- a/LSSensor.ino +++ b/LSSensor.ino @@ -1,169 +1,252 @@ -#include -#include -#include +#include +#include +#include +#include "time.h" +#include "esp_task_wdt.h" #include "config.h" #include "secret.h" -#include +#include "ota.h" #ifndef FW_VERSION #define FW_VERSION 0 #endif -#define LSSensorPIN1 2 -#define LSSensorPIN2 3 -#define SENDINTERVAL 5 * 60 * 1000 //5 minut - -void MQTTMessageReceive(char* topic, uint8_t* payload, uint16_t length) { } -void OnBusy(uint8_t count); -void DataTimeout(); -MQTTConnectData mqttConnectData = { MQTTHost, 1883, "WattMeter", MQTTUsername, MQTTPassword, "", 0, false, "", false, 0x0F }; - -SoftwareSerial serial(4, 5); -EspDrv espDrv(&serial); -MQTTClient mqttClient(&espDrv, MQTTMessageReceive); +#define LSSensorPIN1 32 +#define LSSensorPIN2 33 +#define PIN_PULL INPUT +#define PIN_EDGE RISING +#define PULSE_DEBOUNCE_MS 84 +#define PIN_DIAG 1 +#define PIN_DIAG_INTERVAL_MS 500UL +#define PULSE_INTERVAL 60000UL +#define DIAG_INTERVAL 300000UL +#define WDT_TIMEOUT_S 90 +#define WIFI_CONNECT_TIMEOUT_MS 15000UL +#define TIME_SYNC_TIMEOUT_MS 15000UL +#define MQTT_TLS_PORT 8883 +#define TIME_VALID_THRESHOLD 1700000000UL + +WiFiClientSecure net; +PubSubClient mqtt(net); + +volatile uint32_t counter1 = 0; +volatile uint32_t counter2 = 0; +volatile uint32_t lastTime1 = 0; +volatile uint32_t lastTime2 = 0; +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + char data[32]; -int wattMetter1Counter = 0; -int wattMetter2Counter = 0; -unsigned long lastSendToMQTT = 0; -unsigned long lastTime = 0; -bool closeRequired = false; +unsigned long lastPulseSend = 0; +unsigned long lastDiagSend = 0; +unsigned long lastPinDiag = 0; +bool initialZeroSent = false; +bool timeSynced = false; #pragma pack(push, 1) struct DiagData { uint32_t uptime; - uint16_t freeRam; + uint16_t freeRamKb; uint16_t wifiReconn; uint16_t mqttFailCount; uint8_t resetReason; uint16_t loopMaxMs; int8_t rssi; + uint16_t fwVersion; + uint16_t otaFailCount; }; #pragma pack(pop) +static_assert(sizeof(DiagData) == 18, "DiagData wire layout must stay 18 bytes"); DiagData currentDiagData; +uint16_t otaFailures = 0; -extern int __heap_start, *__brkval; -int freeRam() { - int v; - return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); +uint16_t heapKb() +{ + return (uint16_t)(ESP.getFreeHeap() / 1024); +} + +void IRAM_ATTR WattMeter1Received() +{ + uint32_t t = millis(); + portENTER_CRITICAL_ISR(&mux); + if(t - lastTime1 > PULSE_DEBOUNCE_MS) + { + counter1++; + lastTime1 = t; + } + portEXIT_CRITICAL_ISR(&mux); } -void WattMetter1Received() +void IRAM_ATTR WattMeter2Received() { - unsigned long time = millis(); - if(time - lastTime > 84) + uint32_t t = millis(); + portENTER_CRITICAL_ISR(&mux); + if(t - lastTime2 > PULSE_DEBOUNCE_MS) { - wattMetter1Counter++; - lastTime = time; + counter2++; + lastTime2 = t; } + portEXIT_CRITICAL_ISR(&mux); } -unsigned long lastTime2 = 0; -void WattMetter2Received() +bool SyncTime() { - unsigned long time = millis(); - if(time - lastTime2 > 84) + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + unsigned long start = millis(); + time_t now = time(nullptr); + while(now < TIME_VALID_THRESHOLD && millis() - start < TIME_SYNC_TIMEOUT_MS) { - wattMetter2Counter++; - lastTime2 = time; + esp_task_wdt_reset(); + delay(200); + now = time(nullptr); } + return now >= TIME_VALID_THRESHOLD; } bool Connect() { - int wifiStatus = espDrv.GetConnectionStatus(); - bool wifiConnected = wifiStatus == WL_CONNECTED; - if(wifiStatus == WL_DISCONNECTED || wifiStatus == WL_IDLE_STATUS) + if(WiFi.status() != WL_CONNECTED) { - wifiConnected = espDrv.Connect(WifiSSID, WifiPassword); - if(currentDiagData.wifiReconn < 65535) + if(currentDiagData.wifiReconn < 65535) { currentDiagData.wifiReconn++; } + WiFi.begin(WifiSSID, WifiPassword); + unsigned long start = millis(); + while(WiFi.status() != WL_CONNECTED && millis() - start < WIFI_CONNECT_TIMEOUT_MS) + { + esp_task_wdt_reset(); + delay(100); + } } - if(wifiConnected) + if(WiFi.status() != WL_CONNECTED) { - - bool isConnected = mqttClient.IsConnected(); - if(!isConnected) + return false; + } + if(!timeSynced) + { + timeSynced = SyncTime(); + if(!timeSynced) { - bool ok = mqttClient.Connect(mqttConnectData); - if(!ok && currentDiagData.mqttFailCount < 65535) currentDiagData.mqttFailCount++; - return ok; + return false; } - else + } + if(!mqtt.connected()) + { + bool ok = mqtt.connect("WattMeter", MQTTUsername, MQTTPassword); + if(!ok && currentDiagData.mqttFailCount < 65535) { - return true; + currentDiagData.mqttFailCount++; } + return ok; } - return false; + return true; } -void DataTimeout() -{ - closeRequired = true; -} -void OnBusy(uint8_t count) +#if PIN_DIAG +void probePin(uint8_t pin, const char* name) { - if(count > 10) - { - closeRequired = true; - } + pinMode(pin, INPUT_PULLUP); + delay(20); + int up = digitalRead(pin); + pinMode(pin, INPUT_PULLDOWN); + delay(20); + int down = digitalRead(pin); + Serial.printf("%s (GPIO%u): PULLUP=%d PULLDOWN=%d\n", name, pin, up, down); } +#endif void setup() { - currentDiagData.resetReason = MCUSR; - MCUSR = 0; - // put your setup code here, to run once: - pinMode(LSSensorPIN1, INPUT); - pinMode(LSSensorPIN2, INPUT); - Serial.begin(57600); - serial.begin(57600); - espDrv.Init(16); - espDrv.OnBusy = OnBusy; - espDrv.DataTimeout = DataTimeout; - espDrv.Connect(WifiSSID, WifiPassword); - attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMetter1Received, RISING); - attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMetter2Received, RISING); + currentDiagData.resetReason = (uint8_t)esp_reset_reason(); + Serial.begin(115200); +#if PIN_DIAG + delay(300); + Serial.println("PIN DIAG (klidova uroven):"); + probePin(LSSensorPIN1, "PIN1"); + probePin(LSSensorPIN2, "PIN2"); +#endif + pinMode(LSSensorPIN1, PIN_PULL); + pinMode(LSSensorPIN2, PIN_PULL); + WiFi.mode(WIFI_STA); + WiFi.begin(WifiSSID, WifiPassword); + net.setCACert(MQTTCACert); + mqtt.setServer(MQTTHost, MQTT_TLS_PORT); + mqtt.setBufferSize(256); + mqtt.setKeepAlive(60); + attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMeter1Received, PIN_EDGE); + attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMeter2Received, PIN_EDGE); + esp_task_wdt_config_t wdtConfig = { + .timeout_ms = WDT_TIMEOUT_S * 1000, + .idle_core_mask = 0, + .trigger_panic = true + }; + esp_task_wdt_reconfigure(&wdtConfig); + esp_task_wdt_add(NULL); Serial.println("Setup OK"); - wdt_enable(WDTO_8S); } void loop() { unsigned long currentMillis = millis(); - wdt_reset(); - if(closeRequired) + esp_task_wdt_reset(); + mqtt.loop(); + +#if PIN_DIAG + if(currentMillis - lastPinDiag >= PIN_DIAG_INTERVAL_MS) { - espDrv.Close(); - closeRequired = false; + lastPinDiag = currentMillis; + Serial.printf("P1=%d c1=%lu P2=%d c2=%lu\n", + digitalRead(LSSensorPIN1), (unsigned long)counter1, + digitalRead(LSSensorPIN2), (unsigned long)counter2); } - mqttClient.Loop(); - if(currentMillis - lastSendToMQTT >= 300000) - { - detachInterrupt(digitalPinToInterrupt(LSSensorPIN1)); - detachInterrupt(digitalPinToInterrupt(LSSensorPIN2)); - if(Connect()) +#endif + + bool pulseDue = !initialZeroSent || currentMillis - lastPulseSend >= PULSE_INTERVAL; + bool diagDue = currentMillis - lastDiagSend >= DIAG_INTERVAL; + + if(pulseDue || diagDue) + { + portENTER_CRITICAL(&mux); + uint32_t c1 = counter1; + uint32_t c2 = counter2; + portEXIT_CRITICAL(&mux); + + bool connected = Connect(); + + if(pulseDue) + { + if(connected) + { + if(!initialZeroSent) + { + sprintf(data, "{\"V\":%lu,\"S\":%lu}", 0UL, 0UL); + mqtt.publish(ELCONSUMPTION, data); + initialZeroSent = true; + } + sprintf(data, "{\"V\":%lu,\"S\":%lu}", (unsigned long)c1, (unsigned long)c2); + mqtt.publish(ELCONSUMPTION, data); + } + lastPulseSend = currentMillis; + } + + if(diagDue) { - sprintf(data, "{\"V\":%d,\"S\":%d}", 0, 0); - mqttClient.Publish(ELCONSUMPTION, data); - sprintf(data, "{\"V\":%d,\"S\":%d}", wattMetter1Counter, wattMetter2Counter); - mqttClient.Publish(ELCONSUMPTION, data); - - wattMetter1Counter = 0; - wattMetter2Counter = 0; - - currentDiagData.uptime = currentMillis / 60000UL; - currentDiagData.freeRam = freeRam(); - currentDiagData.rssi = espDrv.GetRssi(); - mqttClient.Publish(LSSENSOR_DIAG, (const uint8_t*)¤tDiagData, sizeof(DiagData), false); - currentDiagData.loopMaxMs = 0; - mqttClient.Disconnect(); + if(connected) + { + currentDiagData.uptime = currentMillis / 60000UL; + currentDiagData.freeRamKb = heapKb(); + currentDiagData.rssi = (int8_t)WiFi.RSSI(); + currentDiagData.fwVersion = (uint16_t)FW_VERSION; + currentDiagData.otaFailCount = otaFailures; + mqtt.publish(LSSENSOR_DIAG, (const uint8_t*)¤tDiagData, sizeof(DiagData), false); + currentDiagData.loopMaxMs = 0; + } + lastDiagSend = currentMillis; } - lastSendToMQTT = currentMillis; - attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMetter1Received, RISING); - attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMetter2Received, RISING); } + + otaLoop(); + unsigned long iterDur = millis() - currentMillis; - if(iterDur > currentDiagData.loopMaxMs) + if(iterDur > currentDiagData.loopMaxMs) { currentDiagData.loopMaxMs = (iterDur > 65535UL) ? 65535 : (uint16_t)iterDur; } diff --git a/ota.cpp b/ota.cpp new file mode 100644 index 0000000..dac0258 --- /dev/null +++ b/ota.cpp @@ -0,0 +1,78 @@ +#include "ota.h" +#include +#include +#include +#include +#include "time.h" +#include "config.h" +#include "secret.h" + +#ifndef OTA_CHECK_INTERVAL_MS +#define OTA_CHECK_INTERVAL_MS (60UL * 60UL * 1000UL) +#endif +#ifndef FW_VERSION +#define FW_VERSION 0 +#endif +#define OTA_TIME_VALID_THRESHOLD 1700000000UL + +extern uint16_t otaFailures; + +unsigned long otaLastCheck = 0; +bool otaFirstRun = true; + +void doOTA() +{ + Serial.printf("OTA: kontrola z %s (verze %d)\n", OtaUrl, (int)FW_VERSION); + WiFiClientSecure client; + client.setCACert(OtaRootCA); + + HTTPUpdate updater(30000); + updater.setAuthorization(OtaUser, OtaPassword); + updater.onStart([]() { esp_task_wdt_reset(); }); + updater.onEnd([]() { esp_task_wdt_reset(); }); + updater.onProgress([](int cur, int total) { esp_task_wdt_reset(); }); + updater.onError([](int err) { Serial.printf("OTA: chyba %d\n", err); }); + updater.rebootOnUpdate(true); + + t_httpUpdate_return ret = updater.update(client, OtaUrl, String((int)FW_VERSION)); + + switch(ret) + { + case HTTP_UPDATE_FAILED: + Serial.printf("OTA: SELHALA (%d): %s\n", updater.getLastError(), updater.getLastErrorString().c_str()); + if(otaFailures < 65535) + { + otaFailures++; + } + break; + case HTTP_UPDATE_NO_UPDATES: + Serial.println("OTA: firmware je aktualni."); + break; + case HTTP_UPDATE_OK: + Serial.println("OTA: OK."); + break; + } +} + +void otaLoop() +{ + if(!otaFirstRun && millis() - otaLastCheck < OTA_CHECK_INTERVAL_MS) + { + return; + } + if(WiFi.status() != WL_CONNECTED) + { + return; + } + if(time(nullptr) < OTA_TIME_VALID_THRESHOLD) + { + return; + } + if(otaFirstRun) + { + Serial.printf("OTA: interval kontroly %lu ms\n", (unsigned long)OTA_CHECK_INTERVAL_MS); + } + otaFirstRun = false; + otaLastCheck = millis(); + doOTA(); +} diff --git a/ota.h b/ota.h new file mode 100644 index 0000000..bcae739 --- /dev/null +++ b/ota.h @@ -0,0 +1,3 @@ +#pragma once + +void otaLoop(); diff --git a/secret_default.h b/secret_default.h index 95b1378..90a79e5 100644 --- a/secret_default.h +++ b/secret_default.h @@ -3,3 +3,16 @@ #define MQTTUsername "MQTTUsername" #define MQTTPassword "MQTTPassword" #define MQTTHost "MQTTHost" +#define MQTTCACert \ +"-----BEGIN CERTIFICATE-----\n" \ +"REPLACE_WITH_BROKER_CA_CERT_PEM\n" \ +"-----END CERTIFICATE-----\n" +#define OtaUrl "https://OtaHost/lssensor.bin" +#define OtaUser "OtaUser" +#define OtaPassword "OtaPassword" +#define OTA_CHECK_INTERVAL_MS 60000 +#define OtaRootCA R"EOF( +-----BEGIN CERTIFICATE----- +REPLACE_WITH_OTA_SERVER_ROOT_CA_PEM +-----END CERTIFICATE----- +)EOF" diff --git a/sketch.yaml b/sketch.yaml index c4c7be5..3bad03d 100644 --- a/sketch.yaml +++ b/sketch.yaml @@ -1,8 +1,11 @@ profiles: - LSSensor_Uno: - fqbn: arduino:avr:uno + LSSensor_ESP32: + fqbn: esp32:esp32:esp32:PartitionScheme=default platforms: - - platform: arduino:avr (1.8.7) + - platform: esp32:esp32 (3.2.0) + platform_index_url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json libraries: - - dir: MQTTESP8266 -default_profile: LSSensor_Uno + - PubSubClient (2.8.0) + port_config: + baudrate: 115200 +default_profile: LSSensor_ESP32 From 502e7bb45d9efb7c83881ad0d9872e445b64af9d Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 10:21:38 +0200 Subject: [PATCH 2/8] =?UTF-8?q?P=C5=99esun?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LSSensor.ino => src/ESP32/ESP32.ino | 0 config_default.h => src/ESP32/config_default.h | 0 ota.cpp => src/ESP32/ota.cpp | 0 ota.h => src/ESP32/ota.h | 0 secret_default.h => src/ESP32/secret_default.h | 0 sketch.yaml => src/ESP32/sketch.yaml | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename LSSensor.ino => src/ESP32/ESP32.ino (100%) rename config_default.h => src/ESP32/config_default.h (100%) rename ota.cpp => src/ESP32/ota.cpp (100%) rename ota.h => src/ESP32/ota.h (100%) rename secret_default.h => src/ESP32/secret_default.h (100%) rename sketch.yaml => src/ESP32/sketch.yaml (100%) diff --git a/LSSensor.ino b/src/ESP32/ESP32.ino similarity index 100% rename from LSSensor.ino rename to src/ESP32/ESP32.ino diff --git a/config_default.h b/src/ESP32/config_default.h similarity index 100% rename from config_default.h rename to src/ESP32/config_default.h diff --git a/ota.cpp b/src/ESP32/ota.cpp similarity index 100% rename from ota.cpp rename to src/ESP32/ota.cpp diff --git a/ota.h b/src/ESP32/ota.h similarity index 100% rename from ota.h rename to src/ESP32/ota.h diff --git a/secret_default.h b/src/ESP32/secret_default.h similarity index 100% rename from secret_default.h rename to src/ESP32/secret_default.h diff --git a/sketch.yaml b/src/ESP32/sketch.yaml similarity index 100% rename from sketch.yaml rename to src/ESP32/sketch.yaml From ef79a8f248c6a7ee8f1fe2c39d013db9078bdb52 Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 10:24:37 +0200 Subject: [PATCH 3/8] Arduino original --- src/Arduino/Arduino.ino | 170 +++++++++++++++++++++++++++++++++++ src/Arduino/config_default.h | 2 + src/Arduino/secret_default.h | 5 ++ src/Arduino/sketch.yaml | 8 ++ 4 files changed, 185 insertions(+) create mode 100644 src/Arduino/Arduino.ino create mode 100644 src/Arduino/config_default.h create mode 100644 src/Arduino/secret_default.h create mode 100644 src/Arduino/sketch.yaml diff --git a/src/Arduino/Arduino.ino b/src/Arduino/Arduino.ino new file mode 100644 index 0000000..b607bdc --- /dev/null +++ b/src/Arduino/Arduino.ino @@ -0,0 +1,170 @@ +#include +#include +#include +#include "config.h" +#include "secret.h" +#include + +#ifndef FW_VERSION +#define FW_VERSION 0 +#endif +#define LSSensorPIN1 2 +#define LSSensorPIN2 3 +#define SENDINTERVAL 5 * 60 * 1000 //5 minut + +void MQTTMessageReceive(char* topic, uint8_t* payload, uint16_t length) { } +void OnBusy(uint8_t count); +void DataTimeout(); +MQTTConnectData mqttConnectData = { MQTTHost, 1883, "WattMeter", MQTTUsername, MQTTPassword, "", 0, false, "", false, 0x0F }; + +SoftwareSerial serial(4, 5); +EspDrv espDrv(&serial); +MQTTClient mqttClient(&espDrv, MQTTMessageReceive); +char data[32]; +int wattMetter1Counter = 0; +int wattMetter2Counter = 0; +unsigned long lastSendToMQTT = 0; +unsigned long lastTime = 0; +bool closeRequired = false; + +#pragma pack(push, 1) +struct DiagData { + uint32_t uptime; + uint16_t freeRam; + uint16_t wifiReconn; + uint16_t mqttFailCount; + uint8_t resetReason; + uint16_t loopMaxMs; + int8_t rssi; +}; +#pragma pack(pop) + +DiagData currentDiagData; + +extern int __heap_start, *__brkval; +int freeRam() { + int v; + return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); +} + +void WattMetter1Received() +{ + unsigned long time = millis(); + if(time - lastTime > 84) + { + wattMetter1Counter++; + lastTime = time; + } +} + +unsigned long lastTime2 = 0; +void WattMetter2Received() +{ + unsigned long time = millis(); + if(time - lastTime2 > 84) + { + wattMetter2Counter++; + lastTime2 = time; + } +} + +bool Connect() +{ + int wifiStatus = espDrv.GetConnectionStatus(); + bool wifiConnected = wifiStatus == WL_CONNECTED; + if(wifiStatus == WL_DISCONNECTED || wifiStatus == WL_IDLE_STATUS) + { + wifiConnected = espDrv.Connect(WifiSSID, WifiPassword); + if(currentDiagData.wifiReconn < 65535) + { + currentDiagData.wifiReconn++; + } + } + if(wifiConnected) + { + + bool isConnected = mqttClient.IsConnected(); + if(!isConnected) + { + bool ok = mqttClient.Connect(mqttConnectData); + if(!ok && currentDiagData.mqttFailCount < 65535) currentDiagData.mqttFailCount++; + return ok; + } + else + { + return true; + } + } + return false; +} + +void DataTimeout() +{ + closeRequired = true; +} +void OnBusy(uint8_t count) +{ + if(count > 10) + { + closeRequired = true; + } +} + +void setup() { + currentDiagData.resetReason = MCUSR; + MCUSR = 0; + // put your setup code here, to run once: + pinMode(LSSensorPIN1, INPUT); + pinMode(LSSensorPIN2, INPUT); + Serial.begin(57600); + serial.begin(57600); + espDrv.Init(16); + espDrv.OnBusy = OnBusy; + espDrv.DataTimeout = DataTimeout; + espDrv.Connect(WifiSSID, WifiPassword); + attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMetter1Received, RISING); + attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMetter2Received, RISING); + Serial.println("Setup OK"); + wdt_enable(WDTO_8S); +} + +void loop() { + unsigned long currentMillis = millis(); + wdt_reset(); + if(closeRequired) + { + espDrv.Close(); + closeRequired = false; + } + mqttClient.Loop(); + if(currentMillis - lastSendToMQTT >= 300000) + { + detachInterrupt(digitalPinToInterrupt(LSSensorPIN1)); + detachInterrupt(digitalPinToInterrupt(LSSensorPIN2)); + if(Connect()) + { + sprintf(data, "{\"V\":%d,\"S\":%d}", 0, 0); + mqttClient.Publish(ELCONSUMPTION, data); + sprintf(data, "{\"V\":%d,\"S\":%d}", wattMetter1Counter, wattMetter2Counter); + mqttClient.Publish(ELCONSUMPTION, data); + + wattMetter1Counter = 0; + wattMetter2Counter = 0; + + currentDiagData.uptime = currentMillis / 60000UL; + currentDiagData.freeRam = freeRam(); + currentDiagData.rssi = espDrv.GetRssi(); + mqttClient.Publish(LSSENSOR_DIAG, (const uint8_t*)¤tDiagData, sizeof(DiagData), false); + currentDiagData.loopMaxMs = 0; + mqttClient.Disconnect(); + } + lastSendToMQTT = currentMillis; + attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMetter1Received, RISING); + attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMetter2Received, RISING); + } + unsigned long iterDur = millis() - currentMillis; + if(iterDur > currentDiagData.loopMaxMs) + { + currentDiagData.loopMaxMs = (iterDur > 65535UL) ? 65535 : (uint16_t)iterDur; + } +} diff --git a/src/Arduino/config_default.h b/src/Arduino/config_default.h new file mode 100644 index 0000000..e7e6f4f --- /dev/null +++ b/src/Arduino/config_default.h @@ -0,0 +1,2 @@ +#define ELCONSUMPTION "ELCONSUMPTION" +#define LSSENSOR_DIAG "LSSENSOR_DIAG" diff --git a/src/Arduino/secret_default.h b/src/Arduino/secret_default.h new file mode 100644 index 0000000..95b1378 --- /dev/null +++ b/src/Arduino/secret_default.h @@ -0,0 +1,5 @@ +#define WifiSSID "WifiSSID" +#define WifiPassword "WifiPassword" +#define MQTTUsername "MQTTUsername" +#define MQTTPassword "MQTTPassword" +#define MQTTHost "MQTTHost" diff --git a/src/Arduino/sketch.yaml b/src/Arduino/sketch.yaml new file mode 100644 index 0000000..c4c7be5 --- /dev/null +++ b/src/Arduino/sketch.yaml @@ -0,0 +1,8 @@ +profiles: + LSSensor_Uno: + fqbn: arduino:avr:uno + platforms: + - platform: arduino:avr (1.8.7) + libraries: + - dir: MQTTESP8266 +default_profile: LSSensor_Uno From 56fb44fa20a1f8c46c782032c1890380e3db8b83 Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 10:31:54 +0200 Subject: [PATCH 4/8] build and deploy --- .github/workflows/build.yml | 11 +-- .github/workflows/deployment.yml | 4 +- .github/workflows/deployment_esp32.yml | 98 ++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/deployment_esp32.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ce7be8..e2d5bdb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,9 +29,12 @@ jobs: - name: Set configs run: | - cp config_default.h config.h - cp secret_default.h secret.h + cp ./src/Arduino/config_default.h ./src/Arduino/config.h + cp ./src/Arduino/secret_default.h ./src/Arduino/secret.h + cp ./src/ESP32/config_default.h ./src/ESP32/config.h + cp ./src/ESP32/secret_default.h ./src/ESP32/secret.h - - name: Compile Arduino project + - name: Compile run: | - arduino-cli compile --profile LSSensor_Uno ./ + arduino-cli compile ./src/Arduino + arduino-cli compile ./src/ESP32 diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 014d7f8..2646074 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -1,4 +1,4 @@ -name: Deploy LS Sensor on Environment +name: Deploy Arduino LS Sensor on Environment on: workflow_dispatch: @@ -23,5 +23,5 @@ jobs: environment: ${{ inputs.environment }} serial_port: ${{ inputs.serial_port }} device_name: LSSensor - source_dir: "." + source_dir: "./src/Arduino" secrets: inherit diff --git a/.github/workflows/deployment_esp32.yml b/.github/workflows/deployment_esp32.yml new file mode 100644 index 0000000..f986aaa --- /dev/null +++ b/.github/workflows/deployment_esp32.yml @@ -0,0 +1,98 @@ +name: Deploy LS Sensor ESP32 on Environment + +on: + workflow_dispatch: + inputs: + serial_port: + required: false + type: string + environment: + required: true + type: choice + options: + - Home + +permissions: + contents: read + +jobs: + deployment: + runs-on: [self-hosted, iot] + environment: ${{ inputs.environment }} + env: + REPO: ${{ vars.REPO }} + steps: + + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install required board core + run: | + & "$Env:RUNNER_TOOL_CACHE\arduino-cli\arduino-cli.exe" --config-file config.yaml core update-index + + + - uses: zefek/GithubActions/Actions/deployConfig@v1 + with: + repo: ${{ env.REPO }} + auth: ${{ secrets.REPO_TOKEN }} + dest: ${{ runner.temp }}\secrets + device: LSSensor + outdir: ${{ github.workspace }}\src + + - name: Compile Arduino project + run: | + & "$Env:RUNNER_TOOL_CACHE\arduino-cli\arduino-cli.exe" --config-file config.yaml compile ./src/ESP32 --output-dir build --build-property "compiler.cpp.extra_flags=-DFW_VERSION=${{ github.run_number }}" + + - name: Upload firmware to Arduino + if: ${{ github.event.inputs.serial_port != '' }} + run: | + $port = "${{ github.event.inputs.serial_port }}" + & "$Env:RUNNER_TOOL_CACHE\arduino-cli\arduino-cli.exe" --config-file config.yaml upload ./src/ESP32 --port $port --input-dir build + + - name: Uložení firmware + if: ${{ github.event.inputs.serial_port == '' }} + shell: powershell + env: + FIRMWARE_TARGET_PATH: ${{ vars.FIRMWARE_TARGET_PATH }} + FW_VERSION: ${{ github.run_number }} + GIT_SHA: ${{ github.sha }} + run: | + $ErrorActionPreference = 'Stop' + if (-not $env:FIRMWARE_TARGET_PATH) { throw "FIRMWARE_TARGET_PATH variable is not set" } + + $buildDir = Join-Path $env:GITHUB_WORKSPACE 'build' + $version = $env:FW_VERSION + + # Aplikační image pro OTA (ESP32 nahrává *.ino.bin, ne bootloader/partitions) + $appBin = Get-ChildItem -Path $buildDir -Filter '*.ino.bin' -File | Select-Object -First 1 + if (-not $appBin) { throw "Application firmware (*.ino.bin) not found in $buildDir" } + + # 1) Historie: celý build do podadresáře pojmenovaného verzí + $versionDir = Join-Path $env:FIRMWARE_TARGET_PATH $version + New-Item -ItemType Directory -Path $versionDir -Force | Out-Null + Copy-Item -Path "$buildDir\*" -Destination $versionDir -Recurse -Force + + # 2) OTA: pevná cesta na poslední firmware + manifest + if (-not (Test-Path $env:FIRMWARE_TARGET_PATH)) { + New-Item -ItemType Directory -Path $env:FIRMWARE_TARGET_PATH -Force | Out-Null + } + $latestBin = Join-Path $env:FIRMWARE_TARGET_PATH 'firmware.bin' + Copy-Item -Path $appBin.FullName -Destination $latestBin -Force + + $hash = (Get-FileHash -Path $latestBin -Algorithm SHA256).Hash.ToLower() + $size = (Get-Item $latestBin).Length + + # Jednoduchý key=value manifest – ESP32 ho rozparsuje bez JSON knihovny + $manifest = @( + "version=$version" + "file=firmware.bin" + "size=$size" + "sha256=$hash" + "commit=$($env:GIT_SHA)" + ) + $manifestPath = Join-Path $env:FIRMWARE_TARGET_PATH 'manifest.txt' + # LF konce řádků a bez BOM – snazší parsování na zařízení + [System.IO.File]::WriteAllText($manifestPath, ($manifest -join "`n") + "`n") + + Write-Host "Firmware verze $version uložen do $versionDir; OTA: $latestBin (manifest $manifestPath)" + From 9eed2bd3ad4bf66ce972f8ea2ef7ffc4c96e69f9 Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 10:33:41 +0200 Subject: [PATCH 5/8] mqttesp8266 --- src/Arduino/sketch.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Arduino/sketch.yaml b/src/Arduino/sketch.yaml index c4c7be5..6810ade 100644 --- a/src/Arduino/sketch.yaml +++ b/src/Arduino/sketch.yaml @@ -4,5 +4,5 @@ profiles: platforms: - platform: arduino:avr (1.8.7) libraries: - - dir: MQTTESP8266 -default_profile: LSSensor_Uno + - dir: ../../MQTTESP8266 +default_profile: LSSensor_Uno \ No newline at end of file From b9decc7bd99e4d53e66a0bce873166fc6780771b Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 23:17:58 +0200 Subject: [PATCH 6/8] deployment --- .github/workflows/deployment.yml | 2 +- .github/workflows/deployment_esp32.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 2646074..0378dd6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -22,6 +22,6 @@ jobs: with: environment: ${{ inputs.environment }} serial_port: ${{ inputs.serial_port }} - device_name: LSSensor + device_name: LSSensorArduino source_dir: "./src/Arduino" secrets: inherit diff --git a/.github/workflows/deployment_esp32.yml b/.github/workflows/deployment_esp32.yml index f986aaa..6c83640 100644 --- a/.github/workflows/deployment_esp32.yml +++ b/.github/workflows/deployment_esp32.yml @@ -36,8 +36,8 @@ jobs: repo: ${{ env.REPO }} auth: ${{ secrets.REPO_TOKEN }} dest: ${{ runner.temp }}\secrets - device: LSSensor - outdir: ${{ github.workspace }}\src + device: LSSensorESP32 + outdir: ${{ github.workspace }}\src\ESP32 - name: Compile Arduino project run: | From 3e8b69974b694d9b84d2c7308000f5a1cd113f89 Mon Sep 17 00:00:00 2001 From: Zefek Date: Mon, 20 Jul 2026 23:29:39 +0200 Subject: [PATCH 7/8] commit --- .github/workflows/deployment.yml | 2 +- .github/workflows/deployment_esp32.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0378dd6..07433eb 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -18,7 +18,7 @@ permissions: jobs: deployment: - uses: Zefek/GithubActions/.github/workflows/arduino-build-deploy.yml@v1 + uses: Zefek/GithubActions/.github/workflows/arduino-build-deploy.yml@c2872a07ea6ff49f3105cf3b8faef0e7169236c7 with: environment: ${{ inputs.environment }} serial_port: ${{ inputs.serial_port }} diff --git a/.github/workflows/deployment_esp32.yml b/.github/workflows/deployment_esp32.yml index 6c83640..7b5867e 100644 --- a/.github/workflows/deployment_esp32.yml +++ b/.github/workflows/deployment_esp32.yml @@ -31,7 +31,7 @@ jobs: & "$Env:RUNNER_TOOL_CACHE\arduino-cli\arduino-cli.exe" --config-file config.yaml core update-index - - uses: zefek/GithubActions/Actions/deployConfig@v1 + - uses: zefek/GithubActions/Actions/deployConfig@c2872a07ea6ff49f3105cf3b8faef0e7169236c7 with: repo: ${{ env.REPO }} auth: ${{ secrets.REPO_TOKEN }} From a9f04b017cf0d776a9e7a853ad6d4601f13df979 Mon Sep 17 00:00:00 2001 From: Zefek Date: Tue, 21 Jul 2026 07:48:52 +0200 Subject: [PATCH 8/8] =?UTF-8?q?Pin=20diag=20a=20=C3=BAprava=20arduina?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Arduino/Arduino.ino | 10 +++++++--- src/ESP32/ESP32.ino | 30 ------------------------------ 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/src/Arduino/Arduino.ino b/src/Arduino/Arduino.ino index b607bdc..2652c59 100644 --- a/src/Arduino/Arduino.ino +++ b/src/Arduino/Arduino.ino @@ -26,6 +26,7 @@ int wattMetter2Counter = 0; unsigned long lastSendToMQTT = 0; unsigned long lastTime = 0; bool closeRequired = false; +bool initialZeroSent = false; #pragma pack(push, 1) struct DiagData { @@ -143,8 +144,12 @@ void loop() { detachInterrupt(digitalPinToInterrupt(LSSensorPIN2)); if(Connect()) { - sprintf(data, "{\"V\":%d,\"S\":%d}", 0, 0); - mqttClient.Publish(ELCONSUMPTION, data); + if(!initialZeroSent) + { + sprintf(data, "{\"V\":%d,\"S\":%d}", 0, 0); + mqttClient.Publish(ELCONSUMPTION, data); + initialZeroSent = true; + } sprintf(data, "{\"V\":%d,\"S\":%d}", wattMetter1Counter, wattMetter2Counter); mqttClient.Publish(ELCONSUMPTION, data); @@ -156,7 +161,6 @@ void loop() { currentDiagData.rssi = espDrv.GetRssi(); mqttClient.Publish(LSSENSOR_DIAG, (const uint8_t*)¤tDiagData, sizeof(DiagData), false); currentDiagData.loopMaxMs = 0; - mqttClient.Disconnect(); } lastSendToMQTT = currentMillis; attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMetter1Received, RISING); diff --git a/src/ESP32/ESP32.ino b/src/ESP32/ESP32.ino index 9bd2fa9..3a1bdd2 100644 --- a/src/ESP32/ESP32.ino +++ b/src/ESP32/ESP32.ino @@ -15,7 +15,6 @@ #define PIN_PULL INPUT #define PIN_EDGE RISING #define PULSE_DEBOUNCE_MS 84 -#define PIN_DIAG 1 #define PIN_DIAG_INTERVAL_MS 500UL #define PULSE_INTERVAL 60000UL #define DIAG_INTERVAL 300000UL @@ -142,28 +141,9 @@ bool Connect() return true; } -#if PIN_DIAG -void probePin(uint8_t pin, const char* name) -{ - pinMode(pin, INPUT_PULLUP); - delay(20); - int up = digitalRead(pin); - pinMode(pin, INPUT_PULLDOWN); - delay(20); - int down = digitalRead(pin); - Serial.printf("%s (GPIO%u): PULLUP=%d PULLDOWN=%d\n", name, pin, up, down); -} -#endif - void setup() { currentDiagData.resetReason = (uint8_t)esp_reset_reason(); Serial.begin(115200); -#if PIN_DIAG - delay(300); - Serial.println("PIN DIAG (klidova uroven):"); - probePin(LSSensorPIN1, "PIN1"); - probePin(LSSensorPIN2, "PIN2"); -#endif pinMode(LSSensorPIN1, PIN_PULL); pinMode(LSSensorPIN2, PIN_PULL); WiFi.mode(WIFI_STA); @@ -189,16 +169,6 @@ void loop() { esp_task_wdt_reset(); mqtt.loop(); -#if PIN_DIAG - if(currentMillis - lastPinDiag >= PIN_DIAG_INTERVAL_MS) - { - lastPinDiag = currentMillis; - Serial.printf("P1=%d c1=%lu P2=%d c2=%lu\n", - digitalRead(LSSensorPIN1), (unsigned long)counter1, - digitalRead(LSSensorPIN2), (unsigned long)counter2); - } -#endif - bool pulseDue = !initialZeroSent || currentMillis - lastPulseSend >= PULSE_INTERVAL; bool diagDue = currentMillis - lastDiagSend >= DIAG_INTERVAL;