Greenhouse first throw

This commit is contained in:
Patrick Haßel 2024-04-22 12:33:31 +02:00
parent 77890778d4
commit 0cc485577c
10 changed files with 468 additions and 0 deletions

View File

@ -65,6 +65,20 @@ void configPutDouble(const char *name, double value) {
}
}
uint64_t configGetUint64_t(const char *name, uint64_t fallback) {
if (!config.containsKey(name)) {
return fallback;
}
return config[name];
}
void configPutUint64_t(const char *name, uint64_t value) {
if (config[name] != value) {
config[name] = value;
lastChangeMillis = millis();
}
}
void configPrint() {
info("Config (%s):", lastChangeMillis == 0 ? "PERSISTED" : "TRANSIENT");
for (JsonPair pair: config.as<JsonObject>()) {

View File

@ -19,6 +19,10 @@ double configGetDouble(const char *name, double fallback);
void configPutDouble(const char *name, double value);
uint64_t configGetUint64_t(const char *name, uint64_t fallback);
void configPutUint64_t(const char *name, uint64_t value);
void configPrint();
#endif

View File

@ -273,3 +273,24 @@ bool setDouble(const char *name, double *destinationPtr, double min, double max)
}
return true;
}
bool setUint64_T(const char *name, uint64_t *destinationPtr, uint64_t min, uint64_t max) {
const char *valueStr = strtok(nullptr, "");
if (valueStr == nullptr) {
consolePrintUsage();
return false;
}
uint64_t value = strtoll(valueStr, nullptr, 10);
if ((min > 0 && value < min) || (max > 0 && value > max)) {
error("Value out of range for \"%s\" [%f..%f]: %f", name, min, max, value);
return false;
}
if (*destinationPtr != value) {
*destinationPtr = value;
info("Value for \"%s\" set to: %f", name, value);
configPutUint64_t(name, value);
} else {
info("Value for \"%s\" unchanged: %f", name, value);
}
return true;
}

View File

@ -1,6 +1,8 @@
#ifndef SENSOR3_CONSOLE_H
#define SENSOR3_CONSOLE_H
#include <cstdint>
void consoleSetup();
void consoleLoop();
@ -13,4 +15,6 @@ void consolePrintUsage();
bool setDouble(const char *name, double *destinationPtr, double min, double max);
bool setUint64_T(const char *name, uint64_t *destinationPtr, uint64_t min, uint64_t max);
#endif

28
lib/patrix/sensors/DHT.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef SENSOR3_DHT_H
#define SENSOR3_DHT_H
#include "base.h"
class DHT {
private:
const int pin;
public:
explicit DHT(const int pin) : pin(pin) {
// nothing
}
void begin() {
// TODO
}
void loop(float *temperatureCelsius, uint8_t *humidityRelativePercent, float *humidityAbsoluteMgL) {
// TODO
}
};
#endif

View File

@ -0,0 +1,22 @@
#ifndef SENSOR3_MOISTURE_H
#define SENSOR3_MOISTURE_H
class Moisture {
private:
const int pin;
public:
Moisture(const int pin) : pin(pin) {
// TODO
}
void loop(float *value) {
// TODO
}
};
#endif

View File

@ -0,0 +1,78 @@
#ifndef SENSOR3_OUTPUT_H
#define SENSOR3_OUTPUT_H
#include "base.h"
class Output {
private:
const int pin;
const bool invert;
uint64_t onSince = 0;
uint64_t offSince = 0;
uint64_t totalOnMillis = 0;
public:
Output(const int pin, const bool invert, const bool initial) : pin(pin), invert(invert) {
pinMode(pin, OUTPUT);
set(initial);
}
void loop() {
uint64_t now = getUptimeMillis();
static uint64_t last = now;
if (isOn()) {
totalOnMillis += now - last;
}
}
[[nodiscard]] bool isOn() const {
return (digitalRead(pin) == HIGH) ^ invert;
}
void on() {
set(true);
}
void off() {
set(false);
}
void set(const bool newState) {
if (isOn() != newState) {
digitalWrite(pin, newState ^ invert ? HIGH : LOW);
if (newState) {
onSince = getUptimeMillis();
} else {
offSince = getUptimeMillis();
}
}
}
[[nodiscard]] uint64_t getOnSince() const {
if (!isOn()) {
return 0;
}
return getUptimeMillis() - onSince;
}
[[nodiscard]] uint64_t getOffSince() const {
if (isOn()) {
return 0;
}
return getUptimeMillis() - offSince;
}
[[nodiscard]] uint64_t getTotalOnMillis() const {
return totalOnMillis;
}
};
#endif

View File

