From 674fafc318c78728fdb42de499772568257eea6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Mon, 17 Feb 2025 16:40:48 +0100 Subject: [PATCH] Fermenter + Log.debug/info/warn/error + removed yield() from mqtt + String name FIX --- data/http/index.html | 194 +++++++++++++++++++++++++++++++++++++ platformio.ini | 30 ++++-- src/Fermenter.cpp | 110 +++++++++++++++++++-- src/patrix/DS18B20.h | 97 ++++++++++++------- src/patrix/DS18B20Sensor.h | 51 ++++++++++ src/patrix/IValueSensor.h | 14 +++ src/patrix/PIDController.h | 70 +++++++++++++ src/patrix/PWMOutput.h | 48 +++++++++ src/patrix/Patrix.cpp | 9 +- src/patrix/bme680.h | 10 +- src/patrix/mqtt.cpp | 29 +++--- src/patrix/mqtt.h | 85 ++++++++++++++++ src/patrix/tsl2561.h | 6 +- src/patrix/wifi.cpp | 18 ++-- 14 files changed, 682 insertions(+), 89 deletions(-) create mode 100644 data/http/index.html create mode 100644 src/patrix/DS18B20Sensor.h create mode 100644 src/patrix/IValueSensor.h create mode 100644 src/patrix/PIDController.h create mode 100644 src/patrix/PWMOutput.h 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 + + + + +
+
Ist-Temperatur
+
+
+
°C
+
+
+ +
+
Ziel-Temperatur
+
+ + + + +
+
+
°C
+
+ + + + +
+
+ +
+
Heizung
+
+
+
%
+
+
+
+
W
+
+
+ + + + + \ 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();