ESP32 Webserver + Sensor Readout Pflanzenvitrine

Guten Tag zusammen,
Nur kurz das Projekt überrissen:
Endziel:
async Webserver mit Temperatur und Luftfeuchtigkeitsanzeige (am besten mit Graph der letzten 24h) bei dem man die Ziel Luftfeuchte und Temperatur einstellen können soll), unabhängig davon soll ein Relaismodul eingeschaltet werden je nachdem ob Geheizt oder die Luftfeuchte erhöht werden soll.

Aktueller Stand:
DHT22 Sensoren sind in einem Array angelegt und lesen Temperatur und Luftfeuchte aus
Webserver läuft und zeigt die aktuellen Werte (bei Aktualisierung) an.

Probleme:

  1. Wie kann ich am effizientesten alle 1-2 Minuten die aktuellen Werte auslesen und entscheiden ob geheizt/Befeuchtet werden soll ohne das ich die Website jedes mal neu laden muss.
  • Direkte Frage hierzu: kann ich in meinen loop auch einfach die Routine (Funktion) zum auslesen der Daten und abspeichern reinschreiben oder kann dies zu Problemen mit server.handleClient(); kommen?

Die DHT Sensoren zeigen eine deutlich zu hohe Temperatur an (2-3 Grad) wie kann ich diese am besten kalibrieren oder soll ich nur einen Offset von den 2-3 Grad einprogrammieren?

Anbei der Absolut schlechte Code von mir (viel zusammenkopiert).

#include <DHT.h>;
#include <WiFiClient.h>
#include <WebServer.h>

#define DHTPIN1 26 
#define DHTPIN2 25
#define DHTPIN3 27
#define DHTPIN4 14

DHT dht[] = {
  {DHTPIN1, DHT22},
  {DHTPIN2, DHT22},
  {DHTPIN3, DHT22},
  {DHTPIN4, DHT22},
};

// Replace with your network credentials
const char* ssid = "--------------";
const char* password = "-----------";


WebServer server(80);
String header;

float humidity[4];
float temperature[4];


const char INDEX_HTML_Start[] =
"<!DOCTYPE HTML>"
"<html>"
"<head>"
"<meta name = \"viewport\" content = \"width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0\">"
"<title>Pflanzenvitrine</title>"
"<style>"
"\"body { background-color: #000000; font-family: Arial, Helvetica, Sans-Serif; Color: #FFFFFF; }\""
"</style>"
"</head>"
"<body>"
;
const char INDEX_HTML_End[] =
"</body>"
"</html>";


void handleRoot() {
  String test = String(INDEX_HTML_Start) + "<br>"
  + String("<h1>Temperatur:</h1>") + "<br>"
  + String("Temperatur1: ") + String(temperature[0]) + "<br>"
  + String("Temperatur2: ") + String(temperature[1]) + "<br>"
  + String("Temperatur3: ") + String(temperature[2]) + "<br>"
  + String("Temperatur4: ") + String(temperature[3]) + "<br>"
  + String("<h1>Luftfeuchte:</h1>") + "<br>"
  + String("Luftfeuchtigkeit1: ") + String(humidity[0]) + "<br>"
  + String("Luftfeuchtigkeit2: ") + String(humidity[1]) + "<br>"
  + String("Luftfeuchtigkeit3: ") + String(humidity[2]) + "<br>"
  + String("Luftfeuchtigkeit4: ") + String(humidity[3]) + "<br>"
  + String(INDEX_HTML_End);
  server.send(200, "text/html", test);
}

void sensorRead() {

    for (int i = 0; i < 4; i++) {
    temperature[i] = dht[i].readTemperature();
    humidity[i] = dht[i].readHumidity();
  }
}