@ -61,3 +61,18 @@ monitor_speed = ${COMMON.monitor_speed}
monitor_filters = esp8266_exception_decoder
lib_deps = ${COMMON.lib_deps}
build_flags = -D FERMENTER -D HOSTNAME=\"Fermenter\" -D WIFI_SSID=\"${COMMON.WIFI_SSID}\" -D WIFI_PKEY=\"${COMMON.WIFI_PKEY}\" -D OTA_PASSWORD=\"OtaAuthPatrixFermenter\" -D BOOT_DELAY=true -D DEBUG_LOG=false
[env:Greenhouse]
;upload_port = 10.0.0.
;upload_flags = --auth=OtaAuthPatrixGreenhouse
;upload_protocol = ${COMMON.ota_protocol}
upload_port = ${COMMON.usb_port}
upload_speed = ${COMMON.usb_speed}
platform = espressif8266
board = esp12e
framework = ${COMMON.framework}
monitor_port = ${COMMON.monitor_port}
monitor_speed = ${COMMON.monitor_speed}
monitor_filters = esp8266_exception_decoder
lib_deps = ${COMMON.lib_deps}
build_flags = -D GREENHOUSE -D HOSTNAME=\"Greenhouse\" -D WIFI_SSID=\"${COMMON.WIFI_SSID}\" -D WIFI_PKEY=\"${COMMON.WIFI_PKEY}\" -D OTA_PASSWORD=\"OtaAuthPatrixGreenhouse\" -D BOOT_DELAY=true -D DEBUG_LOG=false

View File

@ -0,0 +1,187 @@
#if defined(GREENHOUSE)
#include <Patrix.h>
#include "GreenhouseData.h"
#include "sensors/DHT.h"
#include "sensors/Output.h"
#include "sensors/Moisture.h"
#define INSIDE_DHT_PIN 4
#define MOISTURE_PIN 5
#define OUTSIDE_DHT_PIN 6
#define VENTILATOR_PIN 7
#define SPRINKLER_PIN 8
#define TEMPERATURE_HIGH 35
#define TEMPERATURE_LOW 32
#define TEMPERATURE_MILLIS (5 * 60 * 1000)
#define MOISTURE_HIGH 70
#define MOISTURE_LOW 20
#define MOISTURE_MILLIS (5 * 60 * 1000)
GreenhouseData data{};
Cache<GreenhouseData, 800> cache;
DHT insideDHT(INSIDE_DHT_PIN);
Moisture insideMoisture(MOISTURE_PIN);
DHT outsideDHT(OUTSIDE_DHT_PIN);
Output ventilator(VENTILATOR_PIN, true, false);
Output sprinkler(SPRINKLER_PIN, true, false);
double tempHigh = 35;
double tempLow = 32;
uint64_t tempMillis = 5 * 60 * 1000;
uint64_t tempHighSince = 0;
uint64_t tempLowSince = 0;
double moistHigh = 35;
double moistLow = 32;
uint64_t moistMillis = 5 * 60 * 1000;
uint64_t moistHighSince = 0;
uint64_t moistLowSince = 0;
void insideLoop();
void outsideLoop();
void moistureLoop();
void patrixSetup() {
insideDHT.begin();
outsideDHT.begin();
}
void patrixLoop() {
outsideLoop();
insideLoop();
moistureLoop();
ventilator.loop();
data.ventilatorSeconds = ventilator.getTotalOnMillis() / 1000;
sprinkler.loop();
data.sprinklerSeconds = sprinkler.getTotalOnMillis() / 1000;
static unsigned long now = millis();
static unsigned long last = now;
if (now - last >= 60 * 1000) {
last = now;
cache.add(getTime(), data);
cache.loop();
}
}
void outsideLoop() {
float temperature = NAN;
float absolute = NAN;
outsideDHT.loop(&temperature, &data.outsideRelativePercent, &absolute);
data.outsideTemperatureCenti = (int16_t) round(temperature * 100);
data.outsideAbsoluteCenti = (uint16_t) round(absolute * 100);
}
void insideLoop() {
float temperature = NAN;
float absolute = NAN;
insideDHT.loop(&temperature, &data.insideRelativePercent, &absolute);
data.insideTemperatureCenti = (int16_t) round(temperature * 100);
data.insideAbsoluteCenti = (uint16_t) round(absolute * 100);
if (temperature > tempHigh) {
tempLowSince = 0;
if (tempHighSince == 0) {
tempHighSince = getUptimeMillis();
}
if (tempHighSince != 0 && getUptimeMillis() - tempHighSince > tempMillis) {
ventilator.on();
}
}
if (temperature < tempLow) {
tempHighSince = 0;
if (tempLowSince == 0) {
tempLowSince = getUptimeMillis();
}
if (tempLowSince != 0 && getUptimeMillis() - tempLowSince > tempMillis) {
ventilator.off();
}
}
}
void moistureLoop() {
float moisture = NAN;
insideMoisture.loop(&moisture);
data.insideMoisturePercent = (uint16_t) round(moisture * 100);
if (moisture > moistHigh) {
moistLowSince = 0;
if (moistHighSince == 0) {
moistHighSince = getUptimeMillis();
}
if (moistHighSince != 0 && getUptimeMillis() - moistHighSince > moistMillis) {
sprinkler.off();
}
}
if (moisture < moistLow) {
moistHighSince = 0;
if (moistLowSince == 0) {
moistLowSince = getUptimeMillis();
}
if (moistLowSince != 0 && getUptimeMillis() - moistLowSince > moistMillis) {
sprinkler.on();
}
}
}
bool patrix_command(char *first) {
bool result = false;
if (strcmp(first, "tempHigh") == 0 || strcmp(first, "th") == 0) {
result = setDouble("tempHigh", &tempHigh, NAN, NAN);
} else if (strcmp(first, "tempLow") == 0 || strcmp(first, "tl") == 0) {
result = setDouble("tempLow", &tempLow, NAN, NAN);
} else if (strcmp(first, "tempMillis") == 0 || strcmp(first, "tm") == 0) {
result = setUint64_T("tempMillis", &tempMillis, NAN, NAN);
} else if (strcmp(first, "moistHigh") == 0 || strcmp(first, "mh") == 0) {
result = setDouble("moistHigh", &moistHigh, NAN, NAN);
} else if (strcmp(first, "moistLow") == 0 || strcmp(first, "ml") == 0) {
result = setDouble("moistLow", &moistLow, NAN, NAN);
} else if (strcmp(first, "moistMillis") == 0 || strcmp(first, "mm") == 0) {
result = setUint64_T("moistMillis", &moistMillis, NAN, NAN);
}
return result;
}
void configLoaded() {
tempHigh = configGetDouble("tempHigh", TEMPERATURE_HIGH);
tempLow = configGetDouble("tempLow", TEMPERATURE_LOW);
tempMillis = configGetUint64_t("tempMillis", TEMPERATURE_MILLIS);
moistHigh = configGetDouble("moistHigh", MOISTURE_HIGH);
moistLow = configGetDouble("moistLow", MOISTURE_LOW);
moistMillis = configGetUint64_t("moistMillis", MOISTURE_MILLIS);
}
#endif

