webserver na ESP8266

OtikM
Příspěvky: 7
Registrován: 09 dub 2021, 19:57

webserver na ESP8266

Příspěvek od OtikM »

Dobrý den,
vytvářím webserver na esp8266 potřebuju vytvořit textová pole, do kterého je zapisovaná hodnota uživatelem. Po změně se hodnota auto. uloží a načte uložená pro kontrolu = obnova stránky. Bohužel hodnota se ukládá nepravidelně i když dojde k obnově stránky načte se původní hodnota. Hodnota se uloží vždy pokud kliknu myší na tlačítko. Přitom dochází k dvojité obnově stránky. Tlačítko taky funguje až po dvoukliku. Kde je problém?
Na tlačítko používám kod:

Kód: Vybrat vše

client.println("<p>Zpoždění: <input type=\"number\" name=\"txt\" value=\"" +
String(EEPROM.read(1)) + "\" onchange=\"setTimer(this.value)\" min=\"10\" max=\"40\"> s (25s)</p>");
+ tento kod, který se bohužel neprovede při změně (výjimečně nebo pokud kliknu na tlačítko).

Kód: Vybrat vše

if(header.indexOf("GET /?timer=") >= 0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
timer = valueString.toInt();
EEPROM.write(1, timer);
EEPROM.commit();
Serial.println(valueString);
pro zpracování požadavku používam tento kod:

Kód: Vybrat vše

client.println("<script> function setTimer(value) { var xhr = new XMLHttpRequest();");
client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);");
client.println("xhr.send(); location.reload(true); } ");
Naposledy upravil(a) OtikM dne 11 dub 2021, 08:49, celkem upraveno 1 x.
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Re: webserver na ESP8266

Příspěvek od MartinL »

Mnohem lepší by bylo zveřejnit celý kód. V těchto útržcích se dá těžko vyznat (hlavně, když není oddělen kód, který běží na ESP od vlastní webové stránky).
OtikM píše: 10 dub 2021, 09:02 client.println("<script> function setTimer(value) { var xhr = new XMLHttpRequest();");
client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);");
client.println("xhr.send(); location.reload(true); } ");
Takže jen postřeh. Odeslání požadavku na server (viz. výše) má problém, že nečeká na dokončení operace a hned provede reload. Co se stane ve skutečnosti, těžko říci. V každém případě bych počkal na odpověď serveru a až když bude potvrzeno, tak provedl reload stránky.
OtikM
Příspěvky: 7
Registrován: 09 dub 2021, 19:57

Re: webserver na ESP8266

Příspěvek od OtikM »

díky za reakci Zasílám celý loop:

Kód: Vybrat vše

