Toggle Button ist nach jedem Aufruf der index.html wieder auf default

HAllo an Alle,

Sollte mein Thread hier falsch sein, dann bitte ich einen Admin höflich darum meinen Thread entsprechend zu verschieben und es mir mitzuteilen. Ich bin noch neu und kenne mich jetzt noch nicht so gut aus.

Ich habe da ein Problem mit einem Toggle Button:
Ich habe ein ESP8266 auf dem ich im SPIFFS eine index.html und ein style.css abgelegt habe.
Das Problem ist, dass wenn die index.html Seite erneut aufgerufen wird

request->send(SPIFFS, "/index.html", String(), false, processor)

dann ist der Button wieder auf default obwohl er zuvor "checked" war.
Wie kann ich nun über eine Variable oder was auch immer dem request ein entsprechenden status mit übergeben, den die Checkbox dann entsprechend übernimmt. Mir fehlt hier der Ansatz. Ich mache das über EspAsynchronwebServer.h.
Ich habe im Netz ein Besipiel gefunden hierwo es an sich schön erklärt wird, aber da wird die ESP8266WebServer.h verwendet und dann kennt er in meinem Sketch verschiedene Befehle nicht.
Das Hin- und Her schalten mache ich im Moment über Buttons mit "AN" und "AUS" und ich wollte es ein wenig aufhübschen aber da fehlen mir einfach die Kenntnisse.
In meiner Index.html steht folgendes....

<div class="toggle text">
 <label>
 <input type="checkbox" id="RoofHatch" onchange="state_Change(this)" >
 <span class="slider"></span> 
 </label>
 </div>
<script>
function state_Change(element) {
  var xhttp = new XMLHttpRequest();
  
  if (element.checked){
 xhttp.open("GET", "openClose_RoofHatch?button_state=1", true);
 document.getElementById("RoofHatch_state").innerHTML = "AUF"
  } else if (!element.checked){
 xhttp.open("GET", "openClose_RoofHatch?button_state=0", true);
 document.getElementById("RoofHatch_state").innerHTML = "ZU"
  }  
  xhttp.send();
}
</script>

in meinem Sketch steht folgendes: (Auszug)

server.on("/openClose_RoofHatch", HTTP_GET, [](AsyncWebServerRequest *request){
    if(request->hasArg("button_state")){
      String button_state = request->arg("button_state");
      String curr_state = "ZU";
      if (button_state == "1"){
        Serial.println("Dachluke öffnen");
        //set RoofHatchSpeed
        analogWrite(enA, RoofHatchSpeed);
        // set Pins to set moving direction of RoofHatch motor
        digitalWrite(in1, LOW);
        digitalWrite(in2, HIGH);
        curr_state = "AUF";
      }
      else
      {
        Serial.println("Dachluke schliessen");
        //set RoofHatchSpeed
        analogWrite(enA, RoofHatchSpeed);
        // set Pins to set moving direction of RoofHatch motor
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);
        curr_state = "ZU";
      }
    request->send_P(200, "text/plane", "0");
        
    }
  });

Wenn ich nur den Toggle Button betätige funktioniert auch alles wie es soll, jedoch nicht wenn ich einen anderen Button oder so betätige bzw. über den oben geschreibenen Befehl die index.html aus dem Speicher schicke.

Ich hoffe ich konnte es einigermassen beschreiben und habe auch entsprechen Infos reingepackt damit ihr es versteht. WEnn nicht, dann schreibt was ihr noch wissen wollt.

Viele Grüße
Thorsten

Du erweiterst deinen Server um