View File

@ -0,0 +1,95 @@
#ifndef SENSOR3_GREENHOUSE_DATA_H
#define SENSOR3_GREENHOUSE_DATA_H
#include "data.h"
struct GreenhouseData : IData {
int16_t insideTemperatureCenti;
uint8_t insideRelativePercent;
uint16_t insideAbsoluteCenti;
uint8_t insideMoisturePercent;
int16_t outsideTemperatureCenti;
uint8_t outsideRelativePercent;
uint16_t outsideAbsoluteCenti;
bool ventilatorState;
uint32_t ventilatorSeconds;
bool sprinklerState;
uint32_t sprinklerSeconds;
GreenhouseData() {
insideTemperatureCenti = 0;
insideRelativePercent = 0;
insideAbsoluteCenti = 0;
insideMoisturePercent = 0;
outsideTemperatureCenti = 0;
outsideRelativePercent = 0;
outsideAbsoluteCenti = 0;
ventilatorState = false;
ventilatorSeconds = 0;
sprinklerState = false;
sprinklerSeconds = 0;
}
GreenhouseData(
int16_t insideTemperatureCenti,
uint8_t insideRelativePercent,
uint8_t insideAbsoluteCenti,
uint8_t insideMoisturePercent,
int16_t outsideTemperatureCenti,
uint8_t outsideRelativePercent,
uint8_t outsideAbsoluteCenti,
bool ventilatorState,
uint32_t ventilatorSeconds,
bool sprinklerState,
uint32_t sprinklerSeconds
) :
insideTemperatureCenti(insideTemperatureCenti),
insideRelativePercent(insideRelativePercent),
insideAbsoluteCenti(insideAbsoluteCenti),
insideMoisturePercent(insideMoisturePercent),
outsideTemperatureCenti(outsideTemperatureCenti),
outsideRelativePercent(outsideRelativePercent),
outsideAbsoluteCenti(outsideAbsoluteCenti),
ventilatorState(ventilatorState),
ventilatorSeconds(ventilatorSeconds),
sprinklerState(sprinklerState),
sprinklerSeconds(sprinklerSeconds) {
// -
}
void toJson(JsonObject &json) const override {
JsonObject inside = json["inside"];
inside["temperature"] = insideTemperatureCenti / 100;
inside["relative"] = insideRelativePercent;
inside["absolute"] = insideAbsoluteCenti / 100;
inside["moisture"] = insideMoisturePercent;
JsonObject outside = json["outside"];
outside["temperature"] = outsideTemperatureCenti / 100;
outside["relative"] = outsideRelativePercent;
outside["absolute"] = outsideAbsoluteCenti / 100;
JsonObject ventilator = json["ventilator"];
ventilator["state"] = ventilatorState;
ventilator["totalSeconds"] = ventilatorSeconds;
JsonObject sprinkler = json["sprinkler"];
sprinkler["state"] = sprinklerState;
sprinkler["totalSeconds"] = sprinklerSeconds;
}
};
#endif