Ich wollte keine 150€ für einen Controller für meine Grow-LED kaufen. Die Lampe war teuer genug.
So dachte ich, ich hab doch noch einiges hier herum liegen.
Wenn mir jemand sagen könnte, wie ich die kyptischen Zeichen aus den WebServer und vom Display weg bekomme, wäre ich sehr dankbar.
Hier der Code, für Leutz die es nachbauen wollen:
#include "WiFi.h"
#include <WebServer.h>
#include <EEPROM.h>
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#include "DHT.h"
// WiFi credentials
const char* ssid = "SSID";
const char* password = "Passwort";
// WebServer
WebServer server(80);
// Den benötigten Sensor definieren, die ander auskommentieren
// #define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT21 // DHT 21 (AM2301)
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// DHT Sensor
#define DHTPin 4
DHT dht(DHTPin, DHTTYPE);
float Temperature;
float Humidity;
float leafTemperatureOffset = 0.0; // Blatttemperatur-Offset in °C
float VPD = 0.0; // VPD-Wert
// I2C Einstellungen für PCF8591
#define SDA_PIN 5
#define SCL_PIN 6
#define PCF8591_I2C_ADDRESS 0x48
int analogOutput = 0;
float analogPercent = 0;
// EEPROM Einstellungen
#define EEPROM_SIZE 1
// Display Einstellungen
#define TFT_BL 14
Arduino_DataBus *bus = new Arduino_ESP32SPI(11 /* DC */, 10 /* CS */, 12 /* SCK */, 13 /* MOSI */, GFX_NOT_DEFINED /* MISO */);
Arduino_GFX *gfx = new Arduino_ST7789(bus, 1 /* RST */, 1 /* rotation */, true /* IPS */, 170 /* width */, 320 /* height */, 35 /* col offset 1 */, 0 /* row offset 1 */, 35 /* col offset 2 */, 0 /* row offset 2 */);
// WebServer Hauptseite
void handleRoot() {
String html = "<html><body>";
html += "<h1>ESP32-GrowLED-Controller</h1>";
html += "<br><br>";
// Analoger Ausgang
html += "<h2>Lichtintensität: <span id=\"analogValue\">" + String(analogPercent) + "</span>%</h2>";
html += "<input type=\"range\" id=\"analog\" min=\"0\" max=\"100\" step=\"1\" value=\"" + String(analogPercent) + "\" oninput=\"updateAnalog(this.value)\" style=\"width: 100%;\">";
// Schaltflächen für Änderungen mit vergrößertem Abstand
html += "<div style=\"margin-top: 20px; display: flex; justify-content: center; gap: 60px;\">";
html += "<button onclick=\"adjustAnalog(-0.1)\">-0.1%</button>";
html += "<button onclick=\"adjustAnalog(0.1)\">+0.1%</button>";
html += "</div>";
html += "<div style=\"margin-top: 20px; display: flex; justify-content: center; gap: 60px;\">";
html += "<button onclick=\"adjustAnalog(-1)\">-1%</button>";
html += "<button onclick=\"adjustAnalog(1)\">+1%</button>";
html += "</div>";
html += "<script>";
// Funktion zum direkten Setzen des Wertes über den Schieberegler
html += "function updateAnalog(value) {";
html += " fetch('/setanalog', {";
html += " method: 'POST',";
html += " headers: { 'Content-Type': 'application/x-www-form-urlencoded' },";
html += " body: 'analog=' + value";
html += " });";
html += "}";
// Funktion zur Anpassung des Analogwerts
html += "function adjustAnalog(delta) {";
html += " let currentValue = parseFloat(document.getElementById('analogValue').innerText);";
html += " let newValue = Math.min(100, Math.max(0, currentValue + delta));"; // Begrenzung zwischen 0% und 100%
html += " document.getElementById('analog').value = newValue;";
html += " updateAnalog(newValue);"; // Wert setzen
html += "}";
// Funktion zum Abrufen und Aktualisieren der Werte
html += "function fetchValues() {";
html += " fetch('/getvalues').then(response => response.json()).then(data => {";
html += " document.getElementById('analogValue').innerText = data.analogPercent;";
html += " document.getElementById('temperature').innerText = data.temperature;";
html += " document.getElementById('humidity').innerText = data.humidity;";
html += " });";
html += "}";
html += "setInterval(fetchValues, 1000);"; // Aktualisiert die Werte alle 1 Sekunde
html += "</script>";
html += "<h2>Temperatur: <span id=\"temperature\">" + String(Temperature) + "</span> °C</h2>";
html += "<h2>Luftfeuchtigkeit: <span id=\"humidity\">" + String(Humidity) + "</span> % RLF</h2>";
html += "<h2>VPD: <span id=\"vpd\">" + String(VPD, 2) + "</span> kPa</h2>";
// Dropdown-Menü für Blatttemperatur-Offset
html += "<h2>Blatttemperatur gegenüber Raumtemperatur:</h2>";
html += "<select id=\"leafTemp\" onchange=\"updateLeafTemp(this.value)\">";
for (float i = -5.0; i <= 5.0; i += 0.5) { // Werte von -5.0 bis 5.0 in 0.5er-Schritten
html += "<option value=\"" + String(i, 1) + "\"";
if (i == leafTemperatureOffset) {
html += " selected"; // Aktuellen Offset vorauswählen
}
html += ">" + String(i, 1) + " °C</option>";
}
html += "</select>";
html += "<script>";
html += "function updateAnalog(value) {";
html += " fetch('/setanalog', {";
html += " method: 'POST',";
html += " headers: { 'Content-Type': 'application/x-www-form-urlencoded' },";
html += " body: 'analog=' + value";
html += " });";
html += "}";
html += "function updateLeafTemp(value) {";
html += " fetch('/setLeafTemp', {";
html += " method: 'POST',";
html += " headers: { 'Content-Type': 'application/x-www-form-urlencoded' },";
html += " body: 'leafTempOffset=' + value";
html += " });";
html += "}";
html += "function fetchValues() {";
html += " fetch('/getvalues').then(response => response.json()).then(data => {";
html += " document.getElementById('analogValue').innerText = data.analogPercent;";
html += " document.getElementById('temperature').innerText = data.temperature;";
html += " document.getElementById('humidity').innerText = data.humidity;";
html += " document.getElementById('vpd').innerText = data.vpd;";
html += " });";
html += "}";
html += "setInterval(fetchValues, 1000);";
html += "</script>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// Funktion für den analogen Ausgang
void handleSetAnalog() {
if (server.hasArg("analog")) {
analogPercent = server.arg("analog").toFloat(); // Prozentwert als Float
analogOutput = round(analogPercent * 2.55); // Umrechnung in den Bereich 0–255 (1 % = 2.55 Schritte)
// Senden der DAC Daten zum PCF8591
Wire.beginTransmission(PCF8591_I2C_ADDRESS);
Wire.write(0x40);
Wire.write(analogOutput);
Wire.endTransmission();
// Speichern des Werts im EEPROM
EEPROM.write(0, analogOutput);
EEPROM.commit();
// Aktualisiere Anzeige auf dem Display
gfx->fillScreen(BLACK);
gfx->setCursor(5, 10);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Lichtintesität: ");
gfx->print(analogPercent);
gfx->println("%");
gfx->setCursor(5, 30);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Temperatur: ");
gfx->print(Temperature);
gfx->println("°C");
gfx->setCursor(5, 50);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Luftfeuchtigkeit: ");
gfx->print(Humidity);
gfx->println("%");
}
server.sendHeader("Location", "/");
server.send(303);
}
void updateDisplay() {
// Display aktualisieren
gfx->fillScreen(BLACK);
gfx->setCursor(5, 10);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Lichtintensität: ");
gfx->print(analogPercent);
gfx->println("%");
gfx->setCursor(5, 30);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Temperatur: ");
gfx->print(Temperature);
gfx->println("°C");
gfx->setCursor(5, 50);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
gfx->print("Luftfeuchtigkeit: ");
gfx->print(Humidity);
gfx->println("%");
gfx->setCursor(5, 70);
gfx->setTextColor(WHITE);
gfx->setTextSize(2 /* x scale */, 2 /* y scale */, 1 /* pixel_margin */);
VPD = calculateVPD(Temperature, Humidity, leafTemperatureOffset);
gfx->print("VPD: ");
gfx->print(VPD, 4);
gfx->println(" kPa");
}
void handleGetValues() {
String json = "{";
json += "\"analogPercent\":" + String(analogPercent) + ",";
json += "\"temperature\":" + String(Temperature) + ",";
json += "\"humidity\":" + String(Humidity) + ",";
json += "\"vpd\":" + String(VPD, 2); // VPD hinzufügen
json += "}";
server.send(200, "application/json", json);
}
// Berechnung des VPD
float calculateVPD(float temperature, float humidity, float leafTempOffset) {
float leafTemperature = temperature + leafTempOffset - 1.4;
// Sättigungsdampfdruck berechnen (in kPa)
float saturationVaporPressure = 0.6108 * exp((17.27 * leafTemperature) / (leafTemperature + 237.3));
// Tatsächlicher Dampfdruck berechnen (in kPa)
float actualVaporPressure = (humidity / 100.0) * saturationVaporPressure;
// VPD ist die Differenz zwischen Sättigungsdampfdruck und tatsächlichem Dampfdruck
float vpd = saturationVaporPressure - actualVaporPressure;
// Negative Werte verhindern
return vpd > 0 ? vpd : 0.0;
}
void setup() {
Serial.begin(115200);
// Initialisierung des DHT22
dht.begin();
// Initialisierung EEPROM
EEPROM.begin(EEPROM_SIZE);
analogOutput = EEPROM.read(0);
analogPercent = map(analogOutput, 0, 255, 0, 100);
// Initialisierung der I2C-Pins
Wire.begin(SDA_PIN, SCL_PIN);
// Schreibe gespeicherte DAC-Daten in den PCF8591
Wire.beginTransmission(PCF8591_I2C_ADDRESS);
Wire.write(0x40);
Wire.write(analogOutput);
Wire.endTransmission();
// Mit W-LAN verbinden
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Verbinde mit W-LAN...");
}
Serial.println("Verbunden mit W-LAN. IP Addresse: ");
Serial.println(WiFi.localIP());
// Initialisierung des Displays
gfx->begin();
gfx->fillScreen(BLACK);
#ifdef TFT_BL
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
#endif
updateDisplay();
// Webserver-Endpunkte konfigurieren
server.on("/", handleRoot); // **Hier wird die Hauptseite registriert**
server.on("/setanalog", HTTP_POST, handleSetAnalog);
server.on("/getvalues", HTTP_GET, handleGetValues);
server.on("/setLeafTemp", HTTP_POST, []() {
if (server.hasArg("leafTempOffset")) {
leafTemperatureOffset = server.arg("leafTempOffset").toFloat();
// VPD neu berechnen
VPD = calculateVPD(Temperature, Humidity, leafTemperatureOffset);
Serial.println("Neuer Blatttemperatur-Offset: " + String(leafTemperatureOffset));
Serial.println("Aktualisierter VPD: " + String(VPD));
// Weiterleitung zur Hauptseite
server.sendHeader("Location", "/");
server.send(303);
} else {
server.send(400, "text/plain", "Fehler: Kein Offset angegeben.");
}
});
// Webserver starten
server.begin();
}
void loop() {
server.handleClient(); // WebServer-Anfragen verarbeiten
// Sensorwerte regelmäßig aktualisieren
static unsigned long lastUpdate = 0;
unsigned long now = millis();
if (now - lastUpdate >= 2000) { // Alle 2 Sekunden aktualisieren
lastUpdate = now;
// DHT22 auslesen
float temp = dht.readTemperature();
float hum = dht.readHumidity();
// Nur gültige Werte übernehmen
if (!isnan(temp) && !isnan(hum)) {
Temperature = temp;
Humidity = hum;
// VPD berechnen
VPD = calculateVPD(Temperature, Humidity, leafTemperatureOffset);
// Display aktualisieren
updateDisplay();
} else {
Serial.println("Fehler beim Lesen des DHT22-Sensors.");
}
}
}
Bibliotheken:
GFX Library for Arduino (Version 1.5.1 von moononournation
DHT kxn (Version 3.4.4) von Adafruit
WiFiManager (Version 2.0.17) von tzapu
ESP Async WebServer (Version 3.4.5) von Me-No-Dev

