Arduino 2.3.2 - website on esp32 with littlefs uploader

Hey guys,

I would like to control a fan via a website.
I did a project a long time ago in which I implemented something like this for an aquarium. My index.html file is still from that time.
Now I would like to call it up in my current sketch.
What works:

  • serial output of the temperature (i.e. the temperature sensor works)
  • fan
  • Output of a website, but not my index.html file (see screenshot)
    This website should be replaced by the index.html file.

I was also able to upload the index file to the esp using the uploader plugin. this also worked. at the same time, I also uploaded a text file for testing and was able to read it out using a test sketch. so the index.html should also exist in the littlefs file system.

How do I call it up in my sketch?

Unbenannt

// Lolin: Fan läuft im Wechsel hoch und runter, Sensor funktioniert
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"

#define LED1 5
#define FAN1 25
#define PWM_CH_LED 0
#define PWM_CH_FAN 1
#define PWM_RES 8
const int MAX_DC = (int)(pow(2, PWM_RES) - 1);
int fanstate = 0;


// GPIO where the DS18B20 is connected to
const int oneWireBus = 33;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
//Variable to store RPM or fan state
//String fanstate = "";

// Variables to store temperature values
String temperatureF = "";
String temperatureC = "";
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

// Replace with your network credentials
const char *ssid = "fritzboxdampf";
const char *password = "mnghfjmghjkgh";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String readDSTemperatureC() {
  // Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
  sensors.requestTemperatures();
  float tempC = sensors.getTempCByIndex(0);

  if (tempC == -127.00) {
    Serial.println("Failed to read from DS18B20 sensor");
    return "--";
  } else {
    Serial.print("Temperature Celsius: ");
    Serial.println(tempC);
  }
  return String(tempC);
}

String readDSTemperatureF() {
  // Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
  sensors.requestTemperatures();
  float tempF = sensors.getTempFByIndex(0);

  if (int(tempF) == -196) {
    Serial.println("Failed to read from DS18B20 sensor");
    return "--";
  } else {
    Serial.print("Temperature Fahrenheit: ");
    Serial.println(tempF);
  }
  return String(tempF);
}
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://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
  <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; }
    .ds-labels{
      font-size: 1.5rem;
      vertical-align:middle;
      padding-bottom: 15px;
    }
  </style>
 
</head>
<body>
  <h2>Hoymiles Temp/Fan Monitor</h2>
  <p>
    <i class="fa-solid fa-temperature-half" style="color:#059e8a;"></i> 
    <span class="ds-labels">Temperature </span> 
    <span id="temperaturec">%TEMPERATUREC%</span>
    <sup class="units">&deg;C</sup>
  </p>
  <p>
    <i class="fa-solid fa-fan" style="color:#059e8a;"></i> 
    <span class="ds-labels">Fan-State</span>
    <span id="temperaturef">%TEMPERATUREF%</span>
    <sup class="units">%</sup>
  </p>
<p>
</p>


<form action="/get">
    Temperatur 50%: <input type="time" name="start50">
    <input type="submit" value="Submit">
</form><br>
<form action="/get">
    Temperatur 100%: <input type="time" name="start100">
    <input type="submit" value="Submit">
</form><br>
<form action="/get">
 Automatik AN: <input type="radio" name="activate" value="on"><br>
 Automatik AUS: <input type="radio" name="activate"  value="off"><br>
     <input type="submit" value="Submit">
</form>
<form action="/get">
  <label for="illumination">Manuelle Steuerung:</label>
  <select id="illumination" name="illumination">
    <option value="0">off</option>
    <option value="25">25%</option>
    <option value="50">50%</option>
    <option value="75">75%</option>
    <option value="100">100%</option>
  </select>
  <input type="submit" value="Submit">
</form>

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

// Replaces placeholder with DS18B20 values
String processor(const String &var) {
  //Serial.println(var);
  if (var == "TEMPERATUREC") {
    return temperatureC;
  } else if (var == "TEMPERATUREF") {
    return temperatureF;
  }
  return String();
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  if (!LittleFS.begin()) {
    Serial.println("An Error has occurred while mounting LittleFS");
    return;
  }

  File file = LittleFS.open("/test_example.txt", "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.println("File Content:");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();

  Serial.println("PWM Program");

  // PWM CH 0 (LED)
  // 5 kHz freq
  // 8 bit resolution
  // attached to GPIO 5
  ledcSetup(PWM_CH_LED, 5000, PWM_RES);
  ledcAttachPin(LED1, PWM_CH_LED);

  // PWM CH 1 (FAN)
  // 5 kHz freq
  // 8 bit resolution
  // attached to GPIO 25
  ledcSetup(PWM_CH_FAN, 5000, PWM_RES);
  ledcAttachPin(FAN1, PWM_CH_FAN);
  delay(3000);
  // kick start the FAN
  ledcWrite(PWM_CH_FAN, MAX_DC);
  Serial.print("MAX_DC: ");
  Serial.println(MAX_DC);
  delay(1000);

  // Start up the DS18B20 library
  sensors.begin();

  temperatureC = readDSTemperatureC();
  temperatureF = readDSTemperatureF();
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send_P(200, "text/html", index_html, processor);
  });
  server.on("/html", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(LittleFS, "/index.html", "text/html");
  });
  server.on("/temperaturec", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send_P(200, "text/plain", temperatureC.c_str());
  });
  server.on("/temperaturef", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send_P(200, "text/plain", temperatureF.c_str());
  });
  // Start server
  server.begin();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    temperatureC = readDSTemperatureC();
    temperatureF = readDSTemperatureF();
    lastTime = millis();
  }
  /*
  if (fanstate == 0) {

    fanON();
  } else {
    fanOFF();
  }

  delay(5000);
*/
}

