Compare commits

...

7 Commits

22 changed files with 477 additions and 145 deletions

View File

@ -10,6 +10,7 @@ lib_deps = milesburton/DallasTemperature
https://github.com/adafruit/MAX6675-library https://github.com/adafruit/MAX6675-library
paulstoffregen/OneWire paulstoffregen/OneWire
bblanchon/ArduinoJson bblanchon/ArduinoJson
https://github.com/me-no-dev/ESPAsyncWebServer.git
upload_port = /dev/ttyUSB0 upload_port = /dev/ttyUSB0
upload_speed = 921600 upload_speed = 921600
monitor_port = /dev/ttyUSB0 monitor_port = /dev/ttyUSB0
@ -23,8 +24,10 @@ platform = ${common.platform}
board = ${common.board} board = ${common.board}
framework = ${common.framework} framework = ${common.framework}
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
upload_port = ${common.upload_port} upload_port = 10.0.0.119
upload_speed = ${common.upload_speed} upload_protocol = espota
;upload_port = ${common.upload_port}
;upload_speed = ${common.upload_speed}
monitor_port = ${common.monitor_port} monitor_port = ${common.monitor_port}
monitor_speed = ${common.monitor_speed} monitor_speed = ${common.monitor_speed}
monitor_filters = ${common.monitor_filters} monitor_filters = ${common.monitor_filters}

View File

