SPIFFS & Google Charts

I have two Nodemcu/DHT22 sender units in different rooms and each send to a central Nodemcu Receiving unit using espnow. I then created a web server on the Receiving unit that successfully creates a real time graph using SPIFFS (https://randomnerdtutorials.com/esp32-esp8266-plot-chart-web-server/). I wanted to change the real time graphs to Google Gauges and have changed the html file. The html file should be receiving the temperature data (Did not change the sketch) but Google Gauge does not recognize the incoming temperature data. Could anyone please make a suggestion about how to change the html so that Google Gauge can display the temperature data. Thanks.

Arduino Sketch

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <espnow.h>

//Define global variables
float Room1temp;
float Room2temp;
float Tempearture;

//Connection info to Internet Router
const char* ssid = "TG1672G42";
const char* password = "TG1672GD98242";

// declare an object of WebServer library
AsyncWebServer server(80);

//Create structure for incoming Room emp data
typedef struct struct_message {
  int id;
  float temp;
} struc_message;

// Create a struct_message to hold incoming Room sensor readings
struc_message myData;

// Create a structure to hold the readings from each board
struct_message board1;
struct_message board2;

// Create an array with all the structures
struct_message boardsStruct[2] = {board1, board2};

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac_addr, uint8_t *incomingData, uint8_t len) {
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&myData, incomingData, sizeof(myData));
  Tempearture = myData.temp;
  Serial.printf("Board ID %u: %u bytes\n", myData.id, len);
  // Update the structures with the new incoming data
  boardsStruct[myData.id - 1].temp = myData.temp;
}

//Retrive temp from Room 1 and assign as string
String readRoom1temp() {
  Serial.println("Read Room 1 Temp");
  float Room1temp = boardsStruct[0].temp;
  Serial.println(Room1temp);
  return String(Room1temp);
}

//Retrive temp from Room 2 and assign as string
String readRoom2temp() {
  Serial.println("Read Room 2 Temp");
  float Room2temp = boardsStruct[1].temp;
  Serial.println(Room2temp);
  return String(Room2temp);
}

void setup() {
  //Initialize Serial Monitor
  Serial.begin(115200);
  delay(100);

  // Initialize SPIFFS
  if (!SPIFFS.begin()) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  //Set device as a Wi-Fi Station
  //WiFi.mode(WIFI_STA);

  //Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to get recv packer info
  esp_now_register_recv_cb(OnDataRecv);

  // Route for root / web page
  Serial.println("Initialize SPIFFS");
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/index.html");
  });
  Serial.println("Send Room 1 Temp");
  server.on("/Room1temp", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/plain", readRoom1temp().c_str());
  });
  Serial.println("Send Room 2 Temp");
  server.on("/Room2temp", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/plain", readRoom2temp().c_str());
  });

  // Start server
  server.begin();
}

void loop() {
  ///Access the variables for each board and print
  float Room1temp = boardsStruct[0].temp;
  float Room2temp = boardsStruct[1].temp;
  Serial.print("Room 1 temperature: ");
  Serial.print(Room1temp);
  Serial.println();
  Serial.print("Room 2 temperature: ");
  Serial.print(Room2temp);
  Serial.println();

  delay(10000);
}

HTML

<!DOCTYPE html>
<html>
 <head>
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="text/javascript">
    google.charts.load('current', {'packages':['gauge']});
    google.charts.setOnLoadCallback(drawGauge);

    var gaugeOptions = {min: 0, max: 280, yellowFrom: 200, yellowTo: 250,
      redFrom: 250, redTo: 280, minorTicks: 5};
    var gauge;

// Obtain SPIFF Temperature Data
    setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         var Room1temp = parseFloat(this.responseText);
      }
    };
    xhttp.open("GET", "/Room1temp", true);
    xhttp.send();
  }, 10000 ) ;

    setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         var Room2temp = parseFloat(this.responseText);
      }
    };
    xhttp.open("GET", "/Room2temp", true);
    xhttp.send();
  }, 10000 ) ;
 
//Draw Google Gage
    function drawGauge() {
      gaugeData = new google.visualization.DataTable();
      gaugeData.addColumn('number', 'Room 1 Temp');
      gaugeData.addColumn('number', 'Room 2 Temp');
      gaugeData.addRows(2);
      gaugeData.setCell(0, 0, Room1temp);
      gaugeData.setCell(0, 1, Room2temp);

      gauge = new google.visualization.Gauge(document.getElementById('gauge_div'));
      gauge.draw(gaugeData, gaugeOptions);
    }

   </script>
 </head>
 <body>
  <div id="gauge_div" style="width:280px; height: 140px;"></div>
 <body>
</html>

when you do

     if (this.readyState == 4 && this.status == 200) {
         [color=red]var Room2temp[/color] = parseFloat(this.responseText);
      }

