Unsere Markise hat nun eine Steuerung bekommen

Wir besitzen eine funkgesteuerte Markise, die aber keine wetterabhängige Steuerung besitzt.
Darum habe ich mit dem ESP32S3 eine Steuerung bebaut, welche die Sonneneinstrahlung und den Wind misst. Daraus resultiert dann das Auf- und Zufahren der Markise. Wie das nun programmiert ist und welche Hardware ich dazu benötigt habe, ist im folgenden zu lesen.

Die Elektronik

Auf dem Schaltbild ist zu sehen, dass der ESP32S3, ein Display, und 3 Relais ansteuert. Des weiteren wird über den I2C-Bus der Lichtsensor (BH1750) und der Temperatursensor BMP280 angesteuert.
Das Anemometer

Anemometer wird über eine Steckerleiste angeschlossen, wie auch die anderen Sensoren und die Taster der Markisenfunksteuerung. Ein Netzteil komplettiert die Schaltung.
Die Relais werden über jeweils einen Transistor BC337 angesteuert, um genügend Leistung für das Relais zu haben. Die Relais sind mit den Tastern in der Funksteuerung verbunden und simulieren so  den jeweiligen Tastendruck (AUF, ZU, STOPP), da ich keinen Zugang zum Funkprotokoll habe.
Das Signal des Anemometers wird mit einem Operationsverstärker LM358 verstärkt. 
3 LEDs zeigen zusätzlich den Status der Markise an.

Schaltplan der Markisensteuerung

Das Gehäuse

Die Hardware habe ich in eine passende Abzweigdose eingebaut, welche mir einfache Anschlüsse der externen Komponenten wie Sensoren und Spannungsversorgung bietet.
Im Bild sind gut die verwendeten Steckerleisten für die externen Komponenten zu sehen.
Auch ist die Verdrahtung zum Funkschalter, der auf dem Deckel der Abzweigdose montiert ist, gut zu erkennen. 

Schaltbox

Die Sensoren

Für die Steuerung benötige ich mindestens 2 Werte, das sin Sonne und Wind. Beide Werte haben Einfluss auf die Regelung. In der Softwarebeschreibung gehe ich darauf weiter ein. Hier soll nur die Positionierung der beiden Sensoren gezeigt werden.
Oben sehen wir das schwarze Anemometer und weiter unten den Lichtsensor mir Temperaturfühler.

Sensoren für die Markisensteuerung

Die Software

Auf dem ESP32S3 läuft die gesamte Software, welche sich in die Steuersoftware und in das Web-Interface aufteilt. Diese Aufteilung Spiegelt sich auch in der Aufteilung auf die beiden Kerne wieder. Arduino-SW mit dem Webserver läuft auf Kern 1 und die Markisensteuerung auf Kern 0.
Die Daten werden zentral im der struct MarkiseObject gehalten, welche im EEPROM-Bereich des ESP32S3 abgelegt ist. 

				
					struct MarkiseObject
{
	uint16_t statusEEPROM_Values; // Status der Werte in der EEPROM, 0 = keine Werte, EEPROM_VALID_FLAG = Werte vorhanden	
	uint16_t sonneSollWert; // Schwellwert für Sonne
	uint16_t windSollWert; // Schwellwert für Wind	
	uint16_t windSicherheitsZeitSekunden; // Zeit, die die Markise bei starkem Wind eingefahren bleibt, um Schäden zu vermeiden in Sekunden	
	uint16_t markiseLaufzeitSekunden; // Zeit, die die Markise zum Ein- oder Ausfahren benötigt in Sekunden

	uint16_t sonneIstWert; // aktueller Wert für Sonne	
	uint16_t windIstWert; // aktueller Wert für Wind
	uint16_t markiseStatus; // 0 = eingefahren, 1 = ausgefahren, 2 = Einfahren, 3 = Ausfahren
	uint16_t markiseBleibtZuZeit; // Zeitintervall für die "bleibt zu Zeit" in Sekunden
	uint16_t counterMarkiseAufAbtastung; // Aktueller Counter Wert für die Sonne-Auf-Abtastung der Markise
	uint16_t counterMarkiseZuAbtastung; // Aktueller Counter Wert für die Sonne-Zu-Abtastung der Markise
	uint16_t counterWindZuAbtastung; // Aktueller Counter Wert für die Wind-Zu-Abtastung der Markise

