This is an old revision of the document!


Lab 3: Web servers

In this project we'll program the ESP32 Sparrow board to act as a web server on your local network and deliver a simple web page where you can control the on-board RGB led. The web server is mobile responsive and can be accessed with any device that can open a browser in the local network.

So, as a general outline and plan of what you will achieve during this lesson:

  • the web server controls the three individual channels (red, green & blue) of the RGB led
  • you will be able to access the web page served by the board using its IP address in your local network
  • you will expand this simple example to also print in the web page values from the on-board sensors

Run this example code on your board, but replace the SSID and PASSWORD fields with your own.

Test the code works in your phone's or computer's browser and switch on/off the LEDs.

Assignment

Modify the code to control the RGB led in all its colors from the web browser. You will need to replace each button with a text box in which you will input the brightness value for each of the three channels. The brightness needs to be an integer from 0 to 255. Use the analogWrite(pin, value) function to control the PWM channels on the GPIO pins.

SPIFFS Webserver

The previous web server example works well, but it's not a good practice to hard-code HTML directly into C code, as it makes things hard to debug and doesn't scale well if we want to create more elaborate web pages. Luckily we can use the 4MB on-board Flash memory of the ESP32 module to store separate files and access them through the SPIFFS.

You will need to add a plugin to your Arduino IDE (at this moment works only with the 1.x versions of the IDE). Please use this tutorial to install the plugin.

Async Web Server Libraries

From now on, we will build web servers that use the following libraries: ESPAsyncWebServer and AsyncTCP.

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.

RGB Color Picker

Now, let's build a new webserver that shows a slider control which you can use to change the color of one of the on-board leds.

This is how the project should work:

  • The ESP32 web server displays the slider control page
  • When you slide to a certain value, your browser makes a request on a URL that contains the intensity parameter
  • Your ESP32 receives the request and parses it
  • Then, it sends a PWM signal with the corresponding value to the GPIO that is controlling the led

First step is to download the sketch below and save it in a folder named “webserver”.

webserver.ino
// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "SPIFFS.h"
 
// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
 
const int output = 14; //red led
 
String sliderValue = "0";
 
// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
 
const char* PARAM_INPUT = "value";
 
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
 
// Replaces placeholder with button section in your web page
String processor(const String& var){
  Serial.println(var);
  /*if (var == "SLIDERVALUE"){
    return sliderValue;
  }*/
  return String();
}
 
void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
 
   // Initialize SPIFFS
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  // configure LED PWM functionalitites
  ledcSetup(ledChannel, freq, resolution);
 
  // attach the channel to the GPIO to be controlled
  ledcAttachPin(output, ledChannel);
 
  ledcWrite(ledChannel, sliderValue.toInt());
 
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());
 
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
     request->send(SPIFFS, "/index.html", String(), false, processor);
 
  });
 
  // Send a GET request to <ESP_IP>/slider?value=<inputMessage>
  server.on("/slider_r", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
    if (request->hasParam(PARAM_INPUT)) {
      inputMessage = request->getParam(PARAM_INPUT)->value();
      sliderValue = inputMessage;
      Serial.println(sliderValue.toInt());
      ledcWrite(ledChannel, 255 - sliderValue.toInt());
    }
    else {
      inputMessage = "No message sent";
    }
 
    request->send(200, "text/plain", "OK");
  });
 
  // Start server
  server.begin();
}
 
void loop() {
 
}

This is the index.html file that the webserver will send anytime a client connects to it. Download the file and save it in your current sketch folder in a sub-folder named “data”. So, the path to it should be (..)/webserver/data/index.html

index.html
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP Web Server</title>
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 2.3rem;}
    p {font-size: 1.9rem;}
    body {max-width: 400px; margin:0px auto; padding-bottom: 25px;}
    .slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #FFD65C;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; } 
  </style>
</head>
<body>
  <h1>ESP32 Web Server</h1>
  <h3>Red Channel</h3>
  <p><span id="textSliderValueR">%SLIDERVALUE_R%</span></p>
  <p><input type="range" onchange="updateSliderR(this)" id="pwmSlider_r" min="0" max="255" value="%SLIDERVALUE_R%" step="1" class="slider"></p>
  <h3>Green Channel</h3>
  <p><span id="textSliderValueG">%SLIDERVALUE_G%</span></p>
  <p><input type="range" onchange="updateSliderG(this)" id="pwmSlider_g" min="0" max="255" value="%SLIDERVALUE_G%" step="1" class="slider"></p>
  <h3>Blue Channel</h3>
  <p><span id="textSliderValueB">%SLIDERVALUE_B%</span></p>
  <p><input type="range" onchange="updateSliderB(this)" id="pwmSlider_b" min="0" max="255" value="%SLIDERVALUE_B%" step="1" class="slider"></p>