you declare a local variable whose scope will be limited to the onreadystatechange function of your XMLHttpRequest

the function drawGauge() expects the variables to be there

     gaugeData.setCell(0, 0, [color=red]Room1temp[/color]);
      gaugeData.setCell(0, 1, [color=red]Room2temp[/color]);

but they are likely unknown (out of scope)

Don't you get javascript errors if you activate the developer mode of your browser ?

to solve this I'd say you need to make Room1temp and Room2temp global variables

<!DOCTYPE html>
<html>
 <head>
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="text/javascript">
    google.charts.load('current', {'packages':['gauge']});
    google.charts.setOnLoadCallback(drawGauge);

    var gaugeOptions = {min: 0, max: 280, yellowFrom: 200, yellowTo: 250,
      redFrom: 250, redTo: 280, minorTicks: 5};
    var gauge;
    var Room1temp = 30;
    var Room2temp = 22;
		
		
// Obtain SPIFF Temperature Data
    setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         Room1temp = parseFloat(this.responseText);
      }
    };
    xhttp.open("GET", "/Room1temp", true);
    xhttp.send();
  }, 10000 ) ;

    setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         Room2temp = parseFloat(this.responseText);
      }
    };
    xhttp.open("GET", "/Room2temp", true);
    xhttp.send();
  }, 10000 ) ;
 
//Draw Google Gage
    function drawGauge() {
      gaugeData = new google.visualization.DataTable();
      gaugeData.addColumn('number', 'Room 1 Temp');
      gaugeData.addColumn('number', 'Room 2 Temp');
      gaugeData.addRows(2);
      gaugeData.setCell(0, 0, Room1temp);
      gaugeData.setCell(0, 1, Room2temp);

      gauge = new google.visualization.Gauge(document.getElementById('gauge_div'));
      gauge.draw(gaugeData, gaugeOptions);
    }

   </script>
 </head>
 <body>
  <div id="gauge_div" style="width:280px; height: 140px;"></div>
 <body>
</html>

I provided defaults values (30° and 22°) so if you run it now you should see


and if your GET XMLHttpRequest works fine, you should see that change.

PS: a max of 280° is pretty high for your room temperature!! I don't want to live there!

Thanks for the suggestion. I added the variables;
var Room1temp;
var Room2temp;
underneath var gauge so should be global. No difference. I then also took the setInterval(function ( ) for Room1ttemp and Room2temp and placed it within the function drawGauge(). No difference.

I also added;

<script language="javascript">
  message = "Room 1 Temp is equal to ";
  document.write (message); 
  document.write (Room1temp); //prints the value of Temp
 </script>

to the section at the end and it indicated that Room1temp was undefined?

What does your arduino answers to http://your.arduino.IP/Room1temp if you try manually?

When I called the http address with the Room1temp as above the temperature is displayed

that’s good news.

in theory that piece

    setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         Room1temp = parseFloat(this.responseText);
      }
    };
    xhttp.open("GET", "/Room1temp", true);
    xhttp.send();
  }, 10000 ) ;

sends that very same GET every 10 seconds and so every 10 seconds the Room1temp variable should get updated.

What you don’t have though is a call to update the gauge with the new data. So may be you could try calling drawGauge(); just after Room1temp = parseFloat(this.responseText);

that would look like this

setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
         Room1temp = parseFloat(this.responseText);
        drawGauge(); // <<==== UPDATE THE GAUGE
      }
    };
    xhttp.open("GET", "/Room1temp", true);
    xhttp.send();
  }, 10000 ) ;

same for the other one.

Thanks for the great suggestions, I tried placing the drawGauge function after the var definition

f (this.readyState == 4 && this.status == 200) {
         var Room1temp = parseFloat(this.responseText);
         drawGauge();
      }

No change. I also tried renaming the setInterval functions to setInterval1() and setInterval2() for each and the called these in the drawGauge function

function drawGauge() {
      setInterval1();
      setInterval2();
      gaugeData = new google.visualization.DataTable();
      gaugeData.addColumn('number', 'Room 1 Temp');
      gaugeData.addColumn('number', 'Room 2 Temp');
      gaugeData.addRows(2);
      gaugeData.setCell(0, 0, Room1temp);
      gaugeData.setCell(0, 1, Room2temp);

Still Google Gauge does not recognize the temp data????

setinterval() should not be messed up with. It tells your browser to call the function given as a parameter every x ms

Here the function uses AJAX - it creates an XMLHttpRequest which is sent to your arduino and waits for the answer to update the value.

So keep that name.

Do you print something from the arduino side when you get a request ? Would be good to know if this is triggered or not

Other thing to do is activate the developer mode of your browser and see what’s going on.

Something else to try. Ask the drawing directly, after you get back the data in the interval function do

gaugeData.setCell(0, 0, Room1temp);
gauge.draw(gaugeData, gaugeOptions);

instead of previous call to drawGauge.