	uint16_t markiseAufAbtastungSekunden; // Zeitintervall für die Auf-Abtastung der Sensoren in Sekunden
	uint16_t markiseZuAbtastungSekunden; // Zeitintervall für die Zu-Abtastung der Sensoren in Sekunden
	uint16_t windAbtastungSekunden; // Zeitintervall für die Zu-Abtastung der Windstärke in Sekunden
	uint16_t countMarkiseAufAbtastung; // Soll für die Auf-Abtastung der Lichtstärke
	uint16_t countMarkiseZuAbtastung; // Soll für die Zu-Abtastung der Lichtstärke
	uint16_t countWindZuAbtastung; // Soll für die Zu-Abtastung der Windstärke
	uint16_t automatikStatus; // Status der Automatik Aus=0, EIN=1
};
				
			

Die Task-Struktur

Die Haupttask „MarkiseTask“ steuert den Markise. „TaskMarkiseAuf“ und „TaskMarkiseZu“ übernehmen die zeitaufwendigen Funktionen.
„TaskWindSensor“ ist getrennt vom Sonnensensor, da Wind häufiger abgefragt wird.  Die anderen Tasks stellen weitere Werte zur Verfügung. „TaskDisplay“ übernimmt die wechselnde Anzeige der Werte.
„MarkiseTask“ kommuniziert mit „TaskMarkiseAuf“ und „TaskMarkiseZu“ über je ein Semaphore.

				
					TaskHandle_t MarkiseTask;
TaskHandle_t TaskMarkiseAuf;
TaskHandle_t TaskMarkiseZu;
TaskHandle_t TaskWindSensor;
TaskHandle_t TaskLichtMessen;
TaskHandle_t TaskBME280Messen;
TaskHandle_t TaskDisplay;
SemaphoreHandle_t MarkiseZuSemaphore = NULL;
SemaphoreHandle_t MarkiseAufSemaphore = NULL;

				
			

Die Reglung "MarkiseTask"

Die Regelung hat 3 State machines. 
1. Markise zu: Warten auf Sonne ohne Wind
2. Markise auf: Prüfe auf Sonne und Wind
3. Markise fährt AUF/ZU: Warten