void loop() {
  WiFiClient client = server.available();   // Listen for incoming clients
  if (client) {                             // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) {            // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();                     
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");
            
            // Request example: GET /?mode=0& HTTP/1.1 - sets mode to Manual (0)
            if(header.indexOf("GET /?mode=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              selectedMode = valueString.toInt();
              EEPROM.write(1, selectedMode);
              EEPROM.commit();
              configureMode();
            }
            // Change the output state - turn GPIOs on and off
            else if(header.indexOf("GET /?state=on") >= 0) {
              outputOn();
            } 
            else if(header.indexOf("GET /?state=off") >= 0) {
              outputOff();
            }
            // Set timer value
            else if(header.indexOf("GET /?timer=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              timer = valueString.toInt();
              EEPROM.write(2, timer);
              EEPROM.commit();
              Serial.println(valueString);
            }
            // Set LDR Threshold value
            else if(header.indexOf("GET /?ldrthreshold=") >= 0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              ldrThreshold = valueString.toInt();
              EEPROM.write(3, ldrThreshold);
              EEPROM.commit();
              Serial.println(valueString);
            }
            
            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");
            // Drop down menu to select mode
            client.println("<p><strong>Mode selected:</strong> " + modes[selectedMode] + "</p>");
            client.println("<select id=\"mySelect\" onchange=\"setMode(this.value)\">");
            client.println("<option>Change mode");
            client.println("<option value=\"0\">Manual");
            client.println("<option value=\"1\">Auto PIR");
            client.println("<option value=\"2\">Auto LDR");
            client.println("<option value=\"3\">Auto PIR and LDR</select>");
          
            // Display current state, and ON/OFF buttons for output 
            client.println("<p>GPIO - State " + outputState + "</p>");
            // If the output is off, it displays the ON button       
            if(selectedMode == 0) {
              if(outputState == "off") {
                client.println("<p><button class=\"button\" onclick=\"outputOn()\">ON</button></p>");
              } 
              else {
                client.println("<p><button class=\"button button2\" onclick=\"outputOff()\">OFF</button></p>");
              }
            }
            else if(selectedMode == 1) {
              client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
                              String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
            }
            else if(selectedMode == 2) {
              client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
                              String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");
            }
            else if(selectedMode == 3) {
              client.println("<p>Timer (0 and 255 in seconds): <input type=\"number\" name=\"txt\" value=\"" + 
                               String(EEPROM.read(2)) + "\" onchange=\"setTimer(this.value)\" min=\"0\" max=\"255\"></p>");
              client.println("<p>LDR Threshold (0 and 100%): <input type=\"number\" name=\"txt\" value=\"" + 
                               String(EEPROM.read(3)) + "\" onchange=\"setThreshold(this.value)\" min=\"0\" max=\"100\"></p>");            
            }
            // Get and display DHT sensor readings
            if(header.indexOf("GET /?sensor") >= 0) {
              sensors.requestTemperatures(); 
              temperatureString = " " + String(sensors.getTempCByIndex(0)) + "C  " +  
                        String(sensors.getTempFByIndex(0)) + "F";   
             
              client.println("<p>");
              client.println(temperatureString);
              client.println("</p>");
              client.println("<p><a href=\"/\"><button>Remove Sensor Readings</button></a></p>");
            }
            else {
              client.println("<p><a href=\"?sensor\"><button>View Sensor Readings</button></a></p>");
            }
            client.println("<script> function setMode(value) { var xhr = new XMLHttpRequest();"); 
            client.println("xhr.open('GET', \"/?mode=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setTimer(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setThreshold(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?ldrthreshold=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOn() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=on\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOff() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=off\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function updateSensorReadings() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?sensor\", true);"); 
            client.println("xhr.send(); location.reload(true); }</script></body></html>");
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
Naposledy upravil(a) OtikM dne 11 dub 2021, 08:46, celkem upraveno 1 x.
Uživatelský avatar
Dex
Administrátor
Příspěvky: 1519
Registrován: 16 úno 2013, 14:26

Re: webserver na ESP8266

Příspěvek od Dex »

Na "kód" tu máme tlačítko, to páté zleva v editaci příspěvku, takže ho, prosím, použijte!
"all your robots are belong to us"
robodoupe.cz
OtikM
Příspěvky: 7
Registrován: 09 dub 2021, 19:57

Re: webserver na ESP8266

Příspěvek od OtikM »

Už tomu rozumím, ale nevím jak to mám zapsat, nějaký typ bez překopání celého kodu?
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Re: webserver na ESP8266

Příspěvek od MartinL »

OtikM píše: 11 dub 2021, 09:11 Už tomu rozumím, ale nevím jak to mám zapsat, nějaký typ bez překopání celého kodu?
Ono by to stálo za to, to celé předělat. Typické extenzivní programování (kód dlouhý jak měsíc před výplatou ...). Základem rozumného programování je si to celé rozdělit na jednotlivé jednoduché části (pak je kód maximálně na jednu obrazovku - návštěvníci Robodoupat, určitě poznávají moje heslo).
Dále ladít postupně, tj. vyzkoušet, že mi daná část funguje, a pak to tam teprve nasázet 10x. Ale pokud to mám rozčleněno, tak to tam zůstane jen jednou.

Ale abych jen nekritizoval. Zkusil bych ten GET požadavek vyřídit s čekáním na dokončení akce.

Vytvoříme si funkci pro zaslání GET dotazu:

Kód: Vybrat vše

client.println("function sendGet(url) {
  var xhr = new XMLHttpRequest();

  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) 
      location.reload(true);
    else
      alert("Error");
  }
    
  xhr.open('GET', url);
  xhr.send();
}");
a místo:
OtikM píše: 10 dub 2021, 19:59

Kód: Vybrat vše

	...
            client.println("function setTimer(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
        . ..   
použijeme vytvořenou funkci:

Kód: Vybrat vše

	...
            client.println("function setTimer(value) { sendGet(\"/?timer=\" + value + \"&\");}");
        . ..   
Toto bych vyzkoušel, ale neručím za to, že je to jediný problém.
Naposledy upravil(a) MartinL dne 11 dub 2021, 11:55, celkem upraveno 1 x.
OtikM
Příspěvky: 7
Registrován: 09 dub 2021, 19:57

Re: webserver na ESP8266

Příspěvek od OtikM »

Děkuji, zkusím to implementovat.
Vím, že moje programování není elegantní, ale jsem začátečník.
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Re: webserver na ESP8266

Příspěvek od MartinL »

OtikM píše: 11 dub 2021, 10:35 Vím, že moje programování není elegantní, ale jsem začátečník.
Právě proto, že jsem předpokládal, že jsi začátečník, tak se pokouším poradit jak to raději nedělat (neučit se špatným návykům). Bohužel většina zveřejňovaných "projektů" má kód psaný tímto stylem. Dnes každý, co něco "spatlá" se s tím chlubí, což je dobře. Ale je nutné si z toho vybrat pokud možno to dobré (obvykle nápad).
Zrovna tento příklad není pro začátek úplně vhodný. Kombinuje se v něm mnoho věcí dohromady - programování ESP (webserver) a HTML stránka s JavaScriptem. Proto je vhodnější si to pro začátek rozdělit. Tu webovou stránku si napsat na PC (kde si ji zobrazuji) a na ESP si sahat jen pro data, resp. něco nastavovat. Teprve až to bude odladěné, tak tu webovou stránku přesunout na webserver na ESP. Bude se to výrazně lépe ladit (není nutné pokaždé nahrávat do ESP).
Také bych doporučil neprogramovat na straně ESP ten server (rozebírání GET dotazu, atd.), ale využít už hotovou knihovnu realizující Webserver (sice jsem odpůrcem knihoven, ale v tomto případě je to namístě).
Ještě můžu pro inspiraci nabídnout sérii článků, které jsem k tomuto tématu začal sepisovat (ale bohužel nedotáhl do konce).
OtikM
Příspěvky: 7
Registrován: 09 dub 2021, 19:57

Re: webserver na ESP8266

Příspěvek od OtikM »

V javascriptu se moc nevyznám, ale učím se.
Zkusil jsem implementovat tvůj kod takhle, bohužel mi přestali chodit ostatní tlačítka, nebo tam implementace je špatně?

Kód: Vybrat vše

client.println("<script> function setTimer(value) { sendGet(\"/?timer=\" + value + \"&\");");
            client.println("function sendGet(url) {  var xhr = new XMLHttpRequest();");
            client.println("xhr.onreadystatechange = function () { if (xhr.readyState == 4) location.reload(true);  } ");
            client.println("xhr.open('GET', url);  xhr.send(); } ");         
            client.println("<script> function setTimer(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setThreshold(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?threshold=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOn() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=on\", true);"); 
            client.println("xhr.send();  location.reload(true); } ");
            client.println("function outputOff() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=off\", true);"); 
            client.println("xhr.send();  location.reload(true);}</script></body></html>");
MartinL
Příspěvky: 252
Registrován: 24 úno 2013, 14:13
Kontaktovat uživatele:

Re: webserver na ESP8266

Příspěvek od MartinL »

OtikM píše: 11 dub 2021, 19:43 V javascriptu se moc nevyznám, ale učím se.
Zkusil jsem implementovat tvůj kod takhle, bohužel mi přestali chodit ostatní tlačítka, nebo tam implementace je špatně?
Proto jsem navrhoval učit se jednotlivé věci samostatně a teprve pak je spojovat dohromady.
OtikM píše: 11 dub 2021, 19:43

Kód: Vybrat vše

client.println("<script> function setTimer(value) { sendGet(\"/?timer=\" + value + \"&\");");
            client.println("function sendGet(url) {  var xhr = new XMLHttpRequest();");
            client.println("xhr.onreadystatechange = function () { if (xhr.readyState == 4) location.reload(true);  } ");
            client.println("xhr.open('GET', url);  xhr.send(); } ");         
            client.println("<script> function setTimer(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?timer=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function setThreshold(value) { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?threshold=\" + value + \"&\", true);"); 
            client.println("xhr.send(); location.reload(true); } ");
            client.println("function outputOn() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=on\", true);"); 
            client.println("xhr.send();  location.reload(true); } ");
            client.println("function outputOff() { var xhr = new XMLHttpRequest();");
            client.println("xhr.open('GET', \"/?state=off\", true);"); 
            client.println("xhr.send();  location.reload(true);}</script></body></html>");
Když už jsme si udělali tu funkci pro GET dotaz, tak ji budeme používat (místo toho kódu výše to bude vypadat takto):

Kód: Vybrat vše

	    client.println("<script>");
            client.println("function sendGet(url) {  var xhr = new XMLHttpRequest();");
            client.println("xhr.onreadystatechange = function () { if (xhr.readyState == 4) location.reload(true);  } ");
            client.println("xhr.open('GET', url);  xhr.send(); } ");         

	    client.println("function setTimer(value) { sendGet(\"/?timer=\" + value + \"&\");}");
            client.println("function setThreshold(value) { sendGet(\"/?threshold=\" + value + \"&\");}");
            client.println("function outputOn() { sendGet(\"/?state=on\");}");
            client.println("function outputOff() { sendGet( \"/?state=off\");}");
            client.println("</script></body></html>");
Odpovědět