Including Javascript in a webpage served by ESPAsyncWebServer

IDE: Arduino 1.8.13
Board: Wemos D1 mini
Reference code source: ESP8266 DHT11/DHT22 Web Server Arduino IDE | Random Nerd Tutorials

I copied the code from the excellent RandomNerd tutorials.
I modified it to use the DHT 11 library that works with Wemos D1 mini.
I uploaded the sketch and tested the hardware, and everything is working as intended.

When I load the webpage, it shows the temperature and humidity, and then refreshes the data asynchronously every 10 seconds.

I thought to add a timer that shows how long until the next data refresh.
I'm doing this using a simple Javascript snippet found on Stackoverflow.

If I implement the Javascript in a standalone HTML page on my local PC, the countdown works.
Inside the sketch, it does not.

I'd like some help understanding a specific issue that arises, rather than a review of the whole code.

The HTML code is embedded in the sketch as follows:

const char index_html[] PROGMEM = R"rawliteral(
... html and javascript here...
)rawliteral";

When I view source on the resulting website, I get the following:

1. var counter=setInterval(timerjs, 1000); //1000 will  run it every 1 second
2. #line 89 "C:\\Users\\brend\\Documents\\Arduino\\digital_thermometer_server_wemos_d1_mini\\digital_thermometer_server_wemos_d1_mini.ino"
3. function timerjs();
4. #line 89 "C:\\Users\\brend\\Documents\\Arduino\\digital_thermometer_server_wemos_d1_mini\\digital_thermometer_server_wemos_d1_mini.ino"
5.  function timerjs()
6. {

Lines 1, 3, 5 and 6 are present in the sketch
Lines 2 and 4 only appear in the output html file
Can anyone explain why lines 2 and 4 appear?
Thanks
Brendon

We would need to see the actual JavaScript for that, i suspect that you have used characters as part of the script that can not be used inside a char* without being referenced specifically, but there may be some other cause. Try and tell us more about the actual code and don't worry about sharing to much.

It looks like the arduino builder dropped a stitch there.

As requested, please post your sketch.

#include <Arduino.h>                    
#include <ESP8266WiFi.h>                
#include <Hash.h>                       
#include <ESPAsyncTCP.h>                
#include <ESPAsyncWebServer.h>          
#include "DHTesp.h"                     
#include <PubSubClient.h>              

const char* ssid = "PLUSNET-F5PN";
const char* password = "3495aeac5dbjh";

AsyncWebServer server(80);

const char* mqtt_server = "192.168.1.96"; 
WiFiClient espClient;                   
PubSubClient client(espClient);

long now = millis();
long lastMeasure = 0;

const int lamp = 13;                    

int light_level;                        

DHTesp dht;                           

float t = 0.0;
float h = 0.0;

unsigned long previousMillis = 0;

const long interval = 10000;

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <style>
    html {
     font-family: Arial;
     display: inline-block;
     margin: 0px auto;
     text-align: center;
    }
    h2 { font-size: 3.0rem; }
    p { font-size: 3.0rem; }
    .units { font-size: 1.2rem; }
    .dht-labels{
      font-size: 1.5rem;
      vertical-align:middle;
      padding-bottom: 15px;
    }
  </style>
  <script>
  var count=10;

var counter=setInterval(timerjs, 1000); //1000 will  run it every 1 second
  function timerjs()
{
  count=count-1;
  if (count <= 0)
  {
     clearInterval(counter);
     return;
  }

 document.getElementById("timerjs").innerHTML=count + " secs"; // watch for spelling
}
</script>
</head>
<body>
  <h2>Remote 1 temperature & humidity</h2>
  <p>
    <i class="fas fa-thermometer-half" style="color:#059e8a;"></i> 
    <span class="dht-labels">Temperature</span> 
    <span id="temperature">%TEMPERATURE%</span>
    <sup class="units">&deg;C</sup>
  </p>
  <p>
    <i class="fas fa-tint" style="color:#00add6;"></i> 
    <span class="dht-labels">Humidity</span>
    <span id="humidity">%HUMIDITY%</span>
    <sup class="units">%</sup>
  </p>
  <p>
  Time to next sample: <span id="timerjs"></span>
  </p>
</body>
<script>
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("humidity").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 10000 ) ;
</script>
</html>)rawliteral";