Die Regelung erfolgt über die Soll-und IstWerte von Sonne und Wind, die in den Werten „counterMarkiseAufAbtastung“und „counterMarkiseZuAbtastung“ mit den Schwellwerten „countMarkiseAufAntastung“ und  „countMarkiseZuAbtastung“ verglichen werden.  Hierdurch werden die Zeitglieder der Regelung erstellt. 
Der Wind fließt im gleichen System mit „counterWindZuAbtastung“ und „countWindZuAbtastung“ in die Regelung mit ein.
Die Sonnenwerte werden in dieser Task verarbeitet, die Windwerte werden aber in der Wind-Task aufbereitet, da die Abtastung häufiger erfolgt als bei der Sonne.

				
					void Markise( void * pvParameters ){
  Serial.println("Starte Task Markise");
  CheckLichtSensor(&markiseObject); // Init Lichtwert
  FahreMarkiseZu(&markiseObject,true); // Nach Programmstart

  //int windStatus=KEIN_WIND;
  for(;;){
    // Markise eingefahren
    Serial.println("wdt_markise_task==="+String(wdt_markise_task));
    wdt_markise_task = WDT_TIMEOUT; //Trigger watchdog
    if(markiseObject.automatikStatus == EIN){
      if(markiseObject.markiseStatus == EINGEFAHREN){
        display_ctrl.setStatus("MARKISE-ZU", String(markiseObject.sonneIstWert)+"lx");
        Serial.println("Markise eingefahren warten auf Sonne ohne Wind");
        if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp("Markise zu").c_str());
        digitalWrite(LED_ROT, LOW);   // turn the LED on Markise zu
        vTaskDelay((markiseObject.markiseAufAbtastungSekunden*1000) / portTICK_PERIOD_MS);
        if(CheckLichtSensor(&markiseObject) == true)	// Sonne Zähler hochzählen
          markiseObject.counterMarkiseAufAbtastung++;
        else if(markiseObject.counterMarkiseAufAbtastung>0) // Sonne wieder weg, Zähler runterzählen
          markiseObject.counterMarkiseAufAbtastung--;
        if(markiseObject.counterMarkiseAufAbtastung >markiseObject.countMarkiseAufAbtastung)
          markiseObject.counterMarkiseAufAbtastung=markiseObject.countMarkiseAufAbtastung;// Max-Wert begrenzen
        if(markiseObject.counterMarkiseAufAbtastung == markiseObject.countMarkiseAufAbtastung && 
          markiseObject.counterWindZuAbtastung <= 2) {	// Viel Sonne und fast kein Wind?
            FahreMarkiseAuf(&markiseObject, false);
            markiseObject.counterMarkiseAufAbtastung=0;
          }
        String cnt = "cntMarkAuf=" + String(markiseObject.counterMarkiseAufAbtastung) + 
          " Sonne="+ String(markiseObject.sonneIstWert) + " Wind="+ String(markiseObject.windIstWert);
        Serial.println(cnt);
        if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp(cnt).c_str());
    }
      // Markise offen
      if(markiseObject.markiseStatus == AUSGEFAHREN){
        display_ctrl.setStatus("MARKISE-AUF", String(markiseObject.sonneIstWert)+"lx");
        Serial.println("Markise ausgefahren warten auf Wind und Sonne");
        if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp("Markise Auf").c_str());
        vTaskDelay((markiseObject.markiseZuAbtastungSekunden*1000) / portTICK_PERIOD_MS);
        if(CheckLichtSensor(&markiseObject) == false)
          markiseObject.counterMarkiseZuAbtastung++;
        else if(markiseObject.counterMarkiseZuAbtastung>0) // Sonne wieder da, Zähler runterzählen
          markiseObject.counterMarkiseZuAbtastung--;
        if(markiseObject.counterMarkiseZuAbtastung >markiseObject.countMarkiseZuAbtastung)
          markiseObject.counterMarkiseZuAbtastung=markiseObject.countMarkiseZuAbtastung;// Max-Wert begrenzen
        if(markiseObject.counterMarkiseZuAbtastung == markiseObject.countMarkiseZuAbtastung ||
            markiseObject.counterWindZuAbtastung>=markiseObject.countWindZuAbtastung) {
            FahreMarkiseZu(&markiseObject,false);
            markiseObject.counterMarkiseZuAbtastung=0;
          }
        String cnt = "cntMarkZu=" + String(markiseObject.counterMarkiseZuAbtastung) + 
          " Sonne="+ String(markiseObject.sonneIstWert)+ " Wind="+ String(markiseObject.windIstWert);
        Serial.println(cnt);
        if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp(cnt).c_str());
      }

      while(markiseObject.markiseStatus == FAHRE_AUF || markiseObject.markiseStatus == FAHRE_ZU){
        Serial.println("Markise fährt AUF oder ZU -- Warten");
        if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp("Markise fährt AUF oder ZU -- Warten").c_str());
        vTaskDelay((10000) / portTICK_PERIOD_MS); // 10 Sek warten
      }
    } 
    else {
        vTaskDelay((markiseObject.markiseAufAbtastungSekunden*1000) / portTICK_PERIOD_MS);
        Serial.println("Automatik ist aus");
    }

  }
}
				
			

Die WindTask

In der WindTask werden die eingelesenen Werte des AD-Wandlers auf die Werte 0-100 mit der map-Funktion umgesetzt. 
Dieser wert wird dann mit dem „windSollWert“ verglichen um festzustellen, ob es ein böiger Wind oder ein normaler wind ist.
Bei Böen wird ein Offset auf „counterWindZuAbtastung“ aufaddiert.
Mit „counterWindZuAbtastung“ wir die Winderfassung in die Regelung eingespeist.

				
					void TaskWind(void * pvParameters){
  Serial.println("Starte TaskWind");
  for(;;){
    Serial.println("wdt_wind_task==="+String(wdt_wind_task));
    wdt_wind_task = WDT_TIMEOUT; //Trigger watchdog
    vTaskDelay((markiseObject.windAbtastungSekunden*1000) / portTICK_PERIOD_MS);
    int wert=0;
    wert = analogRead(WIND_AEROMETER);
      //Serial.println("Wert="+ String(wert));
    wert= map(wert,0,4096,0,100);
#ifdef TESTHW
    wert = windSensorWert;
#endif
    Serial.println("Analoger WIND-Wert= "+ String(wert));
    //Serial.println("CheckWindSensor->AktuellerWindWert="+ String(windSensorWert)+"km/h");
    markiseObject.windIstWert = wert;
    
    // Starker WIND?
    if(wert >= markiseObject.windSollWert+10){
      Serial.println("Markise zufahren Starker Wind");
      markiseObject.counterWindZuAbtastung=markiseObject.countWindZuAbtastung; // Starker Wind zählt mehr
      markiseObject.counterWindZuAbtastung+=4; // Starker Wind zählt mehr
      Serial.println("WindSensor stark->>windIstWert="+ String(markiseObject.windIstWert));
      Serial.println("WindSensor stark->>counterWindZuAbtastung="+ String(markiseObject.counterWindZuAbtastung));
      continue;
    }
    // Normaler Wind oder kein Wind
    else if(wert>= markiseObject.windSollWert)
      markiseObject.counterWindZuAbtastung++;
    else if(markiseObject.counterWindZuAbtastung>0) // Wind wieder weg, Zähler runterzählen
      markiseObject.counterWindZuAbtastung--;
    Serial.println("WindSensor normal->>windIstWert="+ String(markiseObject.windIstWert));
    Serial.println("WindSensor normal->>counterWindZuAbtastung="+ String(markiseObject.counterWindZuAbtastung));
  }
}
				
			