@ -4,11 +4,15 @@
#include "patrix/Node.h" #include "patrix/Node.h"
#include "patrix/sensor/Dallas.h" #include "patrix/sensor/Dallas.h"
#include "patrix/sensor/DallasSensor.h" #include "patrix/sensor/DallasSensor.h"
#include "patrix/sensor/Max6675Sensor.h"
#include "patrix/sensor/DHT22.h" #include "patrix/sensor/DHT22.h"
#include "patrix/sensor/Max6675Sensor.h"
class NodeHeizung final : public Node { class NodeHeizung final : public Node {
const unsigned long MAX_AGE_SECONDS = 10;
const unsigned long OVERDUE_SECONDS = 60;
DHT22 keller; DHT22 keller;
Max6675Sensor abgas; Max6675Sensor abgas;
@ -37,56 +41,44 @@ class NodeHeizung final : public Node {
public: public:
NodeHeizung() : NodeHeizung() : keller(13, "keller", 0.5, 2, 0.5, MAX_AGE_SECONDS, OVERDUE_SECONDS),
keller(13, "keller", 0.5, 2, 0.5), abgas(27, 26, 25, "heizung/abgas", 2, MAX_AGE_SECONDS, OVERDUE_SECONDS),
abgas(27, 26, 25, "heizung/abgas", 2),
dallas(14), dallas(14),
pufferVorlauf(dallas, 0xAA0121125E4A7528, "heizung/puffer/vorlauf", 1.0), pufferVorlauf(dallas, 0xAA0121125E4A7528, "heizung/puffer/vorlauf", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
pufferRuecklauf(dallas, 0x3E0121126A163B28, "heizung/puffer/ruecklauf", 1.0), pufferRuecklauf(dallas, 0x3E0121126A163B28, "heizung/puffer/ruecklauf", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
pufferEingang(dallas, 0x0201211240EE3128, "heizung/puffer/eingang", 1.0), pufferEingang(dallas, 0x0201211240EE3128, "heizung/puffer/eingang", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
pufferSpeicher(dallas, 0x1001211233D3A428, "heizung/puffer/speicher", 1.0), pufferSpeicher(dallas, 0x1001211233D3A428, "heizung/puffer/speicher", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
pufferAusgang(dallas, 0xE80121126FFD9328, "heizung/puffer/ausgang", 1.0), pufferAusgang(dallas, 0xE80121126FFD9328, "heizung/puffer/ausgang", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
pufferZirkulation(dallas, 0x540121124A48FD28, "heizung/puffer/zirkulation", 1.0), pufferZirkulation(dallas, 0x540121124A48FD28, "heizung/puffer/zirkulation", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
heizkreisVorlauf(dallas, 0x330121126F984728, "heizung/heizkreis/vorlauf", 1.0), heizkreisVorlauf(dallas, 0x330121126F984728, "heizung/heizkreis/vorlauf", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
heizkreisRuecklauf(dallas, 0x4201211270337B28, "heizung/heizkreis/ruecklauf", 1.0), heizkreisRuecklauf(dallas, 0x4201211270337B28, "heizung/heizkreis/ruecklauf", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
abwasser(dallas, 0x810121126EE53828, "abwasser", 1.0), abwasser(dallas, 0x810121126EE53828, "abwasser", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
reserve(dallas, 0x0D0121126716CF28, "__RESERVE__", 1.0) { reserve(dallas, 0x0D0121126716CF28, "__RESERVE__", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS) {
// //
}; };
void setup() override { Sensor *getSensor(const int index) override {
// TODO 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 { protected:
keller.loop();
abgas.loop(); void loopBeforeSensors() override {
dallas.loop(); 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);
} }
}; };

View File

@ -1,16 +1,17 @@
#ifndef NODE_TEST_H #ifndef NODE_TEST_H
#define NODE_TEST_H #define NODE_TEST_H
#include <WiFi.h>
#include "patrix/Node.h" #include "patrix/Node.h"
#include "patrix/sensor/Dallas.h" #include "patrix/sensor/Dallas.h"
#include "patrix/sensor/DallasSensor.h" #include "patrix/sensor/DallasSensor.h"
#include "patrix/sensor/Max6675Sensor.h"
#include "patrix/sensor/DHT22.h" #include "patrix/sensor/DHT22.h"
class NodeTest final : public Node { class NodeTest final : public Node {
const unsigned long MAX_AGE_SECONDS = 10;
const unsigned long OVERDUE_SECONDS = 60;
Dallas dallas; Dallas dallas;
DallasSensor test; DallasSensor test;
@ -20,36 +21,23 @@ class NodeTest final : public Node {
public: public:
NodeTest() : dallas(27), NodeTest() : dallas(27),
test(dallas, 0xC90417C1C5C0FF28, "test/ds18b20", 1.0), test(dallas, 0xC90417C1C5C0FF28, "test/ds18b20", 1.0, MAX_AGE_SECONDS, OVERDUE_SECONDS),
testraum(26, "test/dht22", 0.5, 2, 0.5) { testraum(26, "test/dht22", 0.5, 2, 1, MAX_AGE_SECONDS, OVERDUE_SECONDS) {
// //
}; };
void setup() override { Sensor *getSensor(const int index) override {
// TODO switch (index) {
case 0: return &test;
case 1: return &testraum;
default: return nullptr;
}
} }
void send() { protected:
JsonDocument json;
toJson(json);
mqttPublish(WiFiClass::getHostname(), json);
}
void loop() override { void loopBeforeSensors() override {
dallas.loop(); dallas.loop();
test.loop();
testraum.loop();
static auto last = 0UL;
const auto now = millis();
if (last == 0 || now - last >= 3000) { // TODO
last = now;
send();
}
}
void toJson(JsonDocument& json) override {
test.toJson(json);
testraum.toJson(json);
} }
}; };

View File

@ -1,7 +1,7 @@
#ifndef NODE_H #ifndef NODE_H
#define NODE_H #define NODE_H
#include <ArduinoJson.h> #include "sensor/Sensor.h"
class Node { class Node {
@ -9,11 +9,47 @@ public:
virtual ~Node() = default; 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<JsonObject>());
}
}
virtual void loop() = 0; void send() {
JsonDocument json;
toJson(json.to<JsonObject>());
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;
}
}; };

29
src/patrix/boot.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "boot.h"
#include "wifi.h"
#include "clock.h"
#include "log.h"
void bootDelay() {
info("Waiting for WiFi...");
while (!isWiFiConnected()) {
wifiLoop();
yield();
}
info("Waiting for clock to be set...");
while (!isClockSet()) {
wifiLoop();
clockLoop();
yield();
}
info("Waiting 5 seconds for OTA update...");
const auto start = millis();
while (millis() - start < 5000) {
wifiLoop();
yield();
}
info("Boot delay complete.");
}

6
src/patrix/boot.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef BOOT_H
#define BOOT_H
void bootDelay();
#endif //BOOT_H

62
src/patrix/http.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "http.h"
#include <ESPAsyncWebServer.h>
#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("<ul>");
while ((value = sensor->getValue(valueIndex++)) != nullptr) {
response->println("<li>");
response->printf("<h2>%s = %.1f</h2>\n", value->getName(), value->getCurrentValue());
response->println("</li>");
}
response->println("</ul>");
}
void printSensors(AsyncResponseStream *const response) {
auto sensorIndex = 0;
Sensor *sensor;
response->println("<ul>");
while ((sensor = node.getSensor(sensorIndex++)) != nullptr) {
response->println("<li>");
response->printf("<h3>%s</h3>\n", sensor->getName());
printValues(response, sensor);
response->println("</li>");
}
response->println("</ul>");
}
void httpIndex(AsyncWebServerRequest *request) {
const auto response = request->beginResponseStream("text/html");
response->println("<!DOCTYPE html><html><head>");
response->println("<title>TEST</title>");
response->println("</head>");
response->println("<body>");
response->println("<a href='reboot'>Neustart</a>");
printSensors(response);
response->println("</body></html>");
request->send(response);
}
void httpReboot(AsyncWebServerRequest *request) {
info("Rebooting...");
request->redirect("/");
request->client()->close();
yield();
delay(500);
restart();
}
void httpSetup() {
server.on("/", HTTP_GET, httpIndex);
server.on("/reboot", HTTP_GET, httpReboot);
server.begin();
}

6
src/patrix/http.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef HTTP_H
#define HTTP_H
void httpSetup();
#endif

View File

@ -1,29 +1,34 @@
#include <Arduino.h> #include <Arduino.h>
#include "mqtt.h" #include "main.h"
#include "boot.h"
#include "wifi.h" #include "wifi.h"
#include "clock.h" #include "http.h"
#include "mqtt.h"
#include "sensor/DallasSensor.h"
#include "sensor/DHT22.h"
#include "sensor/Max6675Sensor.h"
#ifdef NODE_TEST #ifdef NODE_TEST
#include "NodeTest.h" NodeTest node = NodeTest();
NodeTest node = NodeTest();
#endif #endif
#ifdef NODE_HEIZUNG #ifdef NODE_HEIZUNG
#include "NodeHeizung.h" NodeHeizung node = NodeHeizung();
NodeHeizung node = NodeHeizung();
#endif #endif
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
delay(500); delay(500);
Serial.print("Startup\n"); Serial.print("Startup\n");
bootDelay();
httpSetup();
node.setup(); node.setup();
} }
void loop() { void loop() {
wifiLoop(); wifiLoop();
clockLoop();
mqttLoop(); mqttLoop();
node.loop(); node.loop();
} }

15
src/patrix/main.h Normal file
View File

@ -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

View File

@ -46,9 +46,13 @@ void mqttLoop() {
} }
} }
void mqttPublish(const char *topic, const JsonDocument& json) { 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]; char buffer[512];
serializeJson(json, buffer); serializeJson(json, buffer);
info(buffer); return mqtt.publish(topic.c_str(), buffer);
mqtt.publish(topic, buffer);
} }