void setup()
{
  Serial.begin(115200);

  // Per WLAN mit dem Netzwerk verbinden
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(3000);
    Serial.print(".");
  }

  // Sensor Begin für DHT22 Sensoren
  
  for (auto& sensor : dht) {
    sensor.begin();
  }

  // Die IP vom Webserver auf dem seriellen Monitor ausgeben
  Serial.println("");
  Serial.println("WLAN verbunden.");
  Serial.println("IP Adresse: ");
  Serial.println(WiFi.localIP());
  server.begin();

  server.on("/", handleRoot);

}

void loop()
{
  server.handleClient();

//  sensorRead();

//  for (int i = 0; i < 4; i++) {
//    Serial.print(F("Temperature "));
//    Serial.print(i);
//    Serial.print("      ");
//    Serial.println(temperature[i]);
//    Serial.print(F("Humidity "));
//    Serial.print(i);
//    Serial.print("      ");
//    Serial.println(humidity[i]);
}

Vielen Dank für eure Hilfe!

mach dir eine Funktion nach dem muster blink without delay und rufe alle 2 Minuten deine Temp-Sensoren auf und schalte dann

pseudocode

void doMessen()
{
  static uint32_t previousMillis = 0;
  if (millis()-previousMillis > 2*60*1000UL)
  {
    previousMillis = millis();
    // hier dann messen und schalten
  }
}

ps: mit Charts haben wir im Forum vor etwa einem Jahr rumgespielt.
Meinen Versuch findest du hier:
https://forum.arduino.cc/t/gelost-fetch-api-mit-json/683727/21

es lohnt sich den ganzen Thread zu lesen, es gibt mehrere Ansätze / Ideen.

Später habe ich mal den Sketch auf ESP32 und ESP8266 angepasset:

/*
// shows a chart.js on a ESP web interface 
// speichert die Werte in einem Ringbuffer
  2021-08-30  esp32 or esp8266
  2020-11-20  https://forum.arduino.cc/index.php?topic=714796.15
*/

#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>                // OTA Upload via ArduinoIDE
WebServer server(80);
#else
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>          // for the webserver
#include <ESP8266mDNS.h>               // Bonjour/multicast DNS, finds the device on network by name
#include <ArduinoOTA.h>                // OTA Upload via ArduinoIDE
ESP8266WebServer server(80);           // an instance for the webserver
#endif

//#include <credentials.h>                 // that's just for my unknown credentials

#ifndef STASSID
#define STASSID "your-ssid"              // set your SSID
#define STAPSK  "your-password"          // set your wifi password
#endif

const char* ssid = STASSID;              // up to 32 characters
const char* password = STAPSK;           // at least 8 characters but not more than 64

const size_t buffersize = 21;            // values to be stored in the ring buffer
size_t writePos = 0;                     // the next write position for the ring buffer

struct Value
{
  float out;                             // inside temperature
  float in;                              // outside temperature
  //timestamp würde noch gut passen
} value[buffersize];

const char WEBSITE[] PROGMEM = R"(<!DOCTYPE HTML>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Temperatur</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
    <script>
      window.addEventListener('DOMContentLoaded', () => {
        const xachse = ['','10:00','','','','','','','','','10:01','','','','','','','','','10:02',''];
        var value = [];
        const config = {
          type: 'line',
          data: {
            labels: xachse,
            datasets: [{
              label: 'Aussen',
              borderColor: "blue",
              backgroundColor:"black",
              data:value,
              fill:false,
            },{
              label: 'Innen',
              borderColor: "red",
              backgroundColor:"black",
              data:value,
              fill:false,
            }]        
          },
          options:{
            animation: {
               duration: 1000 // general animation time
            },
            scales:{
              xAxes:[{
                display: true,
                scaleLabel: {
                  display: true,
                  labelString: 'Uhrzeit'
                }
              }],
              yAxes: [{
                display: true,
                scaleLabel: {
                  display: true,
                  labelString: 'Temperatur °C'
                }
              }]
            }
          }    
        };
        var ctx = document.getElementById("chart").getContext('2d');
        var myChart = new Chart(ctx,config);
        async function loadData(){
          let resp = await fetch('/daten')
          let obj = await resp.json();
          config.data.datasets[0].data = obj.wert1;
          config.data.datasets[1].data = obj.wert2;
          myChart.update();    
        }
        loadData();
        setInterval(loadData, 1000)
      });
    </script>
    <style>
      div {
        width: 50em;
      }
    </style>
  </head>
  <body>
    <div>
      <canvas id="chart"> </canvas>      
    </div>
  </body>
