Compare commits
7 Commits
5c5fba217c
...
5900912184
| Author | SHA1 | Date | |
|---|---|---|---|
| 5900912184 | |||
| 3af5f4fb90 | |||
| 60c9adf677 | |||
| 8418821e77 | |||
| 32401a6560 | |||
| d9be78d738 | |||
| 2d22c2f136 |
@ -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}
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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
29
src/patrix/boot.cpp
Normal 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
6
src/patrix/boot.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef BOOT_H
|
||||||
|
#define BOOT_H
|
||||||
|
|
||||||
|
void bootDelay();
|
||||||
|
|
||||||
|
#endif //BOOT_H
|
||||||
62
src/patrix/http.cpp
Normal file
62
src/patrix/http.cpp
Normal 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
6
src/patrix/http.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef HTTP_H
|
||||||
|
#define HTTP_H
|
||||||
|
|
||||||
|
void httpSetup();
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -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
15
src/patrix/main.h
Normal 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
|
||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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!");
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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
10
src/patrix/system.cpp
Normal 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
6
src/patrix/system.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef SYSTEM_H
|
||||||
|
#define SYSTEM_H
|
||||||
|
|
||||||
|
void restart();
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user