I have a web server on an ESP32 and a homepage on that server. I would like to update sensor values on the homepage every x seconds automatically (without user input). I cannot access the file system.
The sensor is directly connected to the ESP32. The sensor values are in my C program, stored in variables and updated regularly. Variables are global for easier use.
I thought about Ajax (I have no experience), but all examples and methods I can find use a file to load data from (in the 'url'-part of XMLHttpRequest().open(...url....)). I have no file but only strings in which my HTML and Javascript code is generated and sent to the client.
I have no idea how to update my values and hope for some help.
I thought about trying something like the examples from w3schools, but I don't know how to get the values in there:
Example from another page (I don't use this code - I cannot use files!)
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = this.responseText;
}
};
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();
}
Here is some code from my C program on the ESP:
HTML-String:
String html_document() {
String sHTML;
sHTML = "<!doctype html>";
sHTML +="<html>";
sHTML +="<html lang=\"de\">";
/***************** head ****************/
sHTML +="<head>";
/****** avoid favicon requests ** ** <link rel=\"shortcut icon\" href=\"data:image/x-icon;,\" type=\"image/x-icon\"> **/
sHTML +="<link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\"> ";
sHTML +="<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
//Anpassung an Viewport für unterschiedliche Devices
/***************** title ***************/
sHTML +="<title>LetsgoING IoT</title>";
sHTML +="<style>h1{display: flex; flex-flow: row wrap; justify-content: center;} </style>";
sHTML +="<style>h1{ color: green;}</style>";
sHTML +="<style>h2{display: flex; flex-flow: row wrap; justify-content: center;} </style>";
sHTML +="<style>h2{ color: blue;}</style>";
sHTML +="<style>h5{display: flex; flex-flow: row wrap; justify-content: center;} </style>";
sHTML +="<style>p{display: flex; flex-flow: row wrap; justify-content: center; margin-bottom: 3%;} </style>";
sHTML +="<style>p1{display: flex; flex-flow: row wrap; justify-content: center; margin-bottom: 0%;} </style>";
sHTML +="<style>p2{display: flex; flex-flow: row wrap; justify-content: center; margin-bottom: 0%;} </style>";
sHTML +="</head>";
/***************** body ****************/
sHTML+= "<body>"; //onload=\"window.setInterval(updateDiv, 15000);\"
sHTML+= "<h1>LetsgoING</h1>";
sHTML+= "<h2>Internet der Dinge</h2>";
sHTML+= "<p1><a style=\"width:38%;\"></a><a style=\"width:20%;color: green\">LED ein</a> <a style=\"width:15%;\" href=\"LEDON\"><button> EIN </button></a> </a><a style=\"width:27%;\"> </a></p1>";
sHTML+= "<p><a style=\"width:38%;\"></a><a style=\"width:20%;color: red\" >LED aus</a> <a style=\"width:15%\"; href=\"LEDOFF\"><button>AUS</button></a><a style=\"width:27%;\"> </a></p>";
sHTML+= "<h5>RGB-LED PWM-Werte</h5>";
sHTML+= "<form><p2>";
sHTML+= "<a style=\"width:38%;\"></a> <a style=\"width:20%;color: red\"> Rot</a> <a style=\"width:15%;\" ><input name=\"rot\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" value=\"80\" ></a><a style=\"width:27%;\"> </a>";
sHTML+= "<a style=\"width:38%;\"></a> <a style=\"width:20%;color: green\">Grün</a><a style=\"width:15%;\" ><input name=\"gruen\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" value=\"80\"></a><a style=\"width:27%;\"> </a>";
sHTML+= "<a style=\"width:38%;\"></a> <a style=\"width:20%;color: blue\">Blau</a> <a style=\"width:15%;\" ><input name=\"blau\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" value=\"80\"></a><a style=\"width:27%;\"> </a>";
sHTML+= "</p2>";
sHTML+= "<p><a style=\"width:38%;\"></a> <a style=\"width:20%;\"> </a> <label style=\"width:15%;\" ><input type=\"submit\" value=\"senden\"></label><a style=\"width:27%;\"></a></p>";
sHTML+= "</form>";
sHTML+= "<h5>analoger Schwellwert</h5>";
sHTML+= "<form><p2><a style=\"width:38%;\"></a> <a style=\"width:20%;\"> <input name=\"schwell\" type=\"number\" min=\"0\" max=\"1024\" step=\"10\" value=\"300\"> </a> <a style=\"width:15%;\"><input type=\"submit\" value=\"senden\"></a><a style=\"width:27%;\"> </a></p2>";
//sHTML+= "<p><a style=\"width:28%;\"></a> <a style=\"width:30%;\"> </a> <label style=\"width:15%;\" ><input type=\"submit\" value=\"senden\"></label><a style=\"width:27%;\"></a></p>";
sHTML+= "</form>";
sHTML+= "<h5>PWM-Wert</h5>";
sHTML+= "<form><p2><a style=\"width:38%;\"></a> <a style=\"width:20%;\"> <input name=\"pwm\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" value=\"0\"> </a> <a style=\"width:15%;\"><input type=\"submit\" value=\"senden\"></a><a style=\"width:27%;\"> </a></p2>";
sHTML+= "</form>";
sHTML+= "<h5>Messwerte</h5>";
sHTML+="<p><a style=\"width:38%;\"></a> <p3 id=\"an1\"; style=\"width:20%;\" href=\"anlg1\">#-Wert-#</p3><a style=\"width:22%;\">Analoger Pin 36 </a><a style=\"width:20%;\"></a></p>";
sHTML+="<p><a style=\"width:38%;\"></a> <p3 id=\"an2\"; style=\"width:20%;\">#-Wert-#</p3><a style=\"width:22%;\">Analoger Pin 39 </a><a style=\"width:20%;\"></a></p>";
sHTML+="<p><a style=\"width:38%;\"></a> <p3 id=\"dig\"; style=\"width:20%;\">#-Wert-#</p3><a style=\"width:22%;\">Digitaler Pin 5 </a><a style=\"width:20%;\"></a></p>";
sHTML+= "</body>";
sHTML+= "</html>";
return sHTML;
}
Loop:
void loop() {
if (millis() - startTime >= 2000) {
startTime = millis();
/* Check if a client has connected */
client = server.available();
if (!client){
return;
}
/*Wait for the client to send data */
Serial.println("neuer Client verbunden------------------------------");
/*Count requests: */
request_counter ++;
unsigned long clTimeout = millis()+250;
while(!client.available() && (millis()<clTimeout) ) {
delay(1);
}
/*** publish Homepage ***/
client.print(html_document());
/* Read the first line of the clients request string "sHTML" until carriage return \r */
sHTMLRequest = client.readStringUntil('\r');
#ifdef DEBUGMODE
Serial.println("Antwort: ");
Serial.println(sHTMLRequest);
#endif
client.flush();
/* stop client, if request is empty */
if(sHTMLRequest=="") {
Serial.println("Leere Anfrage! - client gestoppt");
client.stop();
return;
}
#ifdef DEBUGMODE
Serial.println("Antwort2: ");
Serial.println(sHTMLRequest);
Serial.println ("---------");
Serial.print("DEBUG: Remote IP - Address : ");
for (int i = 0; i < 3; i++) {
Serial.print( client.remoteIP()[i]);
Serial.print(".");
}
Serial.println(client.remoteIP()[3]);
Serial.print("Seitenaufrufe: ");
Serial.println(request_counter);
Serial.println ("---------");
#endif
/**** call event handler **********/
eventHandler();
#ifdef DEBUGMODE
Serial.println("Zugewiesene PWM-Werte");
Serial.print("rot: ");
Serial.println(rot);
Serial.print("gruen: ");
Serial.println(gruen);
Serial.print("blau: ");
Serial.println(blau);
#endif
/* write PWM values for colors to channels*/
ledcWrite(1, rot);
ledcWrite(2, gruen);
ledcWrite(3, blau);
#ifdef DEBUGMODE
Serial.println(analog1);
#endif
}
UpdateValues();
}
/**** reads pin values **/
void UpdateValues() {
analog1 = analogRead(pinAnalog1);
analog2 = analogRead(pinAnalog2);
DigiOut = digitalRead(LEDpin);
}
Here's the function that updates the values and creates the JSON string:
void UpdateValues() {
analog1 = analogRead(pinAnalog1);
analog2 = analogRead(pinAnalog2);
DigiOut = digitalRead(LEDpin);
String strJson;
strJson = "(200,\"application/json\",\"{\"pin36\": ";
strJson+=analog1;
strJson+=", \"pin39\":";
strJson+= analog2;
strJson+=", \"pin5\": ";
strJson+=DigiOut;
strJson+="}\")";
server.print(strJson);
}
Option 1:
The simplest way is to ask the page refresh itself every 5 seconds by adding this to <head>
:
sHTML +="<meta http-equiv=\"refresh\" content=\"5\">";
Then change your sHTML
string to concatenate the values of your global variables. Each time the page refreshes it will (should) rebuild the html and return the latest values.
Option 2:
You could use ajax to retrieve the latest values, and then frequently update the small section of the webpage that shows the data.
What happens here is you set up your ESP32 to provide a second URL, one that returns just the latest values as a json object. The values are then injected into the page, overwriting the old.
First add a link to jQuery in the <head>
:
sHTML +="<script src=\"https://code.jquery.com/jquery-3.2.1.min.js\"></script>";
In the <body>
add spans
with ids to hold the values to be updated. Something like:
sHTML+= "<h5>Messwerte</h5>";
sHTML+="<p>Analoger Pin 36</p> <span id='pin36'></span>";
sHTML+="<p>Analoger Pin 39</p> <span id='pin39'></span>";
sHTML+="<p>Analoger Pin 5</p> <span id='pin5'></span>";
Create the sHTML
for the following javascript that makes an ajax request every 5 seconds, and updates the latest values in the browser:
<script>
$(function() {
// request data every 5 seconds
setInterval(requestData, 5000);
function requestData() {
// ajax request for latest sensor data
$.get("/sensors")
.done(function(data) {
console.log(data); // debugging - remove when satisfied
if (data) { // if the returned data is not null, update the values
$("#pin36").text(data.pin36);
$("#pin39").text(data.pin39);
$("#pin5").text(data.pin5);
} else { // a problem occurred
$("#pin36").text("?");
$("#pin39").text("?");
$("#pin5").text("?");
}
}).fail(function() {
console.log("The was a problem retrieving the data.");
});
}
});
</script>
When you detect the string /sensors
in sHTMLRequest
you want to return the following json format:
{"pin36": 5.2, "pin39": 0.322, "pin5": 1}
At this point I don't know enough about your setup to advise too much further, but these links should hopefully assist with the C code: http://www.iotsharing.com/2017/05/how-to-turn-esp32-into-web-server.html & http://randomnerdtutorials.com/esp32-web-server-arduino-ide/
If you use ESP32WebServer.h
from https://github.com/nhatuan84/esp32-webserver (see second half of http://www.iotsharing.com/2017/05/how-to-turn-esp32-into-web-server.html for more details), you can use something like:
server.on("/sensors", handleSensorData);
with
/* this callback will be invoked when user request "/sensors" */
void handleSensorData() {
/* server responds 200 with a json payload */
/* although preferably concatenate your real sensor data here */
server.send(200, "application/json", "{\"pin36\": 5.2, \"pin39\": 0.322, \"pin5\": 1}");
}