server.on("/get_RoofHatch", HTTP_GET, ...

was dir den aktuellen Status der Dachluke ausgibt.

Dann erweiterst du dein HTML im

<body onload="... <p>um eine Funktion die /get_RoofHatch aufruft und entsprechend der Rückgabe die Checkbox setzt.</p>

Hallo Rintin,

Danke für Deine Antwort. Oha, jetzt gehts ans eingemachte. Das Prinzip habe ich verstanden. Der WErt für die Checkbox wurde ja vorher auf dem Server gespeichert. Du meinst jetzt, dass wenn die Seite index.html erneut vom Speicher aufgerufen wird zuerst über "onload" mittels Funktion eine Anfrage an den Server sendet wie der Status ist? Sieht bei mir in der index.html jetzt so aus:

<body onload="set_ButtonState()">
function set_ButtonState() {
 var xhttp = new XMLHttpRequest();
 
 xhttp.open("GET", "get_RoofHatchState?", true);
 xhttp.send();
 if (button_state == "1"){
 document.getElementById("checkbox_RoofHatch").checked = true;
  } else if (button_state =="0"){
 document.getElementById("checkbox_RoofHatch").checked = false;
  }  
 
}

Im Server habe ich eine Routine hinterlegt die sieht folgendermaßen aus:

server.on("/get_RoofHatchState", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plane", button_state); 
  });

Send_P klappte nicht (hier kenne ich mich auch viel zu wenig aus. ich wüsste jetzt auch nicht den Unterschied zwischen request -> send und request ->send_P) Wenn ich nun aber die Seite aufrufe und auf den Toggle_Button klicke dann passiert gar nichts mehr. Sorry funzt doch. Hatte einen SChreibfehler drin. Also es lässt sich weiterhin toggeln, aber das ERgebnis ist immer noch das gleiche. WEnn ich einen Button drücke und die index.html wird neu aufgerufen ist der ToggleButton wieder im Ausgangszustand. Meine html Kenntnisse sind hier noch zu gering. Ich weiß jetzt nicht ob ich die Rückgabe auch richtig ausgeführt habe und ob der Server an die aufrufende Funktion den Wert korrekt zurückschickt, sodass der Status gesetzt werden könnte. Kann mir hier jemand unter die Arme greifen?
Das wäre nett.
VIelen Dank!

Gruß Thorsten

Was gibt dein browser aus, wenn du http:///get_RoofHatchState aufrufst? ( mit der richtigen IP ersetzen)

 xhttp.send();
 if (button_state == "1"){

Wie kommt dir Rückgabe vom xhttp-Object in button_state? Vielleicht willst du mit xhttp.responseText arbeiten.

Edit: Wobei wenn ich mir XML HttpRequest so anschaue, hängen die eine Funktion an xhttp.onreadystatechange, das dann den update durchführt.

Hallo Rintin,
Wenn Du mir sagst wie ich das prüfen kann?
Wo kann ich das debuggen?
Wenn ich die Seite direkt im Browser aufrufe dann kommt ein Pop-Ip wo er mir eine Datei runterladen möchte. "get_RoffHatcState" siehe Anhang unten

DA bin ich irgendwo ganz falsch hingekommen...nun weiss ich nimmer weiter

GRuß Thorsten

get_RoofHatchState.JPG

test/plane hört sich falsch an. Sollte vermutlich text/plain sein.
0 Bytes sind auch zu wenig.

Kannst du mal ein compilierbares Sketch hier posten?

Ich lade erst die normale html Seite aus dem SPIFFS und hole dann alle Sensordaten über Ajax.

		<script type="text/javascript">
		// RefreshData function
		$.fn.RefreshData = function(){
			$.ajax({
				type: "GET",
				url: "/myurl_ajax",
			 	data: "",
				cache: false,
				success: function(html){
 					var values = html.split("____");
					$("#version").html(values[0]);
 					$("#logoff").toggle(values[1] === "1" ? true : false); //hier wäre z.B. dein Fenster auf/zu bool drin
					$("#devicename").html(values[2]);
					$("#api").html(values[3]);
					$("#uptime").html(values[4]);
					$("#signal").html(values[5]);
                                }
			});
		};

		$(document).ready(function() {
			// Allways start with 'RefreshData' to collect data with JQuery
			$().RefreshData();
		});

Im Sketch wird zwischen jeden Wert der Seperator "____" eingefügt und ajax: success schreibt die Werte in die id's der html Seite.

Mein ESP hat Internet Verbindung.
Wenn du nur Offline arbeitest, kopiere halt jQuery in den SPIFFS. Da ist doch genügend Platz.

HAllo,
so hab mich wieder etwas versucht.
@Freddy64:
Danke für den Tip. Aber nochmal was neues dazu lernen das wird mir grad zuviel. Ich möchte mich erst mal an dem versuchen was ich bis jetzt angefangen habe.

@Rintin:
ICh versuch mal ne abgespeckte Version hier reinzuposten.
mein Sketch:

// Import required libraries
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

#include <Wire.h>
#include <FS.h>
#include <SPI.h>



int RoofHatch_manOpen, RoofHatch_manClose;

String button_state;


int in1 = D5;
int in2 = D6;
int enA = D7;
int RoofHatchSpeed = 1024;



// Set LED GPIO
const int SSR = D8;
// Stores LED state
String ledState;

//************************************************
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);



