diff --git a/data/http/index.html b/data/http/index.html
new file mode 100644
index 0000000..3937531
--- /dev/null
+++ b/data/http/index.html
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+ Gärbox
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index 5fdad78..e38e4fa 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -4,10 +4,15 @@ lib_deps = https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/adafruit/Adafruit_TSL2561
https://github.com/knolleary/pubsubclient
https://github.com/adafruit/Adafruit_BME680
+ https://github.com/phassel/ArduPID/
+ https://github.com/me-no-dev/ESPAsyncWebServer
+ https://github.com/wayoda/LedControl
build_flags =
+board_build.filesystem = littlefs
monitor_speed = 115200
-;upload_port = /dev/ttyUSB0
-;upload_speed = 460800
+upload_port = /dev/ttyUSB0
+upload_speed = 460800
+upload_protocol = espota
[esp12e]
platform = espressif8266
@@ -23,9 +28,10 @@ board = ${esp12e.board}
framework = ${common.framework}
lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} -DNODE_GREENHOUSE -DHOSTNAME=\"Greenhouse\"
+board_build.filesystem = ${common.board_build.filesystem}
monitor_speed = ${common.monitor_speed}
-upload_port = /dev/ttyUSB0
-upload_speed = 460800
+upload_port = ${common.upload_port}
+upload_speed = ${common.upload_speed}
[env:GreenhouseOTA]
platform = ${esp12e.platform}
@@ -33,9 +39,10 @@ board = ${esp12e.board}
framework = ${common.framework}
lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} -DNODE_GREENHOUSE -DHOSTNAME=\"Greenhouse\"
+board_build.filesystem = ${common.board_build.filesystem}
monitor_speed = ${common.monitor_speed}
-upload_protocol = espota
-upload_port = 10.0.0.169
+upload_protocol = ${common.upload_protocol}
+upload_port = 10.0.0.160
[env:FermenterUSB]
platform = ${esp12e.platform}
@@ -43,9 +50,10 @@ board = ${esp12e.board}
framework = ${common.framework}
lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} -DNODE_FERMENTER -DHOSTNAME=\"Fermenter\"
+board_build.filesystem = ${common.board_build.filesystem}
monitor_speed = ${common.monitor_speed}
-upload_port = /dev/ttyUSB0
-upload_speed = 460800
+upload_port = ${common.upload_port}
+upload_speed = ${common.upload_speed}
[env:FermenterOTA]
platform = ${esp12e.platform}
@@ -53,6 +61,8 @@ board = ${esp12e.board}
framework = ${common.framework}
lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} -DNODE_FERMENTER -DHOSTNAME=\"Fermenter\"
+board_build.filesystem = ${common.board_build.filesystem}
monitor_speed = ${common.monitor_speed}
-upload_protocol = espota
-;upload_port = 10.0.0.169
+upload_flags = --auth=OtaAuthPatrixFermenter
+upload_protocol = ${common.upload_protocol}
+upload_port = 10.0.0.171
diff --git a/src/Fermenter.cpp b/src/Fermenter.cpp
index ad15e74..f1bc03c 100644
--- a/src/Fermenter.cpp
+++ b/src/Fermenter.cpp
@@ -1,24 +1,116 @@
#ifdef NODE_FERMENTER
-#include "patrix/bme680.h"
-#include "patrix/tsl2561.h"
+#include
+#include
+#include
-DS18B20 ds18b20();
+#include "patrix/DS18B20Sensor.h"
+#include "patrix/PIDController.h"
+#include "patrix/PWMOutput.h"
-DS18B20Sensor sensor(ds18b20);
+#define HEATER_POWER_W 30
-PWMOutput heater();
+AsyncWebServer server(80);
+
+DS18B20 ds18b20("DS18B20", D4);
+
+DS18B20Sensor input(ds18b20, 0, "");
+
+PWMOutput heater(D2, "");
+
+PIDController pid("fermenter", input, heater, UNIT_TEMPERATURE_C, 500, 0.00000002, 0);
+
+auto display = LedControl(13, 14, 15, 1);
+
+void displayDecimal(int *digit, const double value) {
+ const auto integer = static_cast(value);
+ const auto decimal = static_cast((value - integer) * 10) % 10;
+ display.setDigit(0, (*digit)++, decimal, false);
+ display.setDigit(0, (*digit)++, integer % 10, true);
+ display.setDigit(0, (*digit)++, integer / 10 % 10, false);
+}
+
+void displayLoop() {
+ static unsigned long lastDisplayInit = 0;
+ if (lastDisplayInit == 0 || millis() - lastDisplayInit > 60 * 60 * 1000) {
+ lastDisplayInit = millis();
+ display.shutdown(0, true);
+ display.shutdown(0, false);
+ display.setIntensity(0, 4);
+ display.clearDisplay(0);
+ }
+ auto digit = 0;
+ displayDecimal(&digit, input.getValue());
+ digit++;
+ digit++;
+ displayDecimal(&digit, pid.targetValue);
+}
+
+void httpStatus(AsyncWebServerRequest *request) {
+ char buffer[256];
+ snprintf(buffer, sizeof buffer, R"({"target": %f, "input": %f, "outputPercent": %f, "outputPowerW": %f})", pid.targetValue, input.getValue(), heater.getPercent(), heater.getPercent() / 100.0 * HEATER_POWER_W);
+ request->send(200, "application/json", buffer);
+}
+
+void httpTargetAdd(AsyncWebServerRequest *request) {
+ const auto delta = request->getParam("delta");
+ if (delta == nullptr) {
+ Log.error("Missing parameter: delta (1)");
+ return;
+ }
+
+ const auto string = delta->value();
+ if (string == nullptr) {
+ Log.error("Missing parameter: delta (2)");
+ return;
+ }
+
+ const auto value = string.toDouble();
+ if (isnan(value)) {
+ Log.error("Missing parameter: delta (3)");
+ return;
+ }
+
+ pid.targetValue = max(0.0, min(40.0, pid.targetValue + value));
+ Log.info("Set targetValue = %.1f%cC", pid.targetValue, 176);
+
+ httpStatus(request);
+}
+
+void httpNotFound(AsyncWebServerRequest *request) {
+ if (request->method() == HTTP_OPTIONS) {
+ request->send(200);
+ } else {
+ request->send(404, "text/plain", "not found");
+ }
+}
void patrixSetup() {
ds18b20.setup();
- sensor.setup();
heater.setup();
+ pid.setup();
+
+ if (LittleFS.begin()) {
+ Log.info("Filesystem mounted.");
+ } else {
+ Log.error("Failed to mount filesystem!");
+ }
+ DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
+ DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, POST, PUT");
+ DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Content-Type");
+ server.serveStatic("/", LittleFS, "/http/", "max-age=86400").setDefaultFile("index.html");
+ server.on("/status", httpStatus);
+ server.on("/status/", httpStatus);
+ server.on("/target/add", httpTargetAdd);
+ server.on("/target/add/", httpTargetAdd);
+ server.onNotFound(httpNotFound);
+ server.begin();
}
void patrixLoop() {
ds18b20.loop();
- sensor.loop();
- heater.loop();
+ input.loop();
+ pid.loop();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/patrix/DS18B20.h b/src/patrix/DS18B20.h
index 7708e58..d3b5397 100644
--- a/src/patrix/DS18B20.h
+++ b/src/patrix/DS18B20.h
@@ -1,74 +1,105 @@
#ifndef HELLIGKEIT_DS18B20_H
#define HELLIGKEIT_DS18B20_H
-#include "OneWire.h"
+#include
+
#include "DallasTemperature.h"
+#include "IValueSensor.h"
+#include "mqtt.h"
+#include "OneWire.h"
class DS18B20 {
enum State {
- IDLE, CONVERTING, DONE
+ IDLE, READING, COMPLETE, ERROR
};
- String& name;
+ String name;
- const int pin;
+ const int gpio;
OneWire oneWire;
- DallasTemperature sensors;
+ DallasTemperature bus;
State state = IDLE;
- unsigned long timeout = 1000;
+ unsigned long last = 0;
+
+ boolean first = true;
public:
- explicit DS18B20(String& name, int pin) : name(name), pin(pin), oneWire(pin), sensors(&oneWire) {
+ unsigned long interval = 5000;
+
+ unsigned long timeout = 3000;
+
+ explicit DS18B20(String name, const int gpio) : name(std::move(name)), gpio(gpio), oneWire(gpio), bus(&oneWire) {
// nothing
}
- void begin() {
- sensors.begin();
- sensors.setWaitForConversion(false);
+ void setup() {
+ bus.begin();
+ bus.setWaitForConversion(false);
}
void loop() {
switch (state) {
- case IDLE:
- sensors.requestTemperatures();
- break;
- case CONVERTING:
- if (sensors.isConversionComplete()) {
-
+ case READING:
+ if (bus.isConversionComplete()) {
+ state = COMPLETE;
+ if (first) {
+ first = false;
+ Log.debug("DS18B20: %d devices", bus.getDeviceCount());
+ for (auto index = 0; index < bus.getDeviceCount(); index++) {
+ uint8_t address[8];
+ bus.getAddress(address, index);
+ char addressHex[19];
+ snprintf(addressHex, sizeof(addressHex), "0x%02X%02X%02X%02X%02X%02X%02X%02X", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]);
+ const auto temperature = bus.getTempC(address);
+ Log.debug(" %s: %5.1f%cC", addressHex, temperature, 176);
+ }
+ }
} else if (millis() - last > timeout) {
- Serial.printf("DS18B20 \"%s\" pin #%d: timeout", name.c_str(), pin);
+ state = ERROR;
+ Log.error("DS18B20 \"%s\" gpio #%d: timeout", name.c_str(), gpio);
}
+ break;
+ default:
+ state = IDLE;
+ if (last == 0 || millis() - last >= interval) {
+ last = max(1UL, millis());
+ state = READING;
+ bus.requestTemperatures();
+ }
+ break;
}
}
-};
-
-class DS18B20Sensor {
-
- DS18B20& bus;
-
- const int index;
-
-public:
-
- DS18B20Sensor(DS18B20& bus, int index) : bus(bus), index(index) {
- //
+ float getTemperatureByIndex(const int index) {
+ if (state != COMPLETE) {
+ return NAN;
+ }
+ const auto temperature = bus.getTempCByIndex(index);
+ return temperature == DEVICE_DISCONNECTED_C ? NAN : temperature;
}
- void setup() {
-
+ float getTemperatureByAddress(const uint8_t *address) {
+ if (state != COMPLETE) {
+ return NAN;
+ }
+ const auto temperature = bus.getTempC(address);
+ return temperature == DEVICE_DISCONNECTED_C ? NAN : temperature;
}
- void loop() {
+ [[nodiscard]] bool isComplete() const {
+ return state == COMPLETE;
+ }
+ [[nodiscard]] bool isError() const {
+ return state == ERROR;
}
};
-#endif //HELLIGKEIT_DS18B20_H
+#endif
diff --git a/src/patrix/DS18B20Sensor.h b/src/patrix/DS18B20Sensor.h
new file mode 100644
index 0000000..36c46ec
--- /dev/null
+++ b/src/patrix/DS18B20Sensor.h
@@ -0,0 +1,51 @@
+#ifndef DS18B20_SENSOR_H
+#define DS18B20_SENSOR_H
+
+#include
+
+#include "DS18B20.h"
+
+class DS18B20Sensor final : public IValueSensor {
+
+ DS18B20& bus;
+
+ String name;
+
+ const int index;
+
+ const uint8_t *address;
+
+ float temperature = NAN;
+
+public:
+
+ DS18B20Sensor(DS18B20& bus, const int index, String name) : bus(bus), name(std::move(name)), index(index), address{} {
+ //
+ }
+
+ DS18B20Sensor(DS18B20& bus, const uint8_t address[8], String name) : bus(bus), name(std::move(name)), index(-1), address(address) {
+ //
+ }
+
+ [[nodiscard]] float getValue() const override {
+ return temperature;
+ }
+
+ void loop() {
+ if (bus.isComplete()) {
+ if (index >= 0) {
+ temperature = bus.getTemperatureByIndex(index);
+ } else {
+ temperature = bus.getTemperatureByAddress(address);
+ }
+ if (!name.isEmpty()) {
+ mqttPublishValue(name, temperature, UNIT_TEMPERATURE_C);
+ }
+ } else if (bus.isError()) {
+ temperature = NAN;
+ }
+ }
+
+};
+
+#endif
diff --git a/src/patrix/IValueSensor.h b/src/patrix/IValueSensor.h
new file mode 100644
index 0000000..b0561bc
--- /dev/null
+++ b/src/patrix/IValueSensor.h
@@ -0,0 +1,14 @@
+#ifndef I_VALUE_SENSOR_H
+#define I_VALUE_SENSOR_H
+
+class IValueSensor {
+
+public:
+
+ virtual ~IValueSensor() = default;
+
+ [[nodiscard]] virtual float getValue() const = 0;
+
+};
+
+#endif
diff --git a/src/patrix/PIDController.h b/src/patrix/PIDController.h
new file mode 100644
index 0000000..62b8ae4
--- /dev/null
+++ b/src/patrix/PIDController.h
@@ -0,0 +1,70 @@
+#ifndef PID_CONTROLLER_H
+#define PID_CONTROLLER_H
+
+#include
+
+#include
+
+#include "IValueSensor.h"
+#include "PWMOutput.h"
+
+class PIDController {
+
+ const String name;
+
+ const IValueSensor& input;
+
+ PWMOutput& output;
+
+ const char *unit;
+
+ ArduPID controller;
+
+ double p = 0;
+
+ double i = 0;
+
+ double d = 0;
+
+ double inputValue = NAN;
+
+ double outputPercent = NAN;
+
+ unsigned long lastSent = 0UL;
+
+public:
+
+ double targetValue = 28;
+
+ PIDController(String name, const IValueSensor& sensor, PWMOutput& pwmOutput, const char *unit, const double p, const double i, const double d)
+ : name(std::move(name)), input(sensor), output(pwmOutput), unit(unit), controller(), p(p), i(i), d(d) {
+ //
+ }
+
+ void setup() {
+ controller.begin(&inputValue, &outputPercent, &targetValue, p, i, d);
+ controller.setOutputLimits(0, 100);
+ }
+
+ void loop() {
+ outputPercent = 0;
+ inputValue = input.getValue();
+ if (!isnan(inputValue) && !isnan(targetValue)) {
+ controller.setCoefficients(p, i, d);
+ controller.compute();
+ if (!isnan(outputPercent) && outputPercent >= 0 && outputPercent <= 100) {
+ const auto now = millis();
+ if (lastSent == 0 || now - lastSent >= 5000) {
+ lastSent = now;
+ mqttPublishValue(name + "/target", targetValue, unit);
+ mqttPublishValue(name + "/input", inputValue, unit);
+ mqttPublishValue(name + "/output", outputPercent, UNIT_PERCENT);
+ }
+ }
+ }
+ output.setPercent(outputPercent);
+ }
+
+};
+
+#endif
diff --git a/src/patrix/PWMOutput.h b/src/patrix/PWMOutput.h
new file mode 100644
index 0000000..b30dac3
--- /dev/null
+++ b/src/patrix/PWMOutput.h
@@ -0,0 +1,48 @@
+#ifndef PWM_OUTPUT_H
+#define PWM_OUTPUT_H
+
+#define CONTROL_PWM_BITS 10
+#define CONTROL_PWM_MAX (static_cast(pow(2, CONTROL_PWM_BITS) - 1))
+
+class PWMOutput {
+
+ const uint8_t gpio;
+
+ String name;
+
+ int value = 0;
+
+ double percent = 0;
+
+public:
+
+ explicit PWMOutput(const uint8_t gpio, String name): gpio(gpio), name(std::move(name)) {
+ //
+ }
+
+ void setup() {
+ analogWriteResolution(CONTROL_PWM_BITS);
+ setValue(0);
+ }
+
+ void setValue(const int v) {
+ value = max(0, min(CONTROL_PWM_MAX, v));
+ percent = 100.0 * value / CONTROL_PWM_MAX;
+ analogWrite(gpio, value);
+ }
+
+ void setPercent(const double percent) {
+ setValue(static_cast(percent / 100.0 * CONTROL_PWM_MAX));
+ }
+
+ [[nodiscard]] double getPercent() const {
+ return percent;
+ }
+
+ [[nodiscard]] int getValue() const {
+ return value;
+ }
+
+};
+
+#endif
diff --git a/src/patrix/Patrix.cpp b/src/patrix/Patrix.cpp
index e79f176..f5e2498 100644
--- a/src/patrix/Patrix.cpp
+++ b/src/patrix/Patrix.cpp
@@ -1,18 +1,17 @@
#include "Patrix.h"
void setup() {
- delay(500);
- Log.printf("\n\n\nStartup\n");
+ Log.info("Startup.");
wifiConnect();
}
void loop() {
wifiLoop();
mqttLoop();
- if (isSetupTimeAfterBootDelay()) {
- patrixSetup();
- }
if (isAfterBootDelay()) {
+ if (isSetupTimeAfterBootDelay()) {
+ patrixSetup();
+ }
patrixLoop();
}
}
diff --git a/src/patrix/bme680.h b/src/patrix/bme680.h
index b7632f0..b9595cb 100644
--- a/src/patrix/bme680.h
+++ b/src/patrix/bme680.h
@@ -16,15 +16,15 @@ public:
unsigned long intervalMs;
- explicit BME680(String name, const unsigned long interval_ms = 5000) : name(std::move(name)), intervalMs(interval_ms) {
+ explicit BME680(String name, const unsigned long interval_ms = 5000) : name(std::move(name)), intervalMs(interval_ms) {
//
}
void setup() {
if (bme.begin()) {
- Log.printf("BME680 \"%s\": Initialized.\n", name.c_str());
+ Log.info("BME680 \"%s\": Initialized.", name.c_str());
} else {
- Log.printf("BME680 \"%s\": Failed to initialize.\n", name.c_str());
+ Log.error("BME680 \"%s\": Failed to initialize.", name.c_str());
}
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
@@ -37,7 +37,7 @@ public:
const auto now = max(1UL, millis());
if (last == 0 || now - last >= intervalMs) {
if (bme.beginReading() == 0) {
- Log.printf("BME680 \"%s\": Failed to request reading.\n", name.c_str());
+ Log.error("BME680 \"%s\": Failed to request reading.", name.c_str());
setup();
}
last = now;
@@ -52,7 +52,7 @@ public:
mqttPublishValue(name + "/gas", bme.gas_resistance, "RESISTANCE_OHMS");
mqttPublishValue(name + "/altitude", bme.readAltitude(1013.25), "ALTITUDE_M");
} else {
- Log.printf("BME680 \"%s\": Failed to complete reading\n", name.c_str());
+ Log.error("BME680 \"%s\": Failed to complete reading", name.c_str());
setup();
}
}
diff --git a/src/patrix/mqtt.cpp b/src/patrix/mqtt.cpp
index 38c36c8..4ce732b 100644
--- a/src/patrix/mqtt.cpp
+++ b/src/patrix/mqtt.cpp
@@ -24,20 +24,20 @@ void mqttCallback(char *topic, const uint8_t *payload, unsigned int length) {
length = min(sizeof message - 1, length);
memcpy(message, payload, length);
*(message + length) = 0;
- Log.printf("MQTT received: topic=\"%s\", message=\"%s\"\n", topic, message);
+ Log.info(R"(MQTT received: topic="%s", message="%s")", topic, message);
if (strcmp(message, "help") == 0) {
- Log.printf("HELP\n");
- Log.printf(" %s\n", "help");
- Log.printf(" %s\n", "info");
- Log.printf(" %s\n", "reboot");
+ Log.info("HELP");
+ Log.info(" %s", "help");
+ Log.info(" %s", "info");
+ Log.info(" %s", "reboot");
} else if (strcmp(message, "info") == 0) {
- Log.printf("INFO\n");
- Log.printf(" %-10s %s\n", "SSID:", WiFi.SSID().c_str());
- Log.printf(" %-10s %s\n", "IP:", WiFi.localIP().toString().c_str());
- Log.printf(" %-10s %d\n", "RSSI:", WiFi.RSSI());
- Log.printf(" %-10s %s\n", "uptime:", uptimeString().c_str());
+ Log.info("INFO");
+ Log.info(" %-10s %s", "SSID:", WiFi.SSID().c_str());
+ Log.info(" %-10s %s", "IP:", WiFi.localIP().toString().c_str());
+ Log.info(" %-10s %d", "RSSI:", WiFi.RSSI());
+ Log.info(" %-10s %s", "uptime:", uptimeString().c_str());
} else if (strcmp(message, "reboot") == 0) {
- Log.printf("rebooting...\n");
+ Log.info("rebooting...");
delay(500);
mqtt.disconnect();
delay(500);
@@ -50,13 +50,13 @@ void mqttLoop() {
mqtt.setServer("10.0.0.50", 1883);
if (mqtt.connect(HOSTNAME, logTopic.c_str(), 0, false, "disconnected\n")) {
yield();
- mqttPublish(logTopic.c_str(), "connected\n");
+ mqttPublish(logTopic, "connected\n");
mqtt.setCallback(mqttCallback);
mqtt.subscribe(cmdTopic.c_str());
- Log.printf("MQTT connected as \"%s\"\n", HOSTNAME);
+ Log.info("MQTT connected as \"%s\".", HOSTNAME);
mqttFailureMillis = 0;
} else {
- Log.printf("Failed to connect MQTT.\n");
+ Log.error("Failed to connect MQTT.");
mqttFailureMillis = max(1UL, millis());
}
}
@@ -100,5 +100,4 @@ void mqttPublishValue(const String& name, const String& value, const char *unit)
void mqttPublish(const String& topic, const String& payload) {
mqtt.publish(topic.c_str(), payload.c_str());
- yield();
}
diff --git a/src/patrix/mqtt.h b/src/patrix/mqtt.h
index b179ba8..407c919 100644
--- a/src/patrix/mqtt.h
+++ b/src/patrix/mqtt.h
@@ -5,6 +5,23 @@
#include "PubSubClient.h"
#include "wifi.h"
+#define UNIT_PERCENT "UNIT_PERCENT"
+#define UNIT_TEMPERATURE_C "TEMPERATURE_C"
+#define UNIT_PRESSURE_HPA "PRESSURE_HPA"
+#define UNIT_HUMIDITY_RELATIVE_PERCENT "HUMIDITY_RELATIVE_PERCENT"
+#define UNIT_HUMIDITY_ABSOLUTE_GM3 "HUMIDITY_ABSOLUTE_GM3"
+#define UNIT_IAQ "IAQ"
+#define UNIT_IAQ_ACCURACY "IAQ_ACCURACY"
+#define UNIT_IAQ_CO2_EQUIVALENT "IAQ_CO2_EQUIVALENT"
+#define UNIT_IAQ_VOC_EQUIVALENT "IAQ_VOC_EQUIVALENT"
+#define UNIT_ILLUMINANCE_LUX "ILLUMINANCE_LUX"
+#define UNIT_RESISTANCE_OHMS "RESISTANCE_OHMS"
+#define UNIT_ALTITUDE_M "ALTITUDE_M"
+#define UNIT_POWER_W "POWER_W"
+#define UNIT_POWER_KW "POWER_KW"
+#define UNIT_ENERGY_WH "ENERGY_WH"
+#define UNIT_ENERGY_KWH "ENERGY_KWH"
+
extern const String logTopic;
extern const String cmdTopic;
@@ -44,7 +61,9 @@ class LogClass final : public Stream {
public:
explicit LogClass(PubSubClient& mqttClient) : mqtt(mqttClient) {
+ delay(500);
Serial.begin(115200);
+ Serial.print("\n\n\n");
}
size_t write(const uint8_t data) override {
@@ -82,6 +101,72 @@ public:
int peek() override {
return -1;
}
+
+ enum Level {
+ OFF, ERROR, WARN, INFO, DEBUG,
+ };
+
+ void debug(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ log(DEBUG, format, args);
+ va_end(args);
+ }
+
+ void info(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ log(INFO, format, args);
+ va_end(args);
+ }
+
+ void warn(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ log(WARN, format, args);
+ va_end(args);
+ }
+
+ void error(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ log(ERROR, format, args);
+ va_end(args);
+ }
+
+ Level level = DEBUG;
+
+ void log(const Level level, const char *format, const va_list args) {
+ if (this->level < level) {
+ return;
+ }
+
+ print(getTimeString().c_str());
+
+ const char *levelName;
+ switch (level) {
+ case ERROR:
+ levelName = "ERROR";
+ break;
+ case WARN:
+ levelName = "WARN";
+ break;
+ case INFO:
+ levelName = "INFO";
+ break;
+ case DEBUG:
+ levelName = "DEBUG";
+ break;
+ default:
+ levelName = " ??? ";
+ }
+ printf(" [%-5s] ", levelName);
+
+ char buffer[256];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ println(buffer);
+ }
+
};
extern LogClass Log;
diff --git a/src/patrix/tsl2561.h b/src/patrix/tsl2561.h
index 34bfafa..c3ec09c 100644
--- a/src/patrix/tsl2561.h
+++ b/src/patrix/tsl2561.h
@@ -21,9 +21,9 @@ public:
void setup() {
if (tsl.begin()) {
- Log.printf("TSL2561 \"%s\": Initialized.\n", name.c_str());
+ Log.info("TSL2561 \"%s\": Initialized.", name.c_str());
} else {
- Log.printf("TSL2561 \"%s\": Failed to initialize.\n", name.c_str());
+ Log.error("TSL2561 \"%s\": Failed to initialize.", name.c_str());
}
tsl.enableAutoRange(true);
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);
@@ -45,7 +45,7 @@ private:
tsl.getLuminosity(&broadband, &ir);
const auto illuminance = tsl.calculateLux(broadband, ir);
if (illuminance == 65536) {
- Log.printf("TSL2561 \"%s\": Failed to read.\n", name.c_str());
+ Log.error("TSL2561 \"%s\": Failed to read.", name.c_str());
setup();
} else {
mqttPublishValue(name + "/illuminance", illuminance, "ILLUMINANCE_LUX");
diff --git a/src/patrix/wifi.cpp b/src/patrix/wifi.cpp
index e92e7ef..cd553e4 100644
--- a/src/patrix/wifi.cpp
+++ b/src/patrix/wifi.cpp
@@ -23,7 +23,7 @@ void wifiConnect() {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
configTime(TZ_Europe_Berlin, NTP_SERVER);
ArduinoOTA.onStart([]() {
- Log.printf("OTA begin...\n");
+ Log.info("OTA begin...");
});
ArduinoOTA.onError([](const ota_error_t error) {
const char *name;
@@ -42,10 +42,10 @@ void wifiConnect() {
name = "[???]";
break;
}
- Log.printf("OTA error #%d: %s\n", error, name);
+ Log.error("OTA error #%d: %s", error, name);
});
ArduinoOTA.onEnd([]() {
- Log.printf("OTA success\n");
+ Log.info("OTA success");
});
ArduinoOTA.begin();
yield();
@@ -93,18 +93,18 @@ void timeLoop() {
if (now > 1700000000) {
timeSetSince = now;
lastHour = nowHour;
- Log.printf("Got time: %s\n", getTimeString().c_str());
- Log.printf("Delaying boot for %d seconds.\n", BOOT_DELAY_SEC);
+ Log.info("Got time: %s.", getTimeString().c_str());
+ Log.info("Delaying boot for %d seconds.", BOOT_DELAY_SEC);
}
} else {
if (lastHour != nowHour) {
lastHour = nowHour;
- Log.printf("%s\n", getTimeString().c_str());
+ Log.info("Alive.");
}
if (!bootDelayOver) {
bootDelayOver = time(nullptr) - timeSetSince > BOOT_DELAY_SEC;
if (bootDelayOver) {
- Log.printf("Boot delay complete.\n");
+ Log.info("Boot delay complete.");
}
}
}
@@ -115,7 +115,7 @@ void wifiLoop() {
if (WiFi.localIP() == 0UL) {
if (wifiConnected) {
wifiConnected = false;
- Log.printf("WiFi disconnected.\n");
+ Log.warn("WiFi disconnected.");
wifiConnect();
} else if (wifiConnectBeginMillis == 0 || millis() - wifiConnectBeginMillis >= WIFI_TIMEOUT_MS) {
WiFi.disconnect();
@@ -126,7 +126,7 @@ void wifiLoop() {
if (!wifiConnected) {
wifiConnected = true;
wifiConnectBeginMillis = 0;
- Log.printf("WiFi connected as \"%s\" (%s)\n", HOSTNAME, WiFi.localIP().toString().c_str());
+ Log.info("WiFi connected as \"%s\" (%s).", HOSTNAME, WiFi.localIP().toString().c_str());
}
}
uptimeLoop();