diff --git a/src/NodeHeizung.h b/src/NodeHeizung.h index 3ea35ad..aa75e10 100644 --- a/src/NodeHeizung.h +++ b/src/NodeHeizung.h @@ -57,39 +57,28 @@ public: // }; - void setup() override { - // TODO + Sensor *getSensor(const int index) override { + switch (index) { + case 0: return &keller; + case 1: return &abgas; + case 3: return &pufferVorlauf; + case 4: return &pufferRuecklauf; + case 5: return &pufferEingang; + case 6: return &pufferSpeicher; + case 7: return &pufferAusgang; + case 8: return &pufferZirkulation; + case 9: return &heizkreisVorlauf; + case 10: return &heizkreisRuecklauf; + case 11: return &abwasser; + case 12: return &reserve; + default: return nullptr; + } } - void loop() override { - keller.loop(); - abgas.loop(); +protected: + + void loopBeforeSensors() override { dallas.loop(); - pufferVorlauf.loop(); - pufferRuecklauf.loop(); - pufferEingang.loop(); - pufferSpeicher.loop(); - pufferAusgang.loop(); - pufferZirkulation.loop(); - heizkreisVorlauf.loop(); - heizkreisRuecklauf.loop(); - abwasser.loop(); - reserve.loop(); - } - - void toJson(JsonDocument& json) override { - keller.toJson(json); - abgas.toJson(json); - pufferVorlauf.toJson(json); - pufferRuecklauf.toJson(json); - pufferEingang.toJson(json); - pufferSpeicher.toJson(json); - pufferAusgang.toJson(json); - pufferZirkulation.toJson(json); - heizkreisVorlauf.toJson(json); - heizkreisRuecklauf.toJson(json); - abwasser.toJson(json); - reserve.toJson(json); } }; diff --git a/src/NodeTest.h b/src/NodeTest.h index 38668f3..959a326 100644 --- a/src/NodeTest.h +++ b/src/NodeTest.h @@ -1,8 +1,6 @@ #ifndef NODE_TEST_H #define NODE_TEST_H -#include - #include "patrix/Node.h" #include "patrix/sensor/Dallas.h" #include "patrix/sensor/DallasSensor.h" @@ -28,30 +26,18 @@ public: // }; - void setup() override { - // TODO - } - - void loop() override { - dallas.loop(); - test.loop(); - testraum.loop(); - const auto tt = test.isDue(); - const auto tr = testraum.isDue(); - if (tt || tr) { - JsonDocument json; - json["timestamp"] = time(nullptr); - toJson(json); - if (mqttPublish(WiFiClass::getHostname(), "state", json)) { - test.markSent(); - testraum.markSent(); - } + Sensor *getSensor(const int index) override { + switch (index) { + case 0: return &test; + case 1: return &testraum; + default: return nullptr; } } - void toJson(JsonDocument& json) override { - test.toJson(json); - testraum.toJson(json); +protected: + + void loopBeforeSensors() override { + dallas.loop(); } }; diff --git a/src/patrix/Node.h b/src/patrix/Node.h index 26fb024..079f854 100644 --- a/src/patrix/Node.h +++ b/src/patrix/Node.h @@ -1,7 +1,7 @@ #ifndef NODE_H #define NODE_H -#include +#include "sensor/Sensor.h" class Node { @@ -9,11 +9,47 @@ public: virtual ~Node() = default; - virtual void setup() = 0; + void toJson(const JsonObject& json) { + auto index = 0; + Sensor *sensor; + while ((sensor = getSensor(index++)) != nullptr) { + sensor->toJson(json[sensor->getName()].to()); + } + } - virtual void loop() = 0; + void send() { + JsonDocument json; + toJson(json.to()); + mqttPublish(String(WiFi.getHostname()) + "/json", json); + } - virtual void toJson(JsonDocument& json) = 0; + void loop() { + loopBeforeSensors(); + const auto changed = loopSensors(); + if (changed) { + send(); + } + } + + virtual void setup() {} + + virtual void loopBeforeSensors() {} + + virtual Sensor *getSensor(int index) = 0; + +private: + + bool loopSensors() { + auto changed = false; + auto index = 0; + Sensor *sensor; + while ((sensor = getSensor(index++)) != nullptr) { + if (sensor->loop()) { + changed = true; + } + } + return changed; + } }; diff --git a/src/patrix/http.cpp b/src/patrix/http.cpp index ca4f444..79ab274 100644 --- a/src/patrix/http.cpp +++ b/src/patrix/http.cpp @@ -3,12 +3,46 @@ #include #include "log.h" +#include "main.h" #include "system.h" AsyncWebServer server(80); +void printValues(AsyncResponseStream *const response, Sensor *sensor) { + auto valueIndex = 0; + Value *value; + response->println("
    "); + while ((value = sensor->getValue(valueIndex++)) != nullptr) { + response->println("
  • "); + response->printf("

    %s = %.1f

    \n", value->getName(), value->getCurrentValue()); + response->println("
  • "); + } + response->println("