Die Antriebssteuerung der Markise

Unspektakulär wird hier jeweils das Semaphore gesetzt, was die Markise dann AUF oder ZU fahren lässt.

				
					void FahreMarkiseAuf(MarkiseObject *markise, bool manuell){
  if(markise->markiseStatus == EINGEFAHREN || manuell==true){
    Serial.println("Fahre Markise Auf (Set Semaphore)");
    markise->markiseStatus = FAHRE_AUF;
    xSemaphoreGive(MarkiseAufSemaphore);
  }
}
void TaskFahreMarkiseAuf(void * pvParameters){
  Serial.println("Starte TaskFahreMarkiseAuf");
  for(;;){
    //Semaphore abfragen
    if (xSemaphoreTake(MarkiseAufSemaphore, portMAX_DELAY)) {
      display_ctrl.setStatus("MARKISE", "--> AUF");
      Serial.println("Task-->Fahre Markise Auf -- Start");
      if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp("Task-->Fahre Markise Auf").c_str());
      digitalWrite(LED_GELB, LOW);
      digitalWrite(LED_ROT, HIGH);   // turn the LED off
      digitalWrite(LED_GRUEN, LOW);   // turn the LED on
      // Schalte Relais für Markise auf
      digitalWrite(PIN18AUF, HIGH);
      vTaskDelay((500) / portTICK_PERIOD_MS); //500MS Tastendruck
      digitalWrite(PIN18AUF, LOW);
      vTaskDelay((markiseObject.markiseLaufzeitSekunden*1000) / portTICK_PERIOD_MS);
      digitalWrite(PIN20STOPP, HIGH);
      vTaskDelay((500) / portTICK_PERIOD_MS); //500MS Tastendruck "Relais Stopp"
      digitalWrite(PIN20STOPP, LOW);
      markiseObject.counterMarkiseAufAbtastung=0;
      markiseObject.counterMarkiseZuAbtastung=0;
      markiseObject.markiseStatus = AUSGEFAHREN;
      Serial.println("Task-->Fahre Markise Auf -- End");
      digitalWrite(LED_ROT, HIGH);   // turn the LED off
      digitalWrite(LED_GRUEN, LOW);   // turn the LED on
      digitalWrite(LED_GELB, HIGH);
    }
  }  
}

//**************************************************
//**************************************************
void FahreMarkiseZu(MarkiseObject *markise, bool manuell){
  digitalWrite(LED_ROT, LOW);   // turn the LED on
  if(markise->markiseStatus == AUSGEFAHREN || manuell==true){
    Serial.println("Fahre Markise ZU (Set Semaphore)");
    markise->markiseStatus = FAHRE_ZU;
    xSemaphoreGive(MarkiseZuSemaphore);
  }
}

