8

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);
}
dda
  • 6,030
  • 2
  • 25
  • 34
carousally
  • 545
  • 1
  • 5
  • 11
  • So you have a sensor and you want to send its data to the esp32 webserver? What kind of sensor? Can you connect it directly to the esp32? Does the sensor have wireless capability? etc? – Musa Jun 28 '17 at 18:09
  • see edit above: sensordata is just any analog input value. No wireless sensor. the idea is to use esp's wifi. – carousally Jun 28 '17 at 18:27
  • You are making an ajax call to retrieve `ajax_info.txt`. Can you post the contents of that? – styfle Jun 28 '17 at 20:18
  • see edit: this is not any code I use, just an example from another page. i have no files and cannot use files. – carousally Jun 28 '17 at 20:19
  • I just cannot figure out how to get my values from the server to the hompage and updated regularly. I can only use a string in which i write the (html, javascript) code and I have no file with the values but variables in my c-program. I cannot create a file. – carousally Jun 29 '17 at 17:49
  • why can't you create a file? it should come with SPIFFS, then you can use `server.onNotFound` to fetch it – dandavis Jul 06 '17 at 20:41
  • As far as I know SPIFFS is not yet supported for ESP32: see: https://arduino.stackexchange.com/a/38475/33723 – carousally Jul 08 '17 at 23:20

1 Answers1

8

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>&nbsp;<span id='pin36'></span>";
sHTML+="<p>Analoger Pin 39</p>&nbsp;<span id='pin39'></span>";
sHTML+="<p>Analoger Pin 5</p>&nbsp;<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}"); 
}
K Scandrett
  • 16,390
  • 4
  • 40
  • 65
  • This looks great I'll try this out as soon as I can! – carousally Jul 05 '17 at 20:41
  • Hello K Scandrett, is it possible that this code only works when connected to the internet? (ESP32 runs in AP mode and without internet connection) For that part: "....https://code.jquery.com/jquery-3.2.1.min.js ..." i get an error message in the browser: "Failed to load resource: net::ERR_NAME_NOT_RESOLVED -------- jquery-3.2.1.min.js " – carousally Jul 21 '17 at 07:53
  • 1
    Yes that will be the case, I did assume the computer you are connecting from has an internet connection. I can't respond properly right now, but will try to do so tomorrow – K Scandrett Jul 21 '17 at 08:39
  • Here's the updated html & JavaScript that doesn't require an internet connection - http://plnkr.co/edit/7DyTmdRdWSIRxx7ESLNv?p=info. I'm not sure in your case if you need to use `xhr.open('GET', 'sensors');` or `xhr.open('GET', '/sensors');`, so you may need to adjust accordingly – K Scandrett Jul 24 '17 at 10:05
  • Make sure the script is placed at the bottom of the html, as I have it, or it may not work. In this Plunker, Index.html is making an ajax request to the file "sensors" to retrieve the dummy data. It does this immediately, and then every 5 seconds (retrieving the same dummy values each time) – K Scandrett Jul 24 '17 at 10:18
  • Thank you very much for your code!! I get an error message in the browser: " Uncaught SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse () at XMLHttpRequest.xhr.onload (sensors?pwm=12:1) " I searched for the section in the code but cannot see why there's an error produced (googled it and found it can be bad interpreted html...??) – carousally Jul 24 '17 at 20:40
  • One more thing: you say the code in Plunker makes a request to a FILE "sensors". since i don't have files, does this also work with a funktion that updates the values in c code? I edit the original post adding that code I use. Its in the function: UpdateValues() – carousally Jul 24 '17 at 20:43
  • The error message means that it wasn't receiving correctly formatted JSON. What you can do is open the web browser and go to `[your url]/sensors` to see what that page returns ([your url] part being however you access the ESP32). It should just return what you see in the plunker sensor file: `{"pin36": ...}`. Re: "File" I was just referring to Plunker, the same code will work in your case. Ajax just makes a request to [your url]/sensors. It doesn't matter how the server responds - from an actual file or by running code to create the JSON object. – K Scandrett Jul 24 '17 at 21:50
  • 1
    Note that `server.send(20, "application/json", ...)` only works if you're using `ESP32WebServer.h`. If you're not using it, then it will look more like `s = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n ...` – K Scandrett Jul 24 '17 at 22:01