WeMOS POST metode
| WeMOS WiFi Processor | |
|---|---|
| WeMOS | Server Connection - Simpel Server - Flere SSID - ESP8266 - Watchdog - Interrupt |
| Anvendelse | LED-eksempel - AD-eksempel - Mail - POST metode - Web Publicering |
Den simple måde at sende data til WeMOS er at skrive dem med i URL'en, men det giver sikkerhedsproblemer, og det begrænser også noget hvad man kan sende.
At skrive data i URL'en lægger op til GET-metoden. Hvis man vil undgå det, så skal man bruge POST-metoden.
Dette eksempel tager udgangspunkt i et eksempel fundet ved techtutorialsx.com[1].
Formålet er at opbygge en kode, så man kan sende forskellige ting ind til siden ved hjælp af siden selv.
Hele den gennemgåede kode kan findes i Denne ZIP-fil
Variabler og biblioteker
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
/*
const char* ssid = "ssid-name";
const char* password = "ssid-password";
*/
#include "WifiHome.h"
boolean LED = LOW;
int ledPin = 2;Server Connection
Til at begynde med sættes LED'en lige op og der klargøres til at skrive ud i Serial Monitor.
Som beskrevet i WeMOS Server Connection forbindes til Wifi og der sættes en server op. Det er primært i setup() det sker.
Derefter skrives en hjælpetekst til brugeren:
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
Serial.begin(115200);
WiFi.begin(ssid, password); //Connect to the WiFi network
while (WiFi.status() != WL_CONNECTED) { //Wait for connection
delay(500);
Serial.println("Waiting to connect...");
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //Print the local IP
Serial.print("Page Address: http://");
Serial.print(WiFi.localIP());
Serial.println("/home"); //Print the local IP
}Det næste er lidt specielt for denne måde at håndtere server på, hvor der etableres forbindelse mellem en del af URL'en (home) og en funktion der servicerer den henvendelse. Det gøres med server.on metoden. :
server.on("/home", handleHome); //Associate the handler function to the pathTil slut i setup() aktiveres serveren, så man kan henvende sig til den:
server.begin(); //Start the server
Serial.println("Server listening");
}Connection response
Det eneste der sker i loop() er at der spørges til om en client har henvendt sig med noget. Det er det der sikrer at handleHome bliver kaldt:
void loop() {
server.handleClient(); //Handling of incoming requests
}Servicering af home henvendelsen
Først sættes det første af svaret til clienten op, som en start på HTML-koden der skal vises:
void handleHome() { //Handler for the body path
String message = "<!DOCTYPE HTML>";
message += "<html>";Som det øverste på siden angives om der er sket en henvendelse til siden, og hvis der er, så angives indholdet af henvendelsen:
if (server.hasArg("tekst")== false){ //Check if body received
message += "Ingen data fra afsender<br />";
} else {
message += "Der er sendt teksten:";
message += server.arg("tekst");
message += "<br />";
message += "LED saettes til: ";
message += server.arg("led");
message += "<br />";
}Hvis der er sendt et argument med i led-feltet, så sættes LED efter det der sendes, og LED'en sættes til det den nu er blevet:
if (server.arg("led") == "ON") {
LED = HIGH;
} else if (server.arg("led") == "OFF") {
LED = LOW;
}
digitalWrite(ledPin, ! LED); // LED tændes ved at trække lavt, derfor inverteringenLED'ens status skrives så i indholdet hvad status er på LED'en:
message += "LED er ";
if(LED == HIGH) {
message += "On<br />";
} else {
message += "Off<br />";
}Som det sidste indhold sættes en HTML-FORM op, så man faktisk kan sende noget til serveren - der er to input-texter, en til en tekst, der bares skrives i henvendelsen, og så en til led, der kan tænde og slukke LED'en med ON og OFF - det skal skrives med stort, ellers ignoreres det. Til slut sendes indholdet tilbage til clienten:
message += "<br />";
message += "<form action='/home' method='POST'>";
message += "Input tekst:<input type='text' name='tekst' value='Test af POST'><br>";
message += "LED ON/OFF:<input type='text' name='led' value='ON'><br>";
message += "<input type='submit' value='Submit'></form>";
message += "</html>";
server.send(200, "text/html", message);
}Test af POST-metoden
Ved en simpel henvendelse til serveren med URL: "http://192.168.0.14/home" får man følgende visning:
![]()
Response på en simpel henvendelse
Hvis man blot klikker Submit får man følgende visning og LED'en tændes:
![]()
Response på en henvendelse med ON til LED'en
får man følgende visning:
![]()
Response på en henvendelse med OFF til LED'en
Videre udvikling med flere undersider
Hvis man ønsker at WeMOS'en skal kunne håndtere mere komplekse ting, så kan man lave forskellig respons ved at lave forskellige undersider. Det gøres ved at der etableres ekstra forbindelse til URL'en (fx activate) og en funktion handleActivate() der servicerer den henvendelse. Det gøres ved at lave to kald til server.on metoden. :
server.on("/home", handleHome); //Associate the handler function to the path
server.on("/activate", handleActivate); //Associate the handler function to the pathFor at man kan komme til at bruge siden activate, og måske sende noget til den, så etableres en ekstra HTML-form inde i handleHome funktionen. Den skal have en ny action der henviser til activate som viste her:
message += "<form action='/activate' method='POST'>";
message += "Akteverings-tekst:<input type='text' name='ac' value='Start'><br>";
message += "<input type='submit' value='Aktiver'></form>";Der skal så også oprettes den handleActivate, der giver responsen på activate siden - her er den bare lavet uden at den laver noget, men det viser princippet med at man kan komme tilbage til home-siden
void handleActivate() { //Handler for the acticate path
String message = "<!DOCTYPE HTML>";
message += "<html>";
if (server.hasArg("ac")== false){ //Check if activate received
message += "Ingen data fra afsender<br />";
} else {
message += "Der er sendt Aktiveringsteksten:";
message += server.arg("ca");
message += "<br />";
}
message += "Det laver ikke noget i programmet.<br />";
message += "<form action='/home' method='POST'>";
message += "<input type='submit' value='Retur'></form>";
message += "</html>";
server.send(200, "text/html", message);
}Udvidelse med løbende opdatering af siden
Denne udvidelse tager udgangspunkt i WeMOS AD-eksempel, men bliver modificeret så det passer ind i strukturen med ESP8266WebServer biblioteket.
I setup() tilføjes en handler til den henvendelse der skal kunne opdatere en værdi på siden - her en AD-værdi:
server.on("/ad", handleAD); //Associate the handler function to the pathTil handleren skal der være en handleAD() funktion der blot skal svare med AD tallet i plain text:
void handleAD() { //Handler for the ad path
String message = String(analogRead(A0)); // Svaret er bare AD-tallet som tekst
server.send(200, "text/plain", message);
}Inde i visningen af siden skal man have tilføjet et felt, hvor værdien skal vises - her vises AD-værdien, så den får en visning fra starten, og span-tagget får et ID='demo', så AJAX funktionaliteten kan udskifte indholdet i dette felt:
message += "<p>AD-tallet: <span id='demo'>";
message += String(analogRead(A0));
message += "</span></p>";I starten af siden tilføjes et head-tag, der indeholder en javascript-funktion, der kan foretage en HTTP Request, som beder om AD-siden. Når den returnerer AD-værdien, så sætter funktionen den AD-værdi ind i span-tagget med ID'et demo.
message += "<head>";
message += "<script>";
message += "function loadDoc() {";
message += " var xhttp = new XMLHttpRequest();";
message += " xhttp.onreadystatechange = function() {";
message += " if (this.readyState == 4 && this.status == 200) {";
message += " document.getElementById('demo').innerHTML =";
message += " this.responseText;";
message += " }";
message += " };";
message += " xhttp.open('GET', 'ad', true);";
message += " xhttp.send();";
message += "}";
message += "</script>";
message += "</head>";Endelig skal den sidste del af AJAX-teknikken etableres ved at der indføres et javascript på siden, som opdaterer siden ved at loadDoc() funktionen kaldes hvert sekund:
message += "<script>";
message += "setInterval(loadDoc, 1000)";
message += "</script>";Opdatering af Funktionalitet med forms
Hvis man gerne vil lave en form-visning afhængig af en værdi i WeMOS'en, så er det lidt mere tricky, for så kan man ikke bare smide HTML-koden ind på siden, når tilstanden er der - det vil gribe forstyrrende ind i indtastningen af værdien. Dette skal løses med en lidt anden teknik.
Her laves således at man kan indtaste et setpunkt på hvornår formen skal vises, så man kan indtaste hvis AD-værdien er over setpunktet, og formen er ikke vist hvis AD-værdien er under setpunktet.
Her skal laves en henvendelse der angiver om formen skal vises. Der skal etableres en handler i setup():
server.on("/visning", handleVisning); //Associate the handler function to the pathHertil skal der så være en handler-funktion handleVisning(), der svarer om formen skal vises:
void handleVisning() { //Handler for the visning path
String message = " ";
if (analogRead(A0) >= setpunkt) {
message += "Vis"; // Svaret er bare Vis
}
server.send(200, "text/plain", message);
}Der skal tilføjes en variabel der indeholder setpunktet, og den initialiseres til 512, som er midt i AD-området:
int setpunkt = 512;Sammen med javascript-funktionen loadDoc() etableres en ny loadDoc2():
message += "function loadDoc2() {";
message += " var xhttp2 = new XMLHttpRequest();";
message += " xhttp2.onreadystatechange = function() {";
message += " if (this.readyState == 4 && this.status == 200) {";
message += " if (this.responseText.indexOf('Vis') > -1) {";
message += " document.getElementById('setForm').style.display = 'block';";
message += " } else {";
message += " document.getElementById('setForm').style.display = 'none';";
message += " }";
message += " }";
message += " };";
message += " xhttp2.open('GET', 'visning', true);";
message += " xhttp2.send();";
message += "}";Sammen med håndteringen af de andre argumenter på siden laves en håndtering af setp-argumentet fra formen med Setpunkt. Der skal også beskyttes mod at man sætter for højt et setpunkt, så man kan komme til at vise formen igen:
if (server.hasArg("setp") == true) {
setpunkt = server.arg("setp").toInt();
if (setpunkt >= 1024) {
setpunkt = 512;
}
}Under visningen af LED'en status laves en visning af hvad setpunkt variablen indeholder:
message += "Setpunkt er : ";
message += String(setpunkt, DEC);
message += "<br />";Mellem de andre forms placeres en ny form, der har et id='setForm', for at man kan henvende sig til den fra loadDoc2(), og den startes med visningen slået fra ved at sætte style til display=none, så man ikke lige får visningen ved første henvendelse, men først når det er konstateret om AD-værdien er over setpunktet:
message += "<form action='/home' method='POST' id='setForm' style='display=none'>";
message += "Visnings-setpunkt (1 - 1024):<input type='text' name='setp' value='512'><br>";
message += "<input type='submit' value='Nyt Setpunkt'></form>";Visningen i browseren når AD-tallet er over setpunktet er som vist her:
![]()
Visningen i browseren af webserveren bygget med WeMOS
Udvidelse med sending af mail ved ændring af setpunkt
For at kunne sende information skal der tilføjes en HTTP Client som et bibliotek:
#include <ESP8266HTTPClient.h>Det skal oprettes en instans af HTTP Cienten, angives en URL hvor der skal sendes og en variabel:
HTTPClient http;
const String serverAddress = "http://htx-elev.ucholstebro.dk/HX-17-el/bar/"; // web address for the webserver
int httpCode;Der hvor man læser setpunktet ind fra formen og ændrer setpunkt-variablen skal man sende den henvendelse til siden, der kan sende mail som det er beskrevet i WeMOS Mail.
String stringToSend = serverAddress; // Serveradressen der skal henvendes til
stringToSend += "sendMailSetpunkt.php?setpunkt="; // Filnavnet og parameter-navn
stringToSend += String(setpunkt); // Indholdet der skal sendes
http.setTimeout(2000); // Use 2 seconds timeout for HTTP connection
http.begin(stringToSend); // Start HTTP connection and send HTTP header
httpCode = http.GET(); // Get response from HTTP request
digitalWrite(ledPin, !digitalRead(ledPin));
if(httpCode > 0) // httpCode will be negative on error
{
Serial.printf("[HTTP] GET... code: %d\n", httpCode); // Response from server is not an error
if(httpCode == HTTP_CODE_OK) { // Response from server is HTTP CODE 200
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}For at det skal fungere, så skal der selvfølgelig ligge den PHP-fil der henvises til, og den skal kunne sende en mail. Her er det lavet så det er en fast mailadresse den bruger, så vær lige venlig ikke at bruge samme adresse (jeg videresender ikke). Indholdet af PHP-filen ser ud som følger:
<?php
/*
* Mail example
* Bent Arnoldsen, Holstebro HTX 2018
*/
if (isset($_GET['setpunkt'])) {
$setpunkt = $_GET['setpunkt'];
mail("bar@ucholstebro.dk", "Mail fra Alarmsystem", "Der er sat nyt setpunkt : " . $setpunkt);
}
?>
<html>
<h2>Mail-System til Setpunkt</h2>
</html>Den samlede kode med alle udvidelserne sammen kan hentes i denne ZIP-fil.