Stránka 1 z 2
webserver na ESP8266
Napsal: 10 dub 2021, 09:02
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); } ");
Re: webserver na ESP8266
Napsal: 10 dub 2021, 10:36
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.
Re: webserver na ESP8266
Napsal: 10 dub 2021, 19:59
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
}
}
}
Re: webserver na ESP8266
Napsal: 10 dub 2021, 22:08
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!
Re: webserver na ESP8266
Napsal: 11 dub 2021, 09:11
od OtikM
Už tomu rozumím, ale nevím jak to mám zapsat, nějaký typ bez překopání celého kodu?
Re: webserver na ESP8266
Napsal: 11 dub 2021, 09:39
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.
Re: webserver na ESP8266
Napsal: 11 dub 2021, 10:35
od OtikM
Děkuji, zkusím to implementovat.
Vím, že moje programování není elegantní, ale jsem začátečník.
Re: webserver na ESP8266
Napsal: 11 dub 2021, 11:53
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).
Re: webserver na ESP8266
Napsal: 11 dub 2021, 19:43
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>");
Re: webserver na ESP8266
Napsal: 11 dub 2021, 20:18
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>");