Compare commits
No commits in common. "1bde26489be476a9a7884c4dec9230a74c8e0a99" and "c59281a5257afd9c58f5d01661287a5946d251ff" have entirely different histories.
1bde26489b
...
c59281a525
@ -2,9 +2,6 @@
|
|||||||
#define SENSOR3_PATRIX_H
|
#define SENSOR3_PATRIX_H
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "config.h"
|
|
||||||
#include "console.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
void patrixSetup();
|
void patrixSetup();
|
||||||
|
|
||||||
|
|||||||
@ -65,34 +65,6 @@ 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t configGetUint8_t(const char *name, uint8_t fallback) {
|
|
||||||
if (!config.containsKey(name)) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
return config[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
void configPutUint8_t(const char *name, uint8_t value) {
|
|
||||||
if (config[name] != value) {
|
|
||||||
config[name] = value;
|
|
||||||
lastChangeMillis = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void configPrint() {
|
void configPrint() {
|
||||||
info("Config (%s):", lastChangeMillis == 0 ? "PERSISTED" : "TRANSIENT");
|
info("Config (%s):", lastChangeMillis == 0 ? "PERSISTED" : "TRANSIENT");
|
||||||
for (JsonPair pair: config.as<JsonObject>()) {
|
for (JsonPair pair: config.as<JsonObject>()) {
|
||||||
|
|||||||
@ -9,27 +9,16 @@ void configLoop();
|
|||||||
|
|
||||||
void configReset();
|
void configReset();
|
||||||
|
|
||||||
|
void configLoaded();
|
||||||
|
|
||||||
String configGetString(const char *name, const char *fallback, bool allowEmpty);
|
String configGetString(const char *name, const char *fallback, bool allowEmpty);
|
||||||
|
|
||||||
void configPutString(const char *name, const char *value);
|
void configPutString(const char *name, const char *value);
|
||||||
|
|
||||||
|
|
||||||
double configGetDouble(const char *name, double fallback);
|
double configGetDouble(const char *name, double fallback);
|
||||||
|
|
||||||
void configPutDouble(const char *name, double value);
|
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);
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t configGetUint8_t(const char *name, uint8_t fallback);
|
|
||||||
|
|
||||||
void configPutUint8_t(const char *name, uint8_t value);
|
|
||||||
|
|
||||||
|
|
||||||
void configPrint();
|
void configPrint();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -67,11 +67,11 @@ void consoleLoop() {
|
|||||||
void consoleHandle(char *cmd) {
|
void consoleHandle(char *cmd) {
|
||||||
char *first = strtok(cmd, " ");
|
char *first = strtok(cmd, " ");
|
||||||
if (first == nullptr) {
|
if (first == nullptr) {
|
||||||
consolePrintUsage();
|
_usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (strcmp(first, "help") == 0) {
|
if (strcmp(first, "help") == 0) {
|
||||||
consolePrintUsage();
|
_usage();
|
||||||
} else if (strcmp(first, "wifi") == 0) {
|
} else if (strcmp(first, "wifi") == 0) {
|
||||||
_wifi();
|
_wifi();
|
||||||
} else if (strcmp(first, "mqtt") == 0) {
|
} else if (strcmp(first, "mqtt") == 0) {
|
||||||
@ -84,12 +84,12 @@ void consoleHandle(char *cmd) {
|
|||||||
_debug();
|
_debug();
|
||||||
} else if (strcmp(first, "config_reset") == 0) {
|
} else if (strcmp(first, "config_reset") == 0) {
|
||||||
configReset();
|
configReset();
|
||||||
} else if (!patrixCommand((char *) cmd)) {
|
} else if (!patrix_command((char *) cmd)) {
|
||||||
info("Unknown command: %s", cmd);
|
info("Unknown command: %s", cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void consolePrintUsage() {
|
void _usage() {
|
||||||
info(R"(USAGE:
|
info(R"(USAGE:
|
||||||
help
|
help
|
||||||
info
|
info
|
||||||
@ -120,7 +120,7 @@ void _reboot() {
|
|||||||
void _mqtt() {
|
void _mqtt() {
|
||||||
char *sub = strtok(nullptr, " ");
|
char *sub = strtok(nullptr, " ");
|
||||||
if (sub == nullptr) {
|
if (sub == nullptr) {
|
||||||
consolePrintUsage();
|
_usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (strcmp(sub, "reconnect") == 0) {
|
if (strcmp(sub, "reconnect") == 0) {
|
||||||
@ -134,7 +134,7 @@ void _mqtt() {
|
|||||||
void _wifi() {
|
void _wifi() {
|
||||||
char *sub = strtok(nullptr, " ");
|
char *sub = strtok(nullptr, " ");
|
||||||
if (sub == nullptr) {
|
if (sub == nullptr) {
|
||||||
consolePrintUsage();
|
_usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (strcmp(sub, "reconnect") == 0) {
|
if (strcmp(sub, "reconnect") == 0) {
|
||||||
@ -150,7 +150,7 @@ void _wifi() {
|
|||||||
void _setConfigString(const char *name, bool allowEmpty) {
|
void _setConfigString(const char *name, bool allowEmpty) {
|
||||||
char *value = strtok(nullptr, "");
|
char *value = strtok(nullptr, "");
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
consolePrintUsage();
|
_usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!allowEmpty && strcmp(value, "") == 0) {
|
if (!allowEmpty && strcmp(value, "") == 0) {
|
||||||
@ -248,49 +248,3 @@ const char *getFlashChipMode() {
|
|||||||
return "[???]";
|
return "[???]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmdReadDouble(const char *name, double *destinationPtr, double min, double max) {
|
|
||||||
const char *valueStr = strtok(nullptr, "");
|
|
||||||
if (valueStr == nullptr) {
|
|
||||||
consolePrintUsage();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
double value = strtod(valueStr, nullptr);
|
|
||||||
if (isnan(value)) {
|
|
||||||
error("Failed to parse double for \"%s\": %s", name, valueStr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((!isnan(min) && value < min) || (!isnan(max) && 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);
|
|
||||||
configPutDouble(name, value);
|
|
||||||
} else {
|
|
||||||
info("Value for \"%s\" unchanged: %f", name, value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cmdReadU64(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;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,20 +1,14 @@
|
|||||||
#ifndef SENSOR3_CONSOLE_H
|
#ifndef SENSOR3_CONSOLE_H
|
||||||
#define SENSOR3_CONSOLE_H
|
#define SENSOR3_CONSOLE_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
void consoleSetup();
|
void consoleSetup();
|
||||||
|
|
||||||
void consoleLoop();
|
void consoleLoop();
|
||||||
|
|
||||||
void consoleHandle(char *cmd);
|
void consoleHandle(char *cmd);
|
||||||
|
|
||||||
bool patrixCommand(char *cmd);
|
bool patrix_command(char *cmd);
|
||||||
|
|
||||||
void consolePrintUsage();
|
void _usage();
|
||||||
|
|
||||||
bool cmdReadDouble(const char *name, double *destinationPtr, double min, double max);
|
|
||||||
|
|
||||||
bool cmdReadU64(const char *name, uint64_t *destinationPtr, uint64_t min, uint64_t max);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,17 +3,9 @@
|
|||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include "wifi.h"
|
|
||||||
#include "mqtt.h"
|
|
||||||
|
|
||||||
struct IData {
|
struct IData {
|
||||||
|
|
||||||
time_t time;
|
|
||||||
|
|
||||||
explicit IData(time_t time) : time(time) {
|
|
||||||
// -
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void toJson(JsonObject &json) const = 0;
|
virtual void toJson(JsonObject &json) const = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -26,13 +18,16 @@ class Cache {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
T buffer[size];
|
struct Entry {
|
||||||
|
time_t timestamp = 0;
|
||||||
|
T data;
|
||||||
|
};
|
||||||
|
|
||||||
T *bufferRead = buffer;
|
Entry buffer[size];
|
||||||
|
|
||||||
T *bufferWrite = buffer;
|
Entry *bufferRead = buffer;
|
||||||
|
|
||||||
T last{};
|
Entry *bufferWrite = buffer;
|
||||||
|
|
||||||
size_t usage = 0;
|
size_t usage = 0;
|
||||||
|
|
||||||
@ -43,26 +38,25 @@ public:
|
|||||||
// -
|
// -
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add(T data) {
|
bool add(const time_t timestamp, const T &data) {
|
||||||
if (usage < size && data.needsPublish(last)) {
|
if (usage >= size) {
|
||||||
last = data;
|
return false;
|
||||||
*bufferWrite = data;
|
|
||||||
bufferWrite = (bufferWrite - buffer + 1) % size + buffer;
|
|
||||||
usage++;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
bufferWrite->timestamp = timestamp;
|
||||||
|
bufferWrite->data = data;
|
||||||
|
bufferWrite = (bufferWrite - buffer + 1) % size + buffer;
|
||||||
|
usage++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (usage == 0 || !isTimeSet()) {
|
if (usage == 0 || !isTimeSet()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JsonDocument doc;
|
JsonDocument json;
|
||||||
JsonObject json = doc.to<JsonObject>();
|
json["timestamp"] = correctTime(bufferRead->timestamp);
|
||||||
correctTime(bufferRead->time);
|
JsonObject data = json["data"].to<JsonObject>();
|
||||||
json["timestamp"] = bufferRead->time;
|
bufferRead->data.toJson(data);
|
||||||
bufferRead->toJson(json);
|
|
||||||
if (mqttPublishData(json)) {
|
if (mqttPublishData(json)) {
|
||||||
bufferRead = (bufferRead - buffer + 1) % size + buffer;
|
bufferRead = (bufferRead - buffer + 1) % size + buffer;
|
||||||
usage--;
|
usage--;
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
#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
|
|
||||||
@ -15,22 +15,36 @@ private:
|
|||||||
|
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
|
|
||||||
|
const double valueThreshold;
|
||||||
|
|
||||||
const time_t timeoutSec;
|
const time_t timeoutSec;
|
||||||
|
|
||||||
|
const time_t minIntervalMillis;
|
||||||
|
|
||||||
|
double lastSentValue = NAN;
|
||||||
|
|
||||||
|
unsigned long lastSentMillis = 0;
|
||||||
|
|
||||||
double lastValue = NAN;
|
double lastValue = NAN;
|
||||||
|
|
||||||
time_t lastTimestamp = 0;
|
time_t lastTimestamp = 0;
|
||||||
|
|
||||||
|
double lastValidValue = NAN;
|
||||||
|
|
||||||
|
time_t lastValidTimestamp = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DallasSensor(Dallas &sensors, const uint64_t address, const time_t timeoutSec) :
|
DallasSensor(Dallas &sensors, const uint64_t address, const double valueThreshold, const time_t timeoutSec, const time_t minIntervalMillis) :
|
||||||
sensors(sensors),
|
sensors(sensors),
|
||||||
address(address),
|
address(address),
|
||||||
timeoutSec(timeoutSec) {
|
valueThreshold(valueThreshold),
|
||||||
|
timeoutSec(timeoutSec),
|
||||||
|
minIntervalMillis(minIntervalMillis) {
|
||||||
// -
|
// -
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
bool loop() {
|
||||||
const time_t now = getTime();
|
const time_t now = getTime();
|
||||||
if (now - lastTimestamp > timeoutSec) {
|
if (now - lastTimestamp > timeoutSec) {
|
||||||
lastTimestamp = 0;
|
lastTimestamp = 0;
|
||||||
@ -39,9 +53,21 @@ public:
|
|||||||
if (sensors.isConverted()) {
|
if (sensors.isConverted()) {
|
||||||
const double value = sensors.read(address);
|
const double value = sensors.read(address);
|
||||||
const time_t timestamp = sensors.getTimestamp();
|
const time_t timestamp = sensors.getTimestamp();
|
||||||
|
const unsigned long millisNow = millis();
|
||||||
|
const bool doPublish = !isnan(value) && (abs(lastSentValue - value) >= valueThreshold || millisNow - lastSentMillis >= minIntervalMillis);
|
||||||
|
if (doPublish) {
|
||||||
|
lastSentValue = value;
|
||||||
|
lastSentMillis = millisNow;
|
||||||
|
}
|
||||||
lastValue = value;
|
lastValue = value;
|
||||||
lastTimestamp = timestamp;
|
lastTimestamp = timestamp;
|
||||||
|
if (!isnan(value)) {
|
||||||
|
lastValidValue = value;
|
||||||
|
lastValidTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
return doPublish;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double getLastValue() const {
|
[[nodiscard]] double getLastValue() const {
|
||||||
@ -52,6 +78,14 @@ public:
|
|||||||
return lastTimestamp;
|
return lastTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double getLastValidValue() const {
|
||||||
|
return lastValidValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] time_t getLastValidTimestamp() const {
|
||||||
|
return lastValidTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
#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
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
#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
|
|
||||||
@ -195,10 +195,18 @@ time_t getTime() {
|
|||||||
return epochSeconds;
|
return epochSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void correctTime(time_t &value) {
|
time_t correctTime(const time_t value) {
|
||||||
if (timeSet && value < MIN_EPOCH_SECONDS) {
|
if (!timeSet) {
|
||||||
value = getTime() - preTimeOffset + value;
|
return value;
|
||||||
}
|
}
|
||||||
|
if (value < MIN_EPOCH_SECONDS) {
|
||||||
|
return getTime() - preTimeOffset + value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOlderThan(time_t time, time_t seconds) {
|
||||||
|
return getTime() > correctTime(time) + seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getDateTime(char *buffer, size_t size) {
|
void getDateTime(char *buffer, size_t size) {
|
||||||
|
|||||||
@ -15,7 +15,9 @@ bool isTimeSet();
|
|||||||
|
|
||||||
time_t getTime();
|
time_t getTime();
|
||||||
|
|
||||||
void correctTime(time_t &value);
|
time_t correctTime(time_t value);
|
||||||
|
|
||||||
|
bool isOlderThan(time_t time, time_t seconds);
|
||||||
|
|
||||||
void getDateTime(char *buffer, size_t size);
|
void getDateTime(char *buffer, size_t size);
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ lib_deps = https://github.com/milesburton/Arduino-Temperature-Control-Library
|
|||||||
Wire
|
Wire
|
||||||
https://github.com/phassel/ArduPID/
|
https://github.com/phassel/ArduPID/
|
||||||
https://github.com/wayoda/LedControl
|
https://github.com/wayoda/LedControl
|
||||||
https://github.com/me-no-dev/ESPAsyncWebServer
|
|
||||||
|
|
||||||
[env:TEST32]
|
[env:TEST32]
|
||||||
upload_port = 10.42.0.66
|
upload_port = 10.42.0.66
|
||||||
@ -31,7 +30,7 @@ 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}
|
||||||
lib_deps = ${COMMON.lib_deps}
|
lib_deps = ${COMMON.lib_deps}
|
||||||
build_flags = -D TEST32 -D HOSTNAME=\"TEST32\" -D WIFI_SSID=\"${COMMON.WIFI_SSID}\" -D WIFI_PKEY=\"${COMMON.WIFI_PKEY}\" -D OTA_PASSWORD=\"OtaAuthPatrixTEST32\" -D BOOT_DELAY=false -D DEBUG_LOG=true
|
build_flags = -D TEST32 -D HOSTNAME=\"TEST32\" -D WIFI_SSID=\"${COMMON.WIFI_SSID}\" -D WIFI_PKEY=\"${COMMON.WIFI_PKEY}\" -D OTA_PASSWORD=\"OtaAuthPatrixTEST32\" -D BOOT_DELAY=false -D DEBUG_LOG=false
|
||||||
|
|
||||||
[env:TEST8266]
|
[env:TEST8266]
|
||||||
upload_port = 10.0.0.162
|
upload_port = 10.0.0.162
|
||||||
@ -61,19 +60,4 @@ monitor_port = ${COMMON.monitor_port}
|
|||||||
monitor_speed = ${COMMON.monitor_speed}
|
monitor_speed = ${COMMON.monitor_speed}
|
||||||
monitor_filters = esp8266_exception_decoder
|
monitor_filters = esp8266_exception_decoder
|
||||||
lib_deps = ${COMMON.lib_deps}
|
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
|
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=true
|
||||||
|
|
||||||
[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
|
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
#include "FementerDisplay.h"
|
|
||||||
|
|
||||||
#include "LedControl.h"
|
|
||||||
|
|
||||||
LedControl lc = LedControl(13, 14, 15, 1);
|
|
||||||
|
|
||||||
void writeDecimal(int *digit, double value) {
|
|
||||||
const int integer = (int) value;
|
|
||||||
const int decimal = (int) ((value - integer) * 10) % 10;
|
|
||||||
lc.setDigit(0, (*digit)++, decimal, false);
|
|
||||||
lc.setDigit(0, (*digit)++, integer % 10, true);
|
|
||||||
lc.setDigit(0, (*digit)++, integer / 10 % 10, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayLoop(double temperature, double target) {
|
|
||||||
static unsigned long lastDisplayInit = 0;
|
|
||||||
if (lastDisplayInit == 0 || millis() - lastDisplayInit > 60 * 60 * 1000) {
|
|
||||||
lastDisplayInit = millis();
|
|
||||||
lc.shutdown(0, true);
|
|
||||||
lc.shutdown(0, false);
|
|
||||||
lc.setIntensity(0, 4);
|
|
||||||
lc.clearDisplay(0);
|
|
||||||
}
|
|
||||||
int digit = 0;
|
|
||||||
writeDecimal(&digit, temperature);
|
|
||||||
digit++;
|
|
||||||
digit++;
|
|
||||||
writeDecimal(&digit, target);
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#ifndef SENSOR3_FEMENTERDISPLAY_H
|
|
||||||
#define SENSOR3_FEMENTERDISPLAY_H
|
|
||||||
|
|
||||||
void displayLoop(double temperature, double target);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,38 +1,192 @@
|
|||||||
#if defined(FERMENTER) || defined(TEST8266)
|
#if defined(FERMENTER) || defined(TEST8266)
|
||||||
|
|
||||||
#include <Patrix.h>
|
#include <Patrix.h>
|
||||||
|
#include "sensors/Dallas.h"
|
||||||
|
#include "sensors/DallasSensor.h"
|
||||||
|
#include "ArduPID.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "LedControl.h"
|
||||||
#include "FermenterData.h"
|
#include "FermenterData.h"
|
||||||
#include "FementerDisplay.h"
|
|
||||||
#include "FermenterPID.h"
|
|
||||||
#include "FermenterSensor.h"
|
|
||||||
|
|
||||||
Cache<FermenterData, 800> cache;
|
#define SENSOR_GPIO D4
|
||||||
|
#define CONTROL_GPIO D2
|
||||||
|
#define CONTROL_PWM_BITS 10
|
||||||
|
#define CONTROL_PWM_MAX (pow(2, CONTROL_PWM_BITS) - 1)
|
||||||
|
|
||||||
|
#define PID_DEFAULT_P 1500
|
||||||
|
#define PID_DEFAULT_I 0
|
||||||
|
#define PID_DEFAULT_D 0
|
||||||
|
#define PID_DEFAULT_TARGET 31
|
||||||
|
#define PID_DEFAULT_OVER 5
|
||||||
|
|
||||||
|
Dallas dallas(SENSOR_GPIO);
|
||||||
|
|
||||||
|
DallasSensor sensor(dallas, 0x3D0417C1D740FF28, 0.1, 5, 60 * 1000);
|
||||||
|
|
||||||
|
LedControl lc = LedControl(13, 14, 15, 1);
|
||||||
|
|
||||||
|
ArduPID pid;
|
||||||
|
|
||||||
|
double proportional = PID_DEFAULT_P;
|
||||||
|
|
||||||
|
double integral = PID_DEFAULT_I;
|
||||||
|
|
||||||
|
double derivative = PID_DEFAULT_D;
|
||||||
|
|
||||||
|
double temperatureTarget = PID_DEFAULT_TARGET;
|
||||||
|
|
||||||
|
double temperatureOver = PID_DEFAULT_OVER;
|
||||||
|
|
||||||
|
double temperatureCurrent = NAN;
|
||||||
|
|
||||||
|
double heaterPWM = 0;
|
||||||
|
|
||||||
|
Cache<FermenterData, 500> cache;
|
||||||
|
|
||||||
|
void writeDecimal(int *digit, double value);
|
||||||
|
|
||||||
|
void displayLoop();
|
||||||
|
|
||||||
|
void pidLoop();
|
||||||
|
|
||||||
void patrixSetup() {
|
void patrixSetup() {
|
||||||
sensorSetup();
|
dallas.begin();
|
||||||
pidSetup(&temperature);
|
analogWriteResolution(CONTROL_PWM_BITS);
|
||||||
|
|
||||||
|
configLoaded();
|
||||||
|
|
||||||
|
pid.begin(&temperatureCurrent, &heaterPWM, &temperatureTarget, proportional, integral, derivative);
|
||||||
|
pid.setOutputLimits(0, CONTROL_PWM_MAX);
|
||||||
|
pid.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void patrixLoop() {
|
void patrixLoop() {
|
||||||
sensorLoop();
|
dallas.loop();
|
||||||
pidLoop();
|
if (sensor.loop()) {
|
||||||
displayLoop(temperature, target);
|
const FermenterData data = {sensor.getLastValue(), temperatureTarget, proportional, integral, derivative};
|
||||||
cache.add({getTime(), (float) temperature, (float) target, getHeaterPercent(), (float) proportional, (float) integral, (float) derivative});
|
cache.add(sensor.getLastTimestamp(), data);
|
||||||
|
}
|
||||||
cache.loop();
|
cache.loop();
|
||||||
|
displayLoop();
|
||||||
|
pidLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool patrixCommand(char *first) {
|
void pidLoop() {
|
||||||
|
temperatureCurrent = sensor.getLastValue();
|
||||||
|
if (!isnan(temperatureCurrent)) {
|
||||||
|
pid.compute();
|
||||||
|
} else {
|
||||||
|
heaterPWM = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *emergencyStr = "";
|
||||||
|
if (heaterPWM > 0 && temperatureCurrent > temperatureTarget + temperatureOver) {
|
||||||
|
heaterPWM = 0;
|
||||||
|
emergencyStr = "[EMERGENCY CUTOFF]";
|
||||||
|
}
|
||||||
|
|
||||||
|
analogWrite(CONTROL_GPIO, (int) round(heaterPWM));
|
||||||
|
|
||||||
|
static unsigned long lastDebug = 0;
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastDebug >= 1000) {
|
||||||
|
lastDebug = now;
|
||||||
|
int heaterPercent = (int) round(100.0 * heaterPWM / CONTROL_PWM_MAX);
|
||||||
|
debug(
|
||||||
|
"cache: %3d/%d | p: %f | i: %f | d: %f | target: %4.1f | heater: %3d%% | temperature: %5.2f %s",
|
||||||
|
cache.getUsage(),
|
||||||
|
cache.getSize(),
|
||||||
|
|
||||||
|
proportional == 0 ? NAN : proportional,
|
||||||
|
integral == 0 ? NAN : integral,
|
||||||
|
derivative == 0 ? NAN : derivative,
|
||||||
|
|
||||||
|
temperatureTarget,
|
||||||
|
heaterPercent,
|
||||||
|
temperatureCurrent,
|
||||||
|
|
||||||
|
emergencyStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayLoop() {
|
||||||
|
static unsigned long lastDisplayInit = 0;
|
||||||
|
if (lastDisplayInit == 0 || millis() - lastDisplayInit > 60 * 60 * 1000) {
|
||||||
|
lastDisplayInit = millis();
|
||||||
|
lc.shutdown(0, true);
|
||||||
|
lc.shutdown(0, false);
|
||||||
|
lc.setIntensity(0, 4);
|
||||||
|
lc.clearDisplay(0);
|
||||||
|
}
|
||||||
|
int digit = 0;
|
||||||
|
writeDecimal(&digit, temperatureCurrent);
|
||||||
|
digit++;
|
||||||
|
digit++;
|
||||||
|
writeDecimal(&digit, temperatureTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDecimal(int *digit, double value) {
|
||||||
|
const int integer = (int) value;
|
||||||
|
const int decimal = (int) ((value - integer) * 10) % 10;
|
||||||
|
lc.setDigit(0, (*digit)++, decimal, false);
|
||||||
|
lc.setDigit(0, (*digit)++, integer % 10, true);
|
||||||
|
lc.setDigit(0, (*digit)++, integer / 10 % 10, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setDouble(const char *name, double *destinationPtr, double min, double max) {
|
||||||
|
const char *valueStr = strtok(nullptr, "");
|
||||||
|
if (valueStr == nullptr) {
|
||||||
|
_usage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
double value = strtod(valueStr, nullptr);
|
||||||
|
if (isnan(value)) {
|
||||||
|
error("Failed to parse double for \"%s\": %s", name, valueStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((!isnan(min) && value < min) || (!isnan(max) && 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);
|
||||||
|
configPutDouble(name, value);
|
||||||
|
} else {
|
||||||
|
info("Value for \"%s\" unchanged: %f", name, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool patrix_command(char *first) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (strcmp(first, "p") == 0) {
|
if (strcmp(first, "over") == 0 || strcmp(first, "o") == 0) {
|
||||||
result = cmdReadDouble("p", &proportional, 0, NAN);
|
result = setDouble("over", &temperatureOver, 0, 20);
|
||||||
} else if (strcmp(first, "i") == 0) {
|
} else if (strcmp(first, "target") == 0 || strcmp(first, "t") == 0) {
|
||||||
result = cmdReadDouble("i", &integral, 0, NAN);
|
result = setDouble("target", &temperatureTarget, -10, 60);
|
||||||
} else if (strcmp(first, "d") == 0) {
|
} else if (strcmp(first, "proportional") == 0 || strcmp(first, "p") == 0) {
|
||||||
result = cmdReadDouble("d", &derivative, 0, NAN);
|
result = setDouble("p", &proportional, NAN, NAN);
|
||||||
} else if (strcmp(first, "t") == 0) {
|
} else if (strcmp(first, "integral") == 0 || strcmp(first, "i") == 0) {
|
||||||
result = cmdReadDouble("t", &target, -10, 60);
|
result = setDouble("i", &integral, NAN, NAN);
|
||||||
|
} else if (strcmp(first, "derivative") == 0 || strcmp(first, "d") == 0) {
|
||||||
|
result = setDouble("d", &derivative, NAN, NAN);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
pid.setCoefficients(proportional, integral, derivative);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void configLoaded() {
|
||||||
|
proportional = configGetDouble("p", PID_DEFAULT_P);
|
||||||
|
integral = configGetDouble("i", PID_DEFAULT_I);
|
||||||
|
derivative = configGetDouble("d", PID_DEFAULT_D);
|
||||||
|
temperatureTarget = configGetDouble("target", PID_DEFAULT_TARGET);
|
||||||
|
temperatureOver = configGetDouble("over", PID_DEFAULT_OVER);
|
||||||
|
pid.setCoefficients(proportional, integral, derivative);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,65 +1,35 @@
|
|||||||
#ifndef SENSOR3_FERMENTER_DATA_H
|
#ifndef SENSOR3_FERMENTERDATA_H
|
||||||
#define SENSOR3_FERMENTER_DATA_H
|
#define SENSOR3_FERMENTERDATA_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
struct FermenterData : IData {
|
struct FermenterData : IData {
|
||||||
float currentCelsius;
|
double temperature;
|
||||||
float targetCelsius;
|
double target;
|
||||||
uint8_t heaterPercent;
|
double p;
|
||||||
float p;
|
double i;
|
||||||
float i;
|
double d;
|
||||||
float d;
|
|
||||||
|
|
||||||
FermenterData() : IData(0) {
|
FermenterData() {
|
||||||
currentCelsius = NAN;
|
temperature = NAN;
|
||||||
targetCelsius = NAN;
|
target = NAN;
|
||||||
heaterPercent = 0;
|
|
||||||
p = NAN;
|
p = NAN;
|
||||||
i = NAN;
|
i = NAN;
|
||||||
d = NAN;
|
d = NAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
FermenterData(
|
FermenterData(double temperature, double target, double p, double i, double d) : temperature(temperature), target(target), p(p), i(i), d(d) {
|
||||||
time_t time,
|
|
||||||
float currentCelsius,
|
|
||||||
float targetCelsius,
|
|
||||||
uint8_t heaterPercent,
|
|
||||||
float p,
|
|
||||||
float i,
|
|
||||||
float d
|
|
||||||
) :
|
|
||||||
IData(time),
|
|
||||||
currentCelsius(currentCelsius),
|
|
||||||
targetCelsius(targetCelsius),
|
|
||||||
heaterPercent(heaterPercent),
|
|
||||||
p(p),
|
|
||||||
i(i),
|
|
||||||
d(d) {
|
|
||||||
// -
|
// -
|
||||||
}
|
}
|
||||||
|
|
||||||
void toJson(JsonObject &json) const override {
|
void toJson(JsonObject &json) const override {
|
||||||
json["currentCelsius"] = currentCelsius;
|
json["temperature"] = temperature;
|
||||||
json["targetCelsius"] = targetCelsius;
|
json["target"] = target;
|
||||||
json["heaterPercent"] = heaterPercent;
|
|
||||||
json["p"] = p;
|
json["p"] = p;
|
||||||
json["i"] = i;
|
json["i"] = i;
|
||||||
json["d"] = d;
|
json["d"] = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool needsPublish(FermenterData &last) {
|
|
||||||
correctTime(time);
|
|
||||||
correctTime(last.time);
|
|
||||||
return time - last.time >= 60
|
|
||||||
|| this->p != last.p
|
|
||||||
|| this->i != last.i
|
|
||||||
|| this->d != last.d
|
|
||||||
|| abs(this->currentCelsius - last.currentCelsius) >= 0.1
|
|
||||||
|| this->targetCelsius != last.targetCelsius
|
|
||||||
|| this->heaterPercent != last.heaterPercent;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,62 +0,0 @@
|
|||||||
#include "FermenterPID.h"
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define CONTROL_GPIO D2
|
|
||||||
#define CONTROL_PWM_BITS 10
|
|
||||||
#define CONTROL_PWM_MAX (pow(2, CONTROL_PWM_BITS) - 1)
|
|
||||||
|
|
||||||
ArduPID pid;
|
|
||||||
|
|
||||||
double proportional = 0;
|
|
||||||
|
|
||||||
double integral = 0;
|
|
||||||
|
|
||||||
double derivative = 0;
|
|
||||||
|
|
||||||
double target = 0;
|
|
||||||
|
|
||||||
double heater = 0;
|
|
||||||
|
|
||||||
void pidSetup(double *temperature) {
|
|
||||||
analogWriteResolution(CONTROL_PWM_BITS);
|
|
||||||
|
|
||||||
proportional = configGetDouble("p", 0);
|
|
||||||
integral = configGetDouble("i", 0);
|
|
||||||
derivative = configGetDouble("d", 0);
|
|
||||||
target = configGetDouble("t", 0);
|
|
||||||
pid.begin(temperature, &heater, &target, proportional, integral, derivative);
|
|
||||||
pid.setOutputLimits(0, CONTROL_PWM_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pidLoop() {
|
|
||||||
if (!isnan(*pid.input)) {
|
|
||||||
pid.setCoefficients(proportional, integral, derivative);
|
|
||||||
pid.compute();
|
|
||||||
} else {
|
|
||||||
heater = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
analogWrite(CONTROL_GPIO, (int) round(heater));
|
|
||||||
|
|
||||||
static unsigned long lastDebug = 0;
|
|
||||||
unsigned long now = millis();
|
|
||||||
if (now - lastDebug >= 1000) {
|
|
||||||
lastDebug = now;
|
|
||||||
debug(
|
|
||||||
"p: %f | i: %.12f | d: %.12f | target: %4.1f | heater: %3d%% | temperature: %5.2f",
|
|
||||||
proportional == 0 ? NAN : proportional,
|
|
||||||
integral == 0 ? NAN : integral,
|
|
||||||
derivative == 0 ? NAN : derivative,
|
|
||||||
|
|
||||||
*pid.setpoint,
|
|
||||||
getHeaterPercent(),
|
|
||||||
*pid.input
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t getHeaterPercent() {
|
|
||||||
return (int) round(100.0 * heater / CONTROL_PWM_MAX);
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
#ifndef SENSOR3_FERMENTERPID_H
|
|
||||||
#define SENSOR3_FERMENTERPID_H
|
|
||||||
|
|
||||||
#include "ArduPID.h"
|
|
||||||
|
|
||||||
extern ArduPID pid;
|
|
||||||
|
|
||||||
extern double proportional;
|
|
||||||
|
|
||||||
extern double integral;
|
|
||||||
|
|
||||||
extern double derivative;
|
|
||||||
|
|
||||||
extern double target;
|
|
||||||
|
|
||||||
void pidSetup(double *temperature);
|
|
||||||
|
|
||||||
void pidLoop();
|
|
||||||
|
|
||||||
uint8_t getHeaterPercent();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
#include "FermenterSensor.h"
|
|
||||||
|
|
||||||
#include "sensors/Dallas.h"
|
|
||||||
#include "sensors/DallasSensor.h"
|
|
||||||
|
|
||||||
#define SENSOR_GPIO D4
|
|
||||||
|
|
||||||
Dallas dallas(SENSOR_GPIO);
|
|
||||||
|
|
||||||
DallasSensor sensor(dallas, 0x3D0417C1D740FF28, 5);
|
|
||||||
|
|
||||||
double temperature = NAN;
|
|
||||||
|
|
||||||
void sensorSetup() {
|
|
||||||
dallas.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sensorLoop() {
|
|
||||||
dallas.loop();
|
|
||||||
sensor.loop();
|
|
||||||
temperature = sensor.getLastValue();
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef SENSOR3_FERMENTERSENSOR_H
|
|
||||||
#define SENSOR3_FERMENTERSENSOR_H
|
|
||||||
|
|
||||||
extern double temperature;
|
|
||||||
|
|
||||||
void sensorSetup();
|
|
||||||
|
|
||||||
void sensorLoop();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,187 +0,0 @@
|
|||||||
#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 patrixCommand(char *first) {
|
|
||||||
bool result = false;
|
|
||||||
if (strcmp(first, "tempHigh") == 0 || strcmp(first, "th") == 0) {
|
|
||||||
result = cmdReadDouble("tempHigh", &tempHigh, NAN, NAN);
|
|
||||||
} else if (strcmp(first, "tempLow") == 0 || strcmp(first, "tl") == 0) {
|
|
||||||
result = cmdReadDouble("tempLow", &tempLow, NAN, NAN);
|
|
||||||
} else if (strcmp(first, "tempMillis") == 0 || strcmp(first, "tm") == 0) {
|
|
||||||
result = cmdReadU64("tempMillis", &tempMillis, NAN, NAN);
|
|
||||||
} else if (strcmp(first, "moistHigh") == 0 || strcmp(first, "mh") == 0) {
|
|
||||||
result = cmdReadDouble("moistHigh", &moistHigh, NAN, NAN);
|
|
||||||
} else if (strcmp(first, "moistLow") == 0 || strcmp(first, "ml") == 0) {
|
|
||||||
result = cmdReadDouble("moistLow", &moistLow, NAN, NAN);
|
|
||||||
} else if (strcmp(first, "moistMillis") == 0 || strcmp(first, "mm") == 0) {
|
|
||||||
result = cmdReadU64("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
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
#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
|
|
||||||
Loading…
Reference in New Issue
Block a user