void fanON() {

  Serial.println("enable fan");
  uint8_t i;
  for (i = 0; i < MAX_DC; i++) {
    ledcWrite(PWM_CH_LED, i);
    ledcWrite(PWM_CH_FAN, i);
    delay(30);
    fanstate = 1;
    Serial.println(i);
  }
}
void fanOFF() {

  Serial.println("disable fan");
  uint8_t i;
  for (i = 255; i > 0; i--) {
    ledcWrite(PWM_CH_LED, i);
    ledcWrite(PWM_CH_FAN, i);
    delay(30);
    fanstate = 0;
    Serial.println(i);
  }
}

What's confusing is that you appear to have two index.html files. One is defined as a character array in PROGMEM. The other is apparently downloaded into your LittleFS file system using the Arduino IDE. I suspect your problem is in part related to confusing these two html's and you're trying to manipulate one, while the other is being served.

The above is just a wild guess based on your somewhat vague problem description, which doesn't make very clear to me what you're trying to do and what problem you're running into exactly.

There are some other structural issues with your sketch, but I admit I've not yet checked diligently. One thing my eye fell on was this:

it's the return statement of your processor() function. You are aware that this will not return the string the function has manipulated, yes?

i'm not so good at programming. i started with the website within the sketch. this is obviously displayed now.
But I want it to call up the index.html file, which I uploaded separately. The previous one could simply be removed if it is not necessary.
sorry, i'm not a native speaker. i hope you understand what i mean now.

by the way my index.html looks like this at the moment.

<!DOCTYPE HTML>
<html>
   <head>
      <title>Fan-Control</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
   <body onload="getActualValues()">
      <form action="/get">
         sunrise: <input type="time" name="start50">
         <input type="submit" value="Submit">
         <span id="start50-actual">xxxx</span> 
      </form>
      <br>
      <form action="/get">
         sunset: <input type="time" name="start100">
        <input type="submit" value="Submit">
        <span id="start100-actual">xxxx</span> 
      </form>
      <br>
      <form action="/get">
         Automatik AN: <input type="radio" name="activate" value="on"><br>
         Automatik AUS: <input type="radio" name="activate"  value="off"><br>
         <input type="submit" value="Submit">
         <br>
         <b><span id="activate-actual">Actual state: xxxx</span></b>
      </form>
      <form action="/get">
         <label for="illumination">Manuelle Steuerung:</label>
         <select id="illumination" name="illumination">
            <option value="0">off</option>
            <option value="25">25%</option>
            <option value="50">50%</option>
            <option value="75">75%</option>
            <option value="100">100%</option>
         </select>
         <input type="submit" value="Submit">
      </form>
      
      <script>      
        function getActualValues() {
          var url = new URL("http://" + `${window.location.hostname}` + "/getValues")
          fetch(url)

          .then(response => response.json())
          .then(data => {
          

OK, I understand now.

Start with a sketch that ONLY serves a static index.html and an index.html that simply says "Hello world" or something like that (doesn't have to contain any html tags). So the first step is to simplify.

Then you can modify/condense this code here:

File file = LittleFS.open("/test_example.txt", "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.println("File Content:");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();

...to fit into the code that serves the html to the client:

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send_P(200, "text/html", index_html, processor);
  });

In the quoted snippet above, it's the variable "index_html" that actually contains the content being sent to the client. So you replace that with the content read from your file.

See if you can make some progress based on these pointers.

Again, the key here is to simplify and break the problem down to only the bit you're trying to figure out. Only once that works, start dressing it up by parsing variables into the html etc.

Thank you very much. I will try this soon. I just have to work a little bit now and in my next break i am going to test it

ok, I did make an easy sketch and gained access to the index.html.

I can open the website by tying this in my browser: http://192.168.178.66/html

#include "LittleFS.h"
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char *ssid = "fritzboxdampf";
const char *password = "hkhkjhkjhjkhj";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

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

  File file = LittleFS.open("/index.html", "r");
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.println("File Content:");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
  // Route for root / web page
  server.on("/html", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(LittleFS, "/index.html", "text/html");
  });
  // Start server
  server.begin();
}

void loop() {
}

it looks like his now, that should be fine.
Unbenannt

I thinkl I have now to figure out how the sketch can change the values at this site and the other way around. Maybe I will have additional questions later.

THank you so much!

Great, good work! I'm glad you made some progress - and especially that the progress was all your own work!

1 Like

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