// Replaces placeholder with LED state value
String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if(digitalRead(SSR)){
      ledState = "AN";
    }
    else {
      ledState = "AUS";
    Serial.println(ledState);
    }
    return ledState;
  }
}
 
void setup(){

  
  pinMode(in1,OUTPUT);
  pinMode(in2,OUTPUT);
  pinMode(enA,OUTPUT);
  pinMode(SSR, OUTPUT);
 
  
  // Serial port for debugging purposes
  Serial.begin(115200);

  WiFi.setAutoConnect(false);
  WiFi.softAP("MeinWemos","12345678");
  server.begin();
  
  //server.onNotFound([](){
  //  request->send(404, "text/plain", "link nicht vorhanden");
  //});
  
  //server.on("/", []() {
  //  request->send(200, "text/plain", "ESP-Startseite");
  //});

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  //Route to load style.css file
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });

  // Route to set GPIO to HIGH
  server.on("/AN", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(SSR, HIGH);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });

   // Route to set GPIO to LOW
  server.on("/AUS", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(SSR, LOW);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/get_RoofHatchState", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", button_state); 
  });

  server.on("/open_RoofHatch", HTTP_GET, [](AsyncWebServerRequest *request){
    RoofHatch_manOpen = 1;
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });

  server.on("/close_RoofHatch", HTTP_GET, [](AsyncWebServerRequest *request){
    RoofHatch_manClose = 1;
    request->send(SPIFFS, "/index.html", String(), false, processor); 
  });

  server.on("/openClose_RoofHatch", HTTP_GET, [](AsyncWebServerRequest *request){
    if(request->hasArg("button_state")){
      String button_state = request->arg("button_state");
      String curr_state = "ZU";
      if (button_state == "1"){
        Serial.println("Dachluke öffnen");
        //set RoofHatchSpeed
        analogWrite(enA, RoofHatchSpeed);
        // set Pins to set moving direction of RoofHatch motor
        digitalWrite(in1, LOW);
        digitalWrite(in2, HIGH);
        curr_state = "AUF";
      }
      else
      {
        Serial.println("Dachluke schliessen");
        //set RoofHatchSpeed
        analogWrite(enA, RoofHatchSpeed);
        // set Pins to set moving direction of RoofHatch motor
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);
        curr_state = "ZU";
      }
    request->send(200, "text/plain", button_state);
        
    }
  });
}

  


  void switchOn_SSR() {
    digitalWrite(SSR, HIGH);
    delay(500);
  }

  void switchOff_SSR() {
    digitalWrite(SSR, LOW);
    delay(500);
  }



 
 
void loop(){
 
}

Meine index.html und meine style.css sind angehängt. haben hier als quote nicht reingepasst.

Es könnte sein, dass das ein oder andere überflüssig ist. aber seis drum.
Ich habe versucht verschiedene Alerts einzubauen um feststellen zu können wohin er läuft und wohin nicht. DAbei ist mir aufgefallen, dass er bei der URL /openClose_RoofHatch korrekt in mein entsprechenden server.on routine reinläuft und auch den Serial.println ausführt. Komischerweise macht er das bei der URL /getRoofHatchState nicht. Warum auch immer?
ICh bin mit meinem LAtein am Ende. grübel
Vielleicht seht ihr ja den Fehler....?

Liebe Grüße
Thorsten

data.zip (2.02 KB)

Mal sehen

server.on("/get_RoofHatchState", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", button_state);
  });

Hier sendest du das globale button_state