// Replaces placeholder with DHT values
String processor(const String& var) {
  //Serial.println(var);
  if (var == "TEMPERATURE") {
    return String(t);
  }
  else if (var == "HUMIDITY") {
    return String(h);
  }
  return String();
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(String topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

 if (topic == "remote1/led") {
    Serial.print("Changing Room lamp to ");
    if (messageTemp == "on") {
      analogWrite(lamp, 5);
      Serial.print("On");
    }
    else if (messageTemp == "off") {
      digitalWrite(lamp, LOW);
      Serial.print("Off");
    }
  }
  Serial.println();
}
void reconnect() {

  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");

    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe or resubscribe to a topic
      // You can subscribe to more topics (to control more LEDs in this example)
      client.subscribe("remote1/led");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {

  Serial.begin(115200); 

  dht.setup(D4, DHTesp::DHT11);

  // Connect to Wi-Fi - server
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println(".");
  }

  Serial.println(WiFi.localIP());         // Print ESP8266 Local IP Address - Server

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  pinMode(lamp, OUTPUT);                  // MQTT

  pinMode(A0, INPUT);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", index_html, processor);
  });
  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/plain", String(t).c_str());
  });
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/plain", String(h).c_str());
  });

  // Start server
  server.begin();
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  if (!client.loop())
    client.connect("ESP8266Client");

  now = millis();
  // Publishes new temperature and humidity every 30 seconds
  if (now - lastMeasure > 30000) {
    lastMeasure = now;
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.getHumidity();                 // MQTT - modified for Wemos
    // Read temperature as Celsius (the default)
    float t = dht.getTemperature();                 // MQTT - modified for Wemos
    // Read temperature as Fahrenheit (isFahrenheit = true)
    // float f = dht.getTemperature(true);                 // MQTT - modified for Wemos

    // Check if any reads failed and exit early (to try again).
    //if (isnan(h) || isnan(t) || isnan(f)) {
    if (isnan(h) || isnan(t)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }

    // Computes temperature values in Celsius
    float hic = dht.computeHeatIndex(t, h, false);
    static char temperatureTemp[7];
    dtostrf(hic, 6, 2, temperatureTemp);

    // Uncomment to compute temperature values in Fahrenheit
    // float hif = dht.computeHeatIndex(f, h);
    // static char temperatureTemp[7];
    // dtostrf(hif, 6, 2, temperatureTemp);

    static char humidityTemp[7];
    dtostrf(h, 6, 2, humidityTemp);

    // Publishes Temperature and Humidity values
    client.publish("room/temperature", temperatureTemp);
    client.publish("room/humidity", humidityTemp);

    Serial.print("Humidity: ");
    Serial.print(h);
    Serial.print(" %\t Temperature: ");
    Serial.print(t);
    Serial.print(" *C ");
    // Serial.print(f);
    Serial.print(" *F\t Heat index: ");
    Serial.print(hic);
    Serial.println(" *C ");
    // Serial.print(hif);
    // Serial.println(" *F");
  }

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    float newT = dht.getTemperature();
    if (isnan(newT)) {
      Serial.println("Failed to read from DHT sensor!");
    }
    else {
      t = newT;
      Serial.println(t);
    }

    float newH = dht.getHumidity();
    if (isnan(newH)) {
      Serial.println("Failed to read from DHT sensor!");
    }
    else {
      h = newH;
      Serial.println(h);
    }
  }

  light_level = analogRead(A0);
  char LRDvalue [3];
  // Serial.println(light_level);
  itoa (light_level, LRDvalue, 10);
  client.publish("remote1/light", LRDvalue);
}

Thanks

I've stripped your code down to the rawliteral and an empty setup() and loop() and it's indeed the builder that causes this.

If you enable verbose output during compilation in file -> preferences, you can find the file that actually is compiled
E.g. from

"C:\Program Files (x86)\Arduino\tools-builder\ctags\5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "C:\Users\yourUserName\AppData\Local\Temp\arduino_build_600637\preproc\ctags_target_for_gcc_minus_e.cpp"

In the highlighted directory (it's an example) is a sketch folder that contains a file yourSketch.ino.cpp and that's the file that is compiled. If you open it, you will see the lines that you're refering to.

I think it's a bug in the Arduino builder; logged a bug: #line directives inserted into raw string literal by sketch preprocessor · Issue #1191 · arduino/arduino-cli · GitHub

1 Like

A workaround was given in #line directives inserted into raw string literal by sketch preprocessor · Issue #1191 · arduino/arduino-cli · GitHub.

Place the literal in a separate file, e.g. myRawliteral.h in the same directory as your sketch. Include the file in your sketch.

My simple example sketch

#include "myRawliteral.h"

void setup()
{
}

void loop()
{
}
1 Like

If you want to display "live" data in a web page, doing page refreshes is a really brute-force way to go about it. A better way would be to use Ajax, and periodically update only those fields that have changed, WITHOUT refreshing the whole page. The best way would be to investigate "server sent events", which allows the server to send updates asynchronously when something changes. The latest versions of ESPAsynchWebServer support this, and it is actually easier to implement, and cleaner overall, than using AJAX. There are lots of tutorials available on the web.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.