View File

@ -5,6 +5,8 @@
void mqttLoop(); void mqttLoop();
void mqttPublish(const char *topic, const JsonDocument& json); bool mqttPublish(const String& topic, const double value, bool retained);
bool mqttPublish(const String& topic, const JsonDocument& json);
#endif #endif

View File

@ -1,44 +1,55 @@
#ifndef DHT22_H #ifndef DHT22_H
#define DHT22_H #define DHT22_H
#include "dht_nonblocking.h"
#include "Sensor.h" #include "Sensor.h"
#include "dht_nonblocking.h"
#include "Value.h"
class DHT22 final : public Sensor { class DHT22 final : public Sensor {
DHT_nonblocking sensor; DHT_nonblocking sensor;
double temperatureThreshold; Value temperature;
double humidityRelativeThreshold; Value humidityRelative;
double humidityAbsoluteThreshold; Value humidityAbsolute;
float temperature = NAN;
float humidityRelative = NAN;
double humidityAbsolute = NAN;
public: public:
DHT22(const int pin, const char *name, const double temperatureThreshold, const double humidityRelativeThreshold, const double humidityAbsoluteThreshold) : Sensor(name), sensor(pin, DHT_TYPE_22), temperatureThreshold(temperatureThreshold), humidityRelativeThreshold(humidityRelativeThreshold), humidityAbsoluteThreshold(humidityAbsoluteThreshold) { DHT22(const int pin,
const char *name,
const double temperatureThreshold,
const double humidityRelativeThreshold,
const double humidityAbsoluteThreshold,
const unsigned long maxAgeSeconds,
const unsigned long overdueSeconds
) : Sensor(name),
sensor(pin, DHT_TYPE_22),
temperature(name, "temperature", temperatureThreshold, maxAgeSeconds, overdueSeconds),
humidityRelative(name, "humidity/relative", humidityRelativeThreshold, maxAgeSeconds, overdueSeconds),
humidityAbsolute(name, "humidity/absolute", humidityAbsoluteThreshold, maxAgeSeconds, overdueSeconds) {
// //
} }
bool loop() override { void loopBeforeValues() override {
if (sensor.measure(&temperature, &humidityRelative)) { float t, hr;
humidityAbsolute = 6.112 * exp(17.67 * temperature / (243.5 + temperature)) * humidityRelative * 2.1674 / (temperature + 273.15); if (sensor.measure(&t, &hr)) {
info("%s: temperature=%.1f^C, humidityRelative=%.0f%%, humidityAbsolute=%.0fmg/L", name, temperature, humidityRelative, humidityAbsolute); temperature.update(t);
humidityRelative.update(hr);
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 { Value *getValue(const int index) override {
json[name]["temperature"] = temperature; switch (index) {
json[name]["humidity"]["relative"] = humidityRelative; case 0: return &temperature;
json[name]["humidity"]["absolute"] = humidityAbsolute; case 1: return &humidityRelative;
case 2: return &humidityAbsolute;
default: return nullptr;
}
} }
}; };

View File

@ -5,7 +5,7 @@
#include "OneWire.h" #include "OneWire.h"
#include "DallasTemperature.h" #include "DallasTemperature.h"
#define DALLAS_TIMEOUT_MILLISECONDS 3000 #define DALLAS_INTERVAL_MILLISECONDS 2000
class Dallas { class Dallas {
@ -29,9 +29,9 @@ public:
} }
void loop() { void loop() {
if (lastMillis == 0 || millis() - lastMillis > DALLAS_TIMEOUT_MILLISECONDS) { if (lastMillis == 0 || millis() - lastMillis > DALLAS_INTERVAL_MILLISECONDS) {
if (converting) { if (converting) {
error("Dallas timeout!\n"); error("Dallas timeout!");
} }
sensors.requestTemperatures(); sensors.requestTemperatures();
timestamp = time(nullptr); timestamp = time(nullptr);
@ -44,7 +44,7 @@ public:
uint64_t address; uint64_t address;
for (int index = 0; index < count; ++index) { for (int index = 0; index < count; ++index) {
sensors.getAddress(reinterpret_cast<uint8_t *>(&address), index); sensors.getAddress(reinterpret_cast<uint8_t *>(&address), index);
info("Dallas %d/%d 0x%016llX = %5.1f ^C", index + 1, count, address, sensors.getTempC(reinterpret_cast<uint8_t *>(&address))); info("Dallas %d/%d 0x%016llX = %5.1f^C", index + 1, count, address, sensors.getTempC(reinterpret_cast<uint8_t *>(&address)));
} }
} else { } else {
warn("No Dallas devices found!"); warn("No Dallas devices found!");

View File

@ -2,6 +2,7 @@
#define DALLAS_SENSOR_H #define DALLAS_SENSOR_H
#include "Sensor.h" #include "Sensor.h"
#include "Value.h"
#include "Dallas.h" #include "Dallas.h"
@ -11,25 +12,35 @@ class DallasSensor final : public Sensor {
uint64_t address; uint64_t address;
double threshold; Value temperature;
double temperature = NAN;
public: public:
DallasSensor(Dallas& sensors, const uint64_t address, const char *name, const double threshold) : Sensor(name), sensors(sensors), address(address), threshold(threshold) { DallasSensor(
// - Dallas& sensors,
const uint64_t address,
const char *name,
const double threshold,
const unsigned long maxAgeSeconds,
const unsigned long overdueSeconds
) : Sensor(name),
sensors(sensors),
address(address),
temperature(name, "temperature", threshold, maxAgeSeconds, overdueSeconds) {
//
} }
bool loop() override { void loopBeforeValues() override {
if (sensors.isConverted()) { if (sensors.isConverted()) {
temperature = sensors.read(address); temperature.update(sensors.read(address));
} }
return false; // TODO
} }
void toJson(JsonDocument& json) override { Value *getValue(const int index) override {
json[name]["temperature"] = temperature; switch (index) {
case 0: return &temperature;
default: return nullptr;
}
} }
}; };

View File

@ -1,34 +1,44 @@
#ifndef MAX6675SENSOR_H #ifndef MAX6675SENSOR_H
#define MAX6675SENSOR_H #define MAX6675SENSOR_H
#include "max6675.h"
#include "Sensor.h" #include "Sensor.h"
#include "Value.h"
#include "max6675.h"
class Max6675Sensor final : public Sensor { class Max6675Sensor final : public Sensor {
MAX6675 sensor; MAX6675 sensor;
double threshold; Value temperature;
double temperature = NAN;
unsigned long lastMillis = 0; unsigned long lastMillis = 0;
public: public:
explicit Max6675Sensor(const int8_t pinMISO, const int8_t pinCS, const int8_t pinCLK, const char *name, const double threshold) : Sensor(name), sensor(pinCLK, pinCS, pinMISO), threshold(threshold) { explicit Max6675Sensor(
// - const int8_t pinMISO,
const int8_t pinCS,
const int8_t pinCLK,
const char *name,
const double threshold,
const unsigned long maxAgeSeconds,
const unsigned long overdueSeconds
) : Sensor(name),
sensor(pinCLK, pinCS, pinMISO),
temperature(name, "temperature", threshold, maxAgeSeconds, overdueSeconds) {
//
} }
bool loop() override { void loopBeforeValues() override {
float current = sensor.readCelsius(); temperature.update(sensor.readCelsius());
temperature = current;
return false; // TODO
} }
void toJson(JsonDocument& json) override { Value *getValue(const int index) override {
json[name]["temperature"] = temperature; switch (index) {
case 0: return &temperature;
default: return nullptr;
}
} }
}; };

View File

@ -1,7 +1,8 @@
#ifndef SENSOR_H #ifndef SENSOR_H
#define SENSOR_H #define SENSOR_H
#include <ArduinoJson.h> #include "Value.h"
#include "../mqtt.h"
class Sensor { class Sensor {
@ -17,9 +18,50 @@ public:
virtual ~Sensor() = default; 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;
}
void send() {
JsonDocument json;
toJson(json.to<JsonObject>());
mqttPublish(String(name) + "/json", json);
}
void toJson(const JsonObject& json) {
auto index = 0;
Value *value;
while ((value = getValue(index++)) != nullptr) {
value->toJson(json[value->getName()].to<JsonObject>());
}
}
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;
}
}; };

View File

@ -1,21 +1,103 @@
#ifndef VALUE_H #ifndef VALUE_H
#define VALUE_H #define VALUE_H
#include <WiFi.h>
#include <patrix/log.h>
#include <patrix/mqtt.h>
class Value { class Value {
const char *parent;
const char *name;
double threshold; double threshold;
double lastValue; unsigned long maxAgeMillis;
unsigned long lastMillis; unsigned long overdueSeconds;
double sentValue; double currentValue = NAN;
unsigned long sentMillis; unsigned long currentMillis = 0;
public: time_t currentEpoch = 0;
double sentValue = NAN;
time_t sentInterval = 0;
public:
Value(
const char *parent,
const char *name,
const double threshold,
const unsigned long maxAgeSeconds,
const unsigned long overdueSeconds
) : parent(parent),
name(name),
threshold(threshold),
maxAgeMillis(maxAgeSeconds * 1000),
overdueSeconds(overdueSeconds) {
//
}
void update(const double value) {
currentValue = value;
currentMillis = millis();
currentEpoch = time(nullptr);
}
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<JsonObject>());
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;
const auto changed = dueToNAN || dueToThreshold || dueToTime;
if (changed) {
send();
}
return changed;
}
const char *getName() const {
return name;
}
double getCurrentValue() const {
return currentValue;
}
time_t getCurrentEpoch() const {
return currentEpoch;
}
private:
void markSent() {
sentValue = currentValue;
sentInterval = time(nullptr) / overdueSeconds;
}
}; };

10
src/patrix/system.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "system.h"
#include <Esp.h>
#include "wifi.h"
void restart() {
wifiOff();
ESP.restart();
}

6
src/patrix/system.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SYSTEM_H
#define SYSTEM_H
void restart();
#endif

View File

@ -5,6 +5,8 @@
#define WIFI_TIMEOUT_MILLIS 10000 #define WIFI_TIMEOUT_MILLIS 10000
auto wifiEnabled = true;
auto wifiConnected = false; auto wifiConnected = false;
auto wifiTryMillis = 0UL; auto wifiTryMillis = 0UL;
@ -52,6 +54,11 @@ void wifiSetupOTA() {
ArduinoOTA.begin(); ArduinoOTA.begin();
} }
void wifiOff() {
info("wifi disabled");
wifiEnabled = false;
}
void wifiLoop() { void wifiLoop() {
const auto currentState = WiFi.localIP() != 0; const auto currentState = WiFi.localIP() != 0;
if (wifiConnected != currentState) { if (wifiConnected != currentState) {
@ -65,6 +72,9 @@ void wifiLoop() {
WiFi.disconnect(); WiFi.disconnect();
} }
} else if (!wifiConnected) { } else if (!wifiConnected) {
if (!wifiEnabled) {
return;
}
if (wifiTryMillis == 0 || millis() - wifiTryMillis >= WIFI_TIMEOUT_MILLIS) { if (wifiTryMillis == 0 || millis() - wifiTryMillis >= WIFI_TIMEOUT_MILLIS) {
wifiTryMillis = millis(); wifiTryMillis = millis();
WiFiClass::hostname(wifiHost); WiFiClass::hostname(wifiHost);

View File

@ -1,6 +1,8 @@
#ifndef WIFI_H #ifndef WIFI_H
#define WIFI_H #define WIFI_H
void wifiOff();
void wifiLoop(); void wifiLoop();
bool isWiFiConnected(); bool isWiFiConnected();