void TaskFahreMarkiseZu(void * pvParameters){
  Serial.println("Starte TaskFahreMarkiseZu");
  for(;;){
    //Semaphore abfragen
   if (xSemaphoreTake(MarkiseZuSemaphore, portMAX_DELAY)) {
    Serial.println("Task-->Fahre Markise Zu -- Start");
    display_ctrl.setStatus("MARKISE", "--> ZU");
    if(trace_on == true) appendFile(LittleFS, "/trace.txt", get_time_stamp("Task-->Fahre Markise Zu").c_str());
    digitalWrite(LED_GELB, LOW);
    digitalWrite(LED_GRUEN, HIGH);
   digitalWrite(LED_ROT, LOW);   
 // Schalte Relais für Markise zu
    digitalWrite(PIN19ZU, HIGH);
    vTaskDelay((500) / portTICK_PERIOD_MS); //500MS Tastendruck "Relais zu"
    digitalWrite(PIN19ZU, LOW);
    vTaskDelay((markiseObject.markiseLaufzeitSekunden*1000) / portTICK_PERIOD_MS);
    digitalWrite(PIN20STOPP, HIGH);
    vTaskDelay((500) / portTICK_PERIOD_MS); //500MS Tastendruck "Relais Stopp"
    digitalWrite(PIN20STOPP, LOW);
    markiseObject.markiseStatus = EINGEFAHREN;
    markiseObject.counterMarkiseAufAbtastung=0;
    markiseObject.counterMarkiseZuAbtastung=0;
    Serial.println("Task-->Fahre Markise Zu -- End");
    digitalWrite(LED_ROT, LOW);   
    digitalWrite(LED_GRUEN, HIGH); 
    digitalWrite(LED_GELB, HIGH);
   }  
  }
}
				
			

Setup()

