From 7c9ea384dd789c67a52f2eded2e22ffbcdd1e9e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Thu, 11 Apr 2024 10:14:34 +0200 Subject: [PATCH] Fermenter PID + deploy --- lib/patrix/console.cpp | 6 +-- lib/patrix/console.h | 2 + lib/patrix/log.cpp | 2 +- lib/patrix/wifi.cpp | 1 + platformio.ini | 46 +++++++++++++++++---- src/Fermenter.cpp | 91 ++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 15 deletions(-) diff --git a/lib/patrix/console.cpp b/lib/patrix/console.cpp index b8ca4e5..aee0382 100644 --- a/lib/patrix/console.cpp +++ b/lib/patrix/console.cpp @@ -10,7 +10,7 @@ char consoleBuffer[64] = ""; char *consoleBufferW = consoleBuffer; -const char * getFlashChipMode(); +const char *getFlashChipMode(); void consoleLoop() { uint8_t i = 0; @@ -118,12 +118,12 @@ void consoleHandle(const char *cmd) { } else if (strcmp(cmd, "debug") == 0) { setDebugEnabled(!isDebugEnabled()); info("DEBUG: %s", isDebugEnabled() ? "ON" : "OFF"); - } else { + } else if (!patrix_command((char *) cmd)) { info("Unknown command: %s", cmd); } } -const char * getFlashChipMode() { +const char *getFlashChipMode() { switch (ESP.getFlashChipMode()) { case FM_QIO: return "QIO"; diff --git a/lib/patrix/console.h b/lib/patrix/console.h index 59a8ed6..a7bc96b 100644 --- a/lib/patrix/console.h +++ b/lib/patrix/console.h @@ -5,4 +5,6 @@ void consoleLoop(); void consoleHandle(const char *cmd); +bool patrix_command(char *cmd); + #endif diff --git a/lib/patrix/log.cpp b/lib/patrix/log.cpp index fc4c563..92afb36 100644 --- a/lib/patrix/log.cpp +++ b/lib/patrix/log.cpp @@ -3,7 +3,7 @@ #include "mqtt.h" #include "wifi.h" -bool debugEnabled = false; +bool debugEnabled = DEBUG_LOG; void log(const char *level, const char *format, va_list vl) { char datetime[26]; diff --git a/lib/patrix/wifi.cpp b/lib/patrix/wifi.cpp index 1e93fda..526e1ac 100644 --- a/lib/patrix/wifi.cpp +++ b/lib/patrix/wifi.cpp @@ -65,6 +65,7 @@ void wifiConnect() { } void otaSetup() { + ArduinoOTA.setPassword(OTA_PASSWORD); ArduinoOTA.onStart([] { info("OTA start..."); otaLastLogStep = 0; diff --git a/platformio.ini b/platformio.ini index 6646956..490fe28 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,17 +1,47 @@ -[env:Fermenter] +[COMMON] platform = espressif32 board = esp32dev framework = arduino -;upload_port = /dev/ttyUSB0 -;upload_speed = 921600 -upload_protocol = espota -upload_port = 10.42.0.66 -upload_flags = --auth=OtaAuthPatrixFermenter +usb_port = /dev/ttyUSB0 +usb_speed = 921600 +ota_protocol = espota monitor_port = /dev/ttyUSB0 monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = -D HOSTNAME=\"Fermenter\" -D WIFI_SSID=\"HappyNet\" -D WIFI_PKEY=\"1Grausame!Sackratte7\" -D OTA_PASSWORD=\"OtaAuthPatrixFermenter\" -D BOOT_DELAY=false +WIFI_SSID = HappyNet +WIFI_PKEY = 1Grausame!Sackratte7 lib_deps = milesburton/DallasTemperature knolleary/PubSubClient bblanchon/ArduinoJson - paulstoffregen/OneWire \ No newline at end of file + paulstoffregen/OneWire + https://github.com/phassel/ArduPID/ + +[env:DEV] +upload_port = 10.42.0.66 +upload_flags = --auth=OtaAuthPatrixDEV +upload_protocol = ${COMMON.ota_protocol} +;upload_port = ${COMMON.usb_port} +;upload_speed = ${COMMON.usb_speed} +platform = ${COMMON.platform} +board = ${COMMON.board} +framework = ${COMMON.framework} +monitor_port = ${COMMON.monitor_port} +monitor_speed = ${COMMON.monitor_speed} +monitor_filters = ${COMMON.monitor_filters} +lib_deps = ${COMMON.lib_deps} +build_flags = -D HOSTNAME=\"DEV\" -D WIFI_SSID=\"${COMMON.WIFI_SSID}\" -D WIFI_PKEY=\"${COMMON.WIFI_PKEY}\" -D OTA_PASSWORD=\"OtaAuthPatrixDEV\" -D BOOT_DELAY=true -D DEBUG_LOG=true + +[env:Fermenter] +upload_port = 10.0.0.138 +upload_flags = --auth=OtaAuthPatrixFermenter +upload_protocol = ${COMMON.ota_protocol} +;upload_port = ${COMMON.usb_port} +;upload_speed = ${COMMON.usb_speed} +platform = ${COMMON.platform} +board = ${COMMON.board} +framework = ${COMMON.framework} +monitor_port = ${COMMON.monitor_port} +monitor_speed = ${COMMON.monitor_speed} +monitor_filters = ${COMMON.monitor_filters} +lib_deps = ${COMMON.lib_deps} +build_flags = -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 diff --git a/src/Fermenter.cpp b/src/Fermenter.cpp index 1358e26..3c4506c 100644 --- a/src/Fermenter.cpp +++ b/src/Fermenter.cpp @@ -1,16 +1,101 @@ #include #include "sensors/Dallas.h" #include "sensors/DallasSensor.h" +#include "ArduPID.h" +#include + +#define CONTROL_GPIO 4 +#define CONTROL_PWM_BITS 10 Dallas dallas(2); -DallasSensor temperatur(dallas, 0xAA0121125E4A7528, "temperatur", 0.5, 5, 60); // TODO wrong address +DallasSensor sensor(dallas, 0xAA0121125E4A7528, "sensor", 0.5, 5, 60); // TODO wrong address + +ArduPID pid; + +double temperatureTarget = 31; + +double temperatureCurrent = NAN; + +double temperatureMaxOvershoot = 5; + +double heaterPWM = 0; + +double proportional = 1; + +double integral = 0; + +double derivative = 0; + +bool setDouble(const char *name, double *destinationPtr, double min, double max); void patrixSetup() { + analogWriteResolution(CONTROL_PWM_BITS); + pid.begin(&temperatureCurrent, &heaterPWM, &temperatureTarget, proportional, integral, derivative); + pid.setOutputLimits(0, pow(10, CONTROL_PWM_BITS) - 1); + pid.start(); } + void patrixLoop() { dallas.loop(); - temperatur.loop(); -} \ No newline at end of file + sensor.loop(); + + temperatureCurrent = sensor.getLastValue(); + if (!isnan(temperatureCurrent)) { + pid.compute(); + } else { + heaterPWM = 0; + } + + const bool emergencyCutOff = heaterPWM > 0 && temperatureCurrent > temperatureTarget + temperatureMaxOvershoot; + if (emergencyCutOff) { + heaterPWM = 0; + } + analogWrite(CONTROL_GPIO, (int) round(heaterPWM)); + + static unsigned long lastDebug = 0; + unsigned long now = millis(); + if (now - lastDebug >= 1000) { + lastDebug = now; + debug("p: %f | i: %f | d: %f | current: %4.1f | target: %4.1f | pwm: %3d%%", proportional, integral, derivative, temperatureCurrent, temperatureTarget, heaterPWM); + if (emergencyCutOff) { + error("[EMERGENCY CUTOFF] temperatureCurrent (=%4.1f) > temperatureTarget + %4.1f (=%4.1f) [EMERGENCY CUTOFF]", temperatureCurrent, temperatureMaxOvershoot, temperatureTarget); + } + } +} + +bool patrix_command(char *cmd) { + const char *first = strtok(cmd, " "); + if (strcmp(first, "target") == 0) { + return setDouble("target", &temperatureTarget, -10, 80); + } else if (strcmp(first, "p") == 0) { + return setDouble("proportional", &proportional, NAN, NAN); + } else if (strcmp(first, "i") == 0) { + return setDouble("integral", &integral, NAN, NAN); + } else if (strcmp(first, "p") == 0) { + return setDouble("derivative", &derivative, NAN, NAN); + } + return false; +} + +bool setDouble(const char *name, double *destinationPtr, double min, double max) { + const char *valueStr = strtok(nullptr, nullptr); + if (valueStr == nullptr) { + error("Missing value for \"%s\"", name); + 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; + } + *destinationPtr = value; + info("Value for \"%s\" set to: %f", name, value); + return true; +}