<script>
function updateSliderR(element) {
  var sliderValue = document.getElementById("pwmSlider_r").value;
  document.getElementById("textSliderValueR").innerHTML = sliderValue;
  console.log(sliderValue);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider_r?value="+sliderValue, true);
  xhr.send();
}
function updateSliderG(element) {
  var sliderValue = document.getElementById("pwmSlider_g").value;
  document.getElementById("textSliderValueG").innerHTML = sliderValue;
  console.log(sliderValue);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider_g?value="+sliderValue, true);
  xhr.send();
}
function updateSliderB(element) {
  var sliderValue = document.getElementById("pwmSlider_b").value;
  document.getElementById("textSliderValueB").innerHTML = sliderValue;
  console.log(sliderValue);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider_b?value="+sliderValue, true);
  xhr.send();
}
</script>
</body>
</html>
Assignment

Modify the webserver sketch to receive data from the other two sliders in the webpage and control the other two PWM channels (13 - green and 15 - blue).

Live charts

 Light plot displayed automatically by the server

Next, we'll use the data provided by the on-board sensors to generate dynamic web pages that plot sensor reading in real-time.

We will use the LTR308 light sensor to supply the light intensity values that we would want to display in the web page in a plot.

The way this works is similar to the previous examples:

  • Compile and upload the webserver.ino sketch to your board, it will automatically connect and get an IP address you can connect to from your browser
  • Upload the below index.html file to your board's Flash memory
  • The code creates a webserver that runs a JS script from the highcharts library
  • The setInterval() function will automatically query the server every 3 seconds for the light value
webserver.ino
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <LTR308.h>
#include <Wire.h>
 
LTR308 light;
unsigned char ID;
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
 
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
 
void initLight(){
  light.begin();
  // To start taking measurements, power up the sensor
 
  light.setPowerUp();
 
  // Allow for a slight delay in power-up sequence (typ. 5ms from the datasheet)
  delay(10);
  light.setGain(0);
  light.setMeasurementRate(0, 3);
  }
 
String readLight(){
  unsigned long rawData;
  double lux;    // Resulting lux value
  boolean good;  // True if sensor is not saturated
 
  light.getData(rawData);
  good = light.getLux(0, 0, rawData, lux);
  if(good) 
    return String(lux);
  else { 
    Serial.println("Failed to read light sensor!");
    return "-1";
  }
 }
 
 
void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
 
  light.begin();
  if (light.getPartID(ID)) {
    Serial.print("Got Sensor Part ID: 0X");
    Serial.print(ID, HEX);
    Serial.println();
  }
  else {
    Serial.println("Could not initialize LTR308 sensor");
    while(1);
  }
  initLight();
 
  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
 
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());
 
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  server.on("/light", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readLight().c_str());
  });
 
  // Start server
  server.begin();
}
 
void loop(){
 
}
index.html
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.highcharts.com/highcharts.js"></script>
  <style>
    body {
      min-width: 310px;
    	max-width: 800px;
    	height: 400px;
      margin: 0 auto;
    }
    h2 {
      font-family: Arial;
      font-size: 2.5rem;
      text-align: center;
    }
  </style>
</head>
<body>
  <h2>ESP Weather Station</h2>
  <div id="chart-light" class="container"></div>
</body>
<script>
var chartL = new Highcharts.Chart({
  chart:{ renderTo : 'chart-light' },
  title: { text: 'LTR308 Light Intensity' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Illumination (lux)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartL.series[0].data.length > 40) {
        chartL.series[0].addPoint([x, y], true, true, true);
      } else {
        chartL.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/light", true);
  xhttp.send();
}, 3000 ) ;
 
</script>
</html>
Assignment

Modify the above example to add temperature, humidity and pressure readings from the BME680 sensor.

iothings/laboratoare/2022/lab3.1698076926.txt.gz · Last modified: 2023/10/23 19:02 by dan.tudose
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0