"); +} + +void printSensors(AsyncResponseStream *const response) { + auto sensorIndex = 0; + Sensor *sensor; + response->println("
    "); + while ((sensor = node.getSensor(sensorIndex++)) != nullptr) { + response->println("
  • "); + response->printf("

    %s

    \n", sensor->getName()); + printValues(response, sensor); + response->println("
  • "); + } + response->println("
"); +} + void httpIndex(AsyncWebServerRequest *request) { - request->send(200, "text/html", R"(reboot)"); + const auto response = request->beginResponseStream("text/html"); + response->println(""); + response->println("TEST"); + response->println(""); + response->println(""); + response->println("Neustart"); + printSensors(response); + response->println(""); + request->send(response); } void httpReboot(AsyncWebServerRequest *request) { @@ -16,6 +50,7 @@ void httpReboot(AsyncWebServerRequest *request) { request->redirect("/"); request->client()->close(); yield(); + delay(500); restart(); } diff --git a/src/patrix/main.cpp b/src/patrix/main.cpp index 773afdf..cb0729c 100644 --- a/src/patrix/main.cpp +++ b/src/patrix/main.cpp @@ -1,5 +1,6 @@ #include +#include "main.h" #include "boot.h" #include "wifi.h" #include "http.h" @@ -9,15 +10,12 @@ #include "sensor/DHT22.h" #include "sensor/Max6675Sensor.h" -#include "NodeTest.h" -#include "NodeHeizung.h" - #ifdef NODE_TEST -auto node = NodeTest(); +NodeTest node = NodeTest(); #endif #ifdef NODE_HEIZUNG -auto node = NodeHeizung(); +NodeHeizung node = NodeHeizung(); #endif void setup() { diff --git a/src/patrix/main.h b/src/patrix/main.h new file mode 100644 index 0000000..21fb2c8 --- /dev/null +++ b/src/patrix/main.h @@ -0,0 +1,15 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "NodeTest.h" +#include "NodeHeizung.h" + +#ifdef NODE_TEST +extern NodeTest node; +#endif + +#ifdef NODE_HEIZUNG +extern NodeHeizung node; +#endif + +#endif diff --git a/src/patrix/mqtt.cpp b/src/patrix/mqtt.cpp index 10ebe79..ed11b3f 100644 --- a/src/patrix/mqtt.cpp +++ b/src/patrix/mqtt.cpp @@ -46,11 +46,13 @@ void mqttLoop() { } } -bool mqttPublish(const char *topic0, const char *topic1, const JsonDocument& json) { - char topic[256]; - snprintf(topic, sizeof(topic), "%s/%s", topic0, topic1); +bool mqttPublish(const String& topic, const double value, const bool retained) { + return mqtt.publish(topic.c_str(), String(value).c_str(), retained); +} + +bool mqttPublish(const String& topic, const JsonDocument& json) { char buffer[512]; serializeJson(json, buffer); - return mqtt.publish(topic, buffer); + return mqtt.publish(topic.c_str(), buffer); } diff --git a/src/patrix/mqtt.h b/src/patrix/mqtt.h index 6c259fa..5335539 100644 --- a/src/patrix/mqtt.h +++ b/src/patrix/mqtt.h @@ -5,6 +5,8 @@ void mqttLoop(); -bool mqttPublish(const char *topic0, const char *topic1, const JsonDocument& json); +bool mqttPublish(const String& topic, const double value, bool retained); + +bool mqttPublish(const String& topic, const JsonDocument& json); #endif diff --git a/src/patrix/sensor/DHT22.h b/src/patrix/sensor/DHT22.h index d070517..b77eb99 100644 --- a/src/patrix/sensor/DHT22.h +++ b/src/patrix/sensor/DHT22.h @@ -1,10 +1,11 @@ #ifndef DHT22_H #define DHT22_H -#include "dht_nonblocking.h" - #include "Sensor.h" +#include "dht_nonblocking.h" +#include "Value.h" + class DHT22 final : public Sensor { DHT_nonblocking sensor; @@ -32,7 +33,7 @@ public: // } - bool loop() override { + void loopBeforeValues() override { float t, hr; if (sensor.measure(&t, &hr)) { temperature.update(t); @@ -40,26 +41,15 @@ public: humidityAbsolute.update(6.112 * exp(17.67 * t / (243.5 + t)) * hr * 2.1674 / (t + 273.15)); info("%s: temperature=%.1f^C, humidityRelative=%.0f%%, humidityAbsolute=%.0fmg/L", name, temperature.getCurrentValue(), humidityRelative.getCurrentValue(), humidityAbsolute.getCurrentValue()); } - return false; // TODO } - void toJson(JsonDocument& json) override { - json[String(name) + "/" + temperature.getName()] = temperature.getCurrentValue(); - json[String(name) + "/" + humidityRelative.getName()] = humidityRelative.getCurrentValue(); - json[String(name) + "/" + humidityAbsolute.getName()] = humidityAbsolute.getCurrentValue(); - } - - bool isDue() override { - const auto tp = temperature.isDue(); - const auto hr = humidityRelative.isDue(); - const auto ha = humidityAbsolute.isDue(); - return tp || hr || ha; - } - - void markSent() override { - temperature.markSent(); - humidityRelative.markSent(); - humidityAbsolute.markSent(); + Value *getValue(const int index) override { + switch (index) { + case 0: return &temperature; + case 1: return &humidityRelative; + case 2: return &humidityAbsolute; + default: return nullptr; + } } }; diff --git a/src/patrix/sensor/DallasSensor.h b/src/patrix/sensor/DallasSensor.h index 48e1d56..191fde6 100644 --- a/src/patrix/sensor/DallasSensor.h +++ b/src/patrix/sensor/DallasSensor.h @@ -30,23 +30,17 @@ public: // } - bool loop() override { + void loopBeforeValues() override { if (sensors.isConverted()) { temperature.update(sensors.read(address)); } - return false; // TODO } - void toJson(JsonDocument& json) override { - json[String(name) + "/" + temperature.getName()] = temperature.getCurrentValue(); - } - - bool isDue() override { - return temperature.isDue(); - } - - void markSent() override { - temperature.markSent(); + Value *getValue(const int index) override { + switch (index) { + case 0: return &temperature; + default: return nullptr; + } } }; diff --git a/src/patrix/sensor/Max6675Sensor.h b/src/patrix/sensor/Max6675Sensor.h index e52db41..bd9cb55 100644 --- a/src/patrix/sensor/Max6675Sensor.h +++ b/src/patrix/sensor/Max6675Sensor.h @@ -30,22 +30,15 @@ public: // } - bool loop() override { - float current = sensor.readCelsius(); - temperature.update(current); - return false; // TODO + void loopBeforeValues() override { + temperature.update(sensor.readCelsius()); } - void toJson(JsonDocument& json) override { - json[String(name) + "/" + temperature.getName()] = temperature.getCurrentValue(); - } - - bool isDue() override { - return temperature.isDue(); - } - - void markSent() override { - temperature.markSent(); + Value *getValue(const int index) override { + switch (index) { + case 0: return &temperature; + default: return nullptr; + } } }; diff --git a/src/patrix/sensor/Sensor.h b/src/patrix/sensor/Sensor.h index b04e20b..53b16ad 100644 --- a/src/patrix/sensor/Sensor.h +++ b/src/patrix/sensor/Sensor.h @@ -1,7 +1,8 @@ #ifndef SENSOR_H #define SENSOR_H -#include +#include "Value.h" +#include "../mqtt.h" class Sensor { @@ -17,13 +18,50 @@ public: virtual ~Sensor() = default; - virtual bool loop() = 0; + const char *getName() const { + return name; + } - virtual void toJson(JsonDocument& json) = 0; + bool loop() { + loopBeforeValues(); + const auto changed = loopValues(); + if (changed) { + send(); + } + return changed; + } - virtual bool isDue() = 0; + void send() { + JsonDocument json; + toJson(json.to()); + mqttPublish(String(name) + "/json", json); + } - virtual void markSent() = 0; + void toJson(const JsonObject& json) { + auto index = 0; + Value *value; + while ((value = getValue(index++)) != nullptr) { + value->toJson(json[value->getName()].to()); + } + } + + virtual void loopBeforeValues() {} + + virtual Value *getValue(int index) = 0; + +private: + + bool loopValues() { + auto changed = false; + auto index = 0; + Value *value; + while ((value = getValue(index++)) != nullptr) { + if (value->loop()) { + changed = true; + } + } + return changed; + } }; diff --git a/src/patrix/sensor/Value.h b/src/patrix/sensor/Value.h index b13a5fb..d31cd46 100644 --- a/src/patrix/sensor/Value.h +++ b/src/patrix/sensor/Value.h @@ -1,6 +1,9 @@ #ifndef VALUE_H #define VALUE_H + +#include #include +#include class Value { @@ -18,6 +21,8 @@ class Value { unsigned long currentMillis = 0; + time_t currentEpoch = 0; + double sentValue = NAN; time_t sentInterval = 0; @@ -41,22 +46,38 @@ public: void update(const double value) { currentValue = value; currentMillis = millis(); + currentEpoch = time(nullptr); } - bool isDue() { + void toJson(const JsonObject& json) const { + json["value"] = currentValue; + json["time"] = currentEpoch; + } + + void send() { + mqttPublish(String(parent) + "/" + name + "/plain", currentValue, true); + + JsonDocument json; + toJson(json.to()); + mqttPublish(String(parent) + "/" + name + "/json", json); + + markSent(); + } + + bool loop() { if (!isnan(currentValue) && millis() - currentMillis > maxAgeMillis) { warn("Value too old: %s/%s", parent, name); update(NAN); } + const auto dueToNAN = isnan(currentValue) != isnan(sentValue); const auto dueToThreshold = abs(sentValue - currentValue) >= threshold; const auto dueToTime = sentInterval != 0 && sentInterval != time(nullptr) / overdueSeconds; - return dueToNAN || dueToThreshold || dueToTime; - } - - void markSent() { - sentValue = currentValue; - sentInterval = time(nullptr) / overdueSeconds; + const auto changed = dueToNAN || dueToThreshold || dueToTime; + if (changed) { + send(); + } + return changed; } const char *getName() const { @@ -67,6 +88,17 @@ public: return currentValue; } + time_t getCurrentEpoch() const { + return currentEpoch; + } + +private: + + void markSent() { + sentValue = currentValue; + sentInterval = time(nullptr) / overdueSeconds; + } + }; #endif