server.on("/openClose_RoofHatch", HTTP_GET, [](AsyncWebServerRequest *request){
    if(request->hasArg("button_state")){
      String button_state = request->arg("button_state");

Hier definierst du ein lokales button_state. Du musst aber das globale nehmen.
Mach mal auf dem String button_state nur ein button_state. Also das String davor entfernen.

Dann sollte der Aufruf von http:///get_RoofHatchState was vernünftiges zurückgeben.
Und wenn du dem globalen button_state im setup() noch einen Wert zuweist, das sogar vor dem 1. Aufruf von /openClose_RoofHatch

Wenn das geht, schau ich mir das JS an.

Hallo Rintin,

ich traue es mir gar nicht zu sagen, aber irgendwie hat es nichts gebracht.....
Also ich habe so wie du gesagt hast das String (lokales button_state) entfernt. Das globale (also im SKetch ganz zu Beginn gelassen) Der einzige Unterschied ist, dass er beim Aufruf (mittels Button "AUF") es nun zirka 10Sekunden dauert bis plötzlich der Toggle Button wieder zurückspringt. Wie eine Art Zeitverzögerung. Danach ist aber wieder alles gleich. ER springt sofort zurück. Aber ich habe mal ein Bild angehängt was ich zurückbekomme, wenn ich direkt /get_RofofHatchState in der URL eingebe. SIeht nicht ganz verkehrt aus denke ich.

Du hast geschrieben ich solle der globalen button_State einen Wert vor dem 1.Aufruf vergeben.....das vertehe ich nicht ganz.

EDIT: ICh habe gerade mal noch was im Javascript experimentiert....leider auch ohne ERfolg.....
MAn sieht ich kenne mich noch nicht aus. Das ist fischen im trüben Wasser :wink: ICh habe folgendes gemacht:

function set_ButtonState() {
	var xhttp = new XMLHttpRequest();
	alert("das ist vor onreadystate");
	xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
	alert("DAs ist in der if Schleife");
		if (button_state == "1"){
			document.getElementById("checkbox_RoofHatch").checked = true;
		} else if (button_state =="0"){
			document.getElementById("checkbox_RoofHatch").checked = false;
		} 
	}
    };
	alert("das ist vor xhttopen");
	alert(xhttp.responseText);
	xhttp.open("GET", "get_RoofHatchState", true);
	alert("das ist vor send");
	alert(button_state);
	xhttp.send();
	
}

DEr Gedanke an sich bestimmt nicht verkehrt.....aber die Umsetzung happert

GRuß Thorsten

Ich hab das mal von button_state auf this.responseText umgestellt.

function set_ButtonState() {
	var xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
       // Typical action to be performed when the document is ready:
		if (this.responseText == "1"){
			document.getElementById("checkbox_RoofHatch").checked = true;
		} else if (this.responseText =="0"){
			document.getElementById("checkbox_RoofHatch").checked = false;
		} 
	}
    };
	xhttp.open("GET", "get_RoofHatchState", true);
	xhttp.send();
}

Du hast geschrieben ich solle der globalen button_State einen Wert vor dem 1.Aufruf vergeben.....das vertehe ich nicht ganz.

Einfach im setup() ein
button_State = "1";
machen. (Oder was als Defaultwert da passt)

HAllo Rintin,

ich wünsche einen schönen guten Abend. Sorry dass es ein wenig länger dauerte, bin aber geschäftlich im Moment nicht dazu gekommen.
Also dein Tipp mit dem this.responseText scheint das Problem gelöst zu haben.
Der Toggle Button bleibt auf dem vorher selektierten WErt. Vielen Dank hierfür.
Ein kleinen Schönheitsfehler hat die ganze Sache aber.
Und zwar im ersten Moment wenn man etwas anderes anklickt, "springt" der Toggle Button kurz auf den Default Wert. Das heisst man sieht ihn kurz umschalten. In Wirklichkeit schaltet er wohl nicht um, aber er ändert kurz seinen State. Gibt es hier irgendwie eine Möglichkeit das zu unterbinden?

VIele Grüße und nochmals ein Dankeschön an Dich Rintin.

Gruß Thorsten