</html>)";

size_t getIndex(size_t startPosition, size_t sequence) {
  size_t index;
  index = (startPosition + sequence) % buffersize;
  //Serial.print(startPosition); Serial.print(" + "); Serial.print(sequence); Serial.print("-->"); Serial.println(index);
  return index;
}

void buildJson() {
  size_t readPos = writePos;
  String temp = "{\"wert1\":[";
  for (size_t i = 0; i <= buffersize - 1; i++) {
    if (temp != "{\"wert1\":[") temp += ",";
    temp += value[getIndex(readPos, i)].out;
  }
  temp += "],\"wert2\":[";
  for (size_t i = 0; i <= buffersize - 1; i++) {
    if (i > 0) temp += ",";
    temp += value[getIndex(readPos, i)].in;
  }
  temp += "]}";
  //Serial.print (readPos); Serial.print(" "); Serial.println(temp);
  server.send(200, "application/json", temp);
}

// this is just a demo function 
// it simulate the reading of two sensor values into the value struct

void readSensors()
{
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (currentMillis - previousMillis > 4000)           // once per second
  {
    previousMillis = currentMillis;
    //Serial.print("write into "); Serial.println(writePos);
    value[writePos].in = random(200, 270) / 10.0;
    value[writePos].out = random(100, 250) / 10.0;
    //value[writePos].out = millis()/1000;
    writePos++;
    if (writePos >= buffersize) writePos = 0;
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.hostname("esp");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print(F("\nIP address: "));
  Serial.println(WiFi.localIP());
  server.on ( "/daten", buildJson);
  server.on ( "/", []() {
    server.send_P(200, "text/html", WEBSITE);
  });
  server.begin();

  ArduinoOTASetup();
}

void loop() {
  server.handleClient();
  readSensors();
  ArduinoOTA.handle();                 // OTA Upload via ArduinoIDE
}

void ArduinoOTASetup()
{
  //IDE OTA
#ifdef ARDUINO_ARCH_ESP32
  ArduinoOTA.setHostname("esp");       // give a name to your ESP for the Arduino IDE - not needed for ESP8266
#endif
  ArduinoOTA.begin();                  // OTA Upload via ArduinoIDE https://arduino-esp8266.readthedocs.io/en/latest/ota_updates/readme.html
  // OTA Messages
  ArduinoOTA.onStart([]() {
    Serial.println(F("OTA Start"));
  });
}

Hallo,
wozu hast Du 4 Sensoren ? Die Sensoren messen schon mal 1-2 Grad zuviel, das hab ich auch schon bemerkt. Eventuell siehst Du Dir auch mal den BME280 an. Ich habe das als Offset abgezogen. Ich denke für die gängigen sinnvollen Messbereiche 15-30 Grad wird das reichen. ob es dann 8 oder 10 sind wird egal sein.

Du solltest Dich noch mal auf der Seite von Fips umsehen , da wirst Du sicher was finden. Ich denke Du solltest eventuell mit zwei Webseiten oder drei Webseiten arbeiten. Eine zur Eingabe von Sollwerten, Temperatur , Luftfeuchte , Zeiten , eine Zweite zur Anzeige von Soll und Istwerten und eine weitere Seite zur Anzeige der charts. Dazu wirst dich ein Wenig mit Java-Skript beschäftigen müssen.
Verwende Fuktionen die Teilaufgaben lösen damit das übersichtlich bleibt.
Heinz