Hier im Setup() sieht man das anlegen der Tasks und Semaphores sowie das Aufsetzen des LittleFS, welches für das Traceing und den Webserver benötigt wird. Auch ist zu sehen das die Uhrzeit über NTC geholt wird und die interne Uhr (rtc) gesetzt wird.
Das Starten des Webservers leitet dann über zum Web-Interface welches auch auf dem ESP32S3 gehostet ist.

				
					void setup() {
	Serial.begin(115200);
	//Wire.setPins(SDA, SCL);
	Wire.begin(SDA,SLC);

	pinMode(LED_GELB, OUTPUT);
	pinMode(LED_ROT, OUTPUT);
	pinMode(LED_GRUEN, OUTPUT);
	pinMode(PIN18AUF, OUTPUT);
	pinMode(PIN19ZU, OUTPUT);
	pinMode(PIN20STOPP, OUTPUT);

	digitalWrite(LED_GELB, HIGH);   // turn the LED off
	digitalWrite(LED_ROT, HIGH);   // turn the LED off
	digitalWrite(LED_GRUEN, HIGH);   // turn the LED off
	digitalWrite(PIN18AUF, LOW); // Turn Transistor closed
	digitalWrite(PIN19ZU, LOW); // Turn Transistor closed
	digitalWrite(PIN20STOPP, LOW); // Turn Transistor closed
	lightMeter.begin();

	/*bool status = bme.begin(); 
	if (!status) {
  	Serial.println("Could not find a valid BME280 sensor, check wiring!");
  	while (1);
	}*/
 
	sensor.begin(Bme280TwoWireAddress::Primary); // Also for BMP
  sensor.setSettings(Bme280Settings::weatherMonitoring());
  DisplayMutex = xSemaphoreCreateMutex();
  if (DisplayMutex == NULL) {
    Serial.println("Failed to create DISPLAY mutex!");
    while (1);
  }
	EEPROM.begin(EEPROM_SIZE);
	int eepromSize = EEPROM.length();
	Serial.println("EEPROM_SIZE=="+ String(eepromSize));
	uint16_t statusEEPROM_Values;
	EEPROM.get(0, statusEEPROM_Values);
	Serial.println("statusEEPROM_Values==="+ String(statusEEPROM_Values));
	EEPROM.get(0, markiseObject);
	PrintMarkiseStruct(&markiseObject);

	if (statusEEPROM_Values == uint16_t(EEPROM_VALID_FLAG)) // Überprüfen, ob Werte im EEPROM gültig sind
	{
		EEPROM.get(0, markiseObject);
		Serial.println("EEPROM holen= "+ String(markiseObject.statusEEPROM_Values));
		PrintMarkiseStruct(&markiseObject);

	}
	else
	{
		InitMyData(&markiseObject);
		//EEPROM.put(0, markiseObject);
		//EEPROM.commit();
		//EEPROM.get(0, markiseObject);
		//markiseObject.statusEEPROM_Values= EEPROM_VALID_FLAG;
		Serial.println("EEPROM schreiben= "+ String(markiseObject.statusEEPROM_Values));
		PrintMarkiseStruct(&markiseObject);
	}

	display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
	u8g2_for_adafruit_gfx.begin(display);                 // connect u8g2 procedures to Adafruit GFX
	u8g2_for_adafruit_gfx.setFont(u8g2_font_helvR14_tf);  // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
	u8g2_for_adafruit_gfx.setFontMode(1);                 // use u8g2 transparent mode (this is default)
	u8g2_for_adafruit_gfx.setFontDirection(0);            // left to right (this is default)
	u8g2_for_adafruit_gfx.setForegroundColor(WHITE);      // apply Adafruit GFX color
	//print_display("WAIT FOR", "COMMAND");

  timer= timerBegin(1000000000); // Set IR pulse counter to 1MS resolution

	initWiFi();
	initLittleFS();
	configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  if(!getLocalTime(&timeinfo)){
    Serial.println("Zeit konnte nicht geholt werden");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
 	rtc.setTimeStruct(timeinfo);
	display_ctrl.setTime(rtc.getTime(), ""); // Set time for display

 
 	MarkiseZuSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);
  if (MarkiseZuSemaphore == NULL) {
    Serial.println("Failed to create MarkiseZuSemaphore!");
    while (1);
  }
 	MarkiseAufSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);
  if (MarkiseAufSemaphore == NULL) {
    Serial.println("Failed to create MarkiseAufSemaphore!");
    while (1);
  }

	xTaskCreatePinnedToCore(
    TaskFahreMarkiseZu,   /* Funktion */
    "TaskFahreMarkiseZu",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskMarkiseZu,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    TaskFahreMarkiseAuf,   /* Funktion */
    "TaskFahreMarkiseAuf",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskMarkiseAuf,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    TaskWind,   /* Funktion */
    "TaskWind",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskWindSensor,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    TaskMesseLicht,   /* Funktion */
    "LichtMessen",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskLichtMessen,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    TaskCheck_BME280,   /* Funktion */
    "BME280Messen",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskBME280Messen,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    Markise,   /* Funktion */
    "MarkiseTask",     /* Name */
    10000,       /* Stackgröße */
    NULL,        /* Parameter */
    1,           /* Priorität */
    &MarkiseTask,      /* Task-Handle */
    0);          /* Kern 1 */
	xTaskCreatePinnedToCore(
    TaskChangeDisplay,   /* Funktion */
    "DisplayTask",     /* Name */
    10000,       /* Stackgröße */
   NULL,        /* Parameter */
    2,           /* Priorität */
    &TaskDisplay,      /* Task-Handle */
    0);          /* Kern 1 */
	// Activate watchdog with system reset after timeout
	//esp_task_wdt_init(&twdt_config); 

	Watchdog = xTimerCreate("WatchdogTimer",
	 	1000/portTICK_PERIOD_MS, // 1 Sek period
	 	pdTRUE,
	 	NULL,
		WDTCallback);
	if (Watchdog == NULL) {
		Serial.println("Failed to create Watchdogtimer!");
    while (1);
	}
	xTimerStart(Watchdog, 0); // Starte Watchdog

	// Route for root / web page
	server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
		request->send(LittleFS, "/index.html", "text/html", false);
		});

	server.serveStatic("/", LittleFS, "/");

	//Response for all states
  server.on("/states", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("Response for all states");
    String json = getOutputStates();
    request->send(200, "application/json", json);
    json = String();
  });
  // Response for current sun/wind values
	server.on("/current", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("Response for current sun/wind values");
		String json = getOutputStates2();
    request->send(200, "application/json", json);
    json = String();
  });

  //GET request to <ESP_IP>/update?output=<output>&state=<state>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String value="";
     // GET input1 value on <ESP_IP>/update?output=<output>&state=<state>
    if (request->hasParam("manu")) {
      value = request->getParam("manu")->value();
			if (value =="auf") {
			FahreMarkiseAuf(&markiseObject, true);
			}else {
			FahreMarkiseZu(&markiseObject, true);
			}
    }
    if (request->hasParam("auto")) {
      value = request->getParam("auto")->value();
			if (value =="ein") {
			markiseObject.automatikStatus=EIN;
			}else {
			markiseObject.automatikStatus=AUS;
			}
    }
    if (request->hasParam("sonne")) {
      value = request->getParam("sonne")->value();
			markiseObject.sonneSollWert= value.toInt();
    }
    if (request->hasParam("wind")) {
      value = request->getParam("wind")->value();
			markiseObject.windSollWert= value.toInt();
    }
		Serial.println("Gesendeter Wert von Webpage: " + value);
    request->send(200, "text/plain", "OK");
  });
	// Start server
	server.begin();
}