ESP32 PWM Slider Web Server – Control LED Brightness

In this ESP32 web server project, we will learn how to build an ESP32 PWM slider web server that will control the brightness of an LED connected to the ESP32 GPIO pin. This will be achieved through an HTML slider that we will add to the web server page. The slider will be moved to set the slider value. In response, this slider value will set the duty cycle of the PWM signal generated by ESP32. In short, this variable duty cycle PWM will control the LED brightness. However, you can also use a DC motor and a stepper motor to control their speed instead of an LED.

ESP32 PWM Slider Web Server Control LED Brightness
  • In a previous article, we also built an ESP32/ESP8266 slider web server. We used the slider to control the onboard LED of the ESP board and turned it ON for a certain number of seconds. We will use the same method to create the slider as we did in that user guide. You can have a look at that article as well:

ESP32/ESP8266 Web Server to Control Outputs with a Timer (Pulse Width)

  • Moreover, you can also go through the recommended reading given below. It is a useful guide in which we used ESP32 PWM to control the brightness of an LED using Arduino IDE. We will be using a similar approach in this guide as well but will include a web server with slider instead to control the brightness.

Recommended Reading: ESP32 PWM with Arduino IDE – LED fading example

ESP32 PWM Slider Web Server Overview

We will create an ESP32 web server based on a slider that will be used to control the brightness of an LED. The web server will consist of a title, “ESP32 Brightness Control Web Server,” a slider to set the value that will alter the duty cycle for PWM and the slider value.

ESP32 Slider Web server (LED brightness control) overview
ESP32 Slider Web Server Overview

We will start by uploading our Arduino sketch to our ESP32 board. In our serial monitor, we will obtain the IP address of our module that will help us access the web server. Copy that IP address in a web browser and press enter. The web server will be displayed.

Now we will move the slider to set the duty cycle. This will generate an HTTP request with the slider value to our ESP32 board. The slider value will also update to a new one as we move the slider. In our program sketch, we will set the slider value range between 0-255 as we will be using 8-bit resolution for optimization. You can set your own range as well. Hence, the slider can take a minimum value of 0 (no PWM) and a maximum of 255 (duty cycle of 100%). When the ESP32 receives the slider value it alters the PWM cycle by setting the duty cycle according to the value. Thus the slider will be used to control the brightness of the LED where the increasing values increase the brightness.

Required Components

For this project we will require the following components:

  1. ESP32 board
  2. LED
  3. 220-ohm resistor
  4. Connecting wires
  5. Breadboard

ESP32 LED Brightness Control Web Server Schematic Diagram

Follow the schematic diagram below, to connect the LED with your ESP32 module:

ESP32 PWM Micropython LED Fading circuit

 In ESP32, PWM is supported through all output pins GPIO only. So choose an appropriate GPIO pin to connect with the anode pin of the LED. We have used GPIO14 as the PWM pin. The cathode pin of the LED is connected with the ground through a 220 ohm resistor.

Setting up Arduino IDE for ESP32 PWM Slider Web Server

We will use Arduino IDE to program our ESP32 development board. Thus, you should have the latest version of Arduino IDE. Additionally, you also need to install the ESP32 plugin. You can visit the link shown below to have a look.

Installing ESPAsyncWebServer Library and Async TCP Library

We will need two libraries to build our ESP32 web server.

  • ESPAsyncWebServer
  • AsyncTCP

The ESPAsyncWebServer library will help us in creating our web server easily. With this library, we will set up an asynchronous HTTP server. AsyncTCP library will also be incorporated as it a dependency for the ESPAsyncWebServer library. All of these libraries are not available in the Arduino library manager. Therefore, we will have to download and load them on our ESP32 board ourselves.

  • To install the ESPAsyncWebServer library for free, click here to download. You will download the library as a .zip folder which you will extract and rename as ‘ESPAsyncWebServer.’ Then, transfer this folder to the installation library folder in your Arduino IDE.
  • To install the Async TCP library for free, click here to download. You will download the library as a .zip folder which you will extract and rename as ‘AsyncTCP.’ Then, transfer this folder to the installation library folder in your Arduino IDE.

Likewise, you can also go to Sketch > Include Library > Add .zip Library inside the IDE to add the libraries as well. After installation of the libraries, restart your IDE.

You can read about ESP32 asynchronous web server here:

Arduino Sketch : ESP32 PWM Slider Web Server

Open your Arduino IDE and go to File > New. A new file will open. Copy the code given below in that file and save it.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "Your_SSID";
const char* password = "Your_Password";

const int led_pin = 14;
String slider_value = "0";

const int frequency = 5000;
const int led_channel = 0;
const int resolution = 8;

const char* input_parameter = "value";

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP32 Brightness Control Web Server</title>
  <style>
    html {font-family: Times New Roman; 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: #38c0ff  ;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background:#01070a; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #01070a; cursor: pointer; } 
  </style>
</head>
<body>
  <h2>ESP32 Brightness Control Web Server</h2>
  <p><span id="textslider_value">%SLIDERVALUE%</span></p>
  <p><input type="range" onchange="updateSliderPWM(this)" id="pwmSlider" min="0" max="255" value="%SLIDERVALUE%" step="1" class="slider"></p>
<script>
function updateSliderPWM(element) {
  var slider_value = document.getElementById("pwmSlider").value;
  document.getElementById("textslider_value").innerHTML = slider_value;
  console.log(slider_value);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?value="+slider_value, true);
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

String processor(const String& var){
  if (var == "SLIDERVALUE"){
    return slider_value;
  }
  return String();
}

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

  ledcSetup(led_channel, frequency, resolution);
  ledcAttachPin(led_pin, led_channel);
  ledcWrite(led_channel, slider_value.toInt());

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

  Serial.println(WiFi.localIP());

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

  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String message;
    if (request->hasParam(input_parameter)) {
      message = request->getParam(input_parameter)->value();
      slider_value = message;
      ledcWrite(led_channel, slider_value.toInt());
    }
    else {
      message = "No message sent";
    }
    Serial.println(message);
    request->send(200, "text/plain", "OK");
  });
  
  server.begin();
}
  
void loop() {
  
}

How the Code Works?

Including Libraries

Firstly, we will include all the necessary libraries which are required for this project. WiFi.h will help in establishing the connection between our ESP32 module to a wireless network. We will also include the libraries which we installed previously, the ESPAsyncWebServer library and the AsyncTCP to build the ESP32 asynchronous web server.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

Setting Network Credentials

Next, we will create two global variables, one for the SSID and the other for the password. These will hold our network credentials which will be used to connect to our wireless network. Replace both of them with your credentials to ensure a successful connection.

const char* ssid = "Your_SSID";
const char* password = "Your_Password";

Setting LED GPIO

We will define the GPIO pin that we will connect with the anode pin of the LED. It is saved in the variable ‘led_pin.’ It is GPIO14 in our case.

const int led_pin = 14;

Defining Slider Value

We will also define the slider value in a string value and set it to 0. Thus, initially when the web server will load you will see 0 as the slider value.

String slider_value = "0";

Defining PWM parameters

First we define the frequency of the signal. We have set it to 5000 Hz. Next, we will define the PWM channel. There are a total of sixteen PWM channels in ESP32. You can use any channel from 0-15. We have set the PWM channel to 0. Additionally, we have set the PWM resolution to 8 bits as it is the optimal resolution to obtain the maximum frequency. Although you can use resolution between 1-16 bits. As we are using 8-bit resolution, thus the duty cycle value will vary between 0-255. Duty cycle defines the width of the signal or ON time of the signal.

const int frequency = 5000;
const int led_channel = 0;
const int resolution = 8;

Setting Input Parameter

We will pass another global variable of type char. This will be the input parameter which we will use. It is is the ‘value’ of the slider also known as the slider value.

const char* input_parameter = "value";

Creating the AsyncWebServer Object

The AsyncWebServer object will be used to set up the ESP32 web server. We will pass the default HTTP port which is 80, as the input to the constructor. This will be the port where the server will listen to the requests.

AsyncWebServer server(80);

Creating the Web page

We will create the index_html variable to store the HTML text. We will start with the title of the web page. The <title> tag will indicate the beginning of the title and the </tile> tag will indicate the ending. In between these tags, we will specify “ESP32 Brightness Control Web Server” which will be displayed in the browser’s title bar.

  <title>ESP32 Brightness Control Web Server</title>

Next, we will create a meta tag to make sure our web server is available for all browsers e.g., smartphones, laptops, computers etc.


<meta name="viewport" content="width=device-width, initial-scale=1">
CSS Styling Web Page

CSS is used to give styles to a web page. To add CSS files in head tags, we will use <style></style> tags to mark the beginning and the end. We will set the display text to font type Times New Roman and align it in the center of the webpage. The font size of the heading and paragraph as indicated by ‘h2’ and ‘p’ will also be set. Additionally, we will also add the styling for the slider that we will display on our web server. The track of the slider will be blue in colour with a black thumb.

<style>
    html {font-family: Times New Roman; 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: #38c0ff  ;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background:#01070a; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #01070a; cursor: pointer; } 
  </style>
HTML Web Page Body

The next step will be to define the HTML web page body. This will go inside the <body></body> tags which mark the beginning and the ending of the script. This part will include the heading of the web page and the paragraphs. We will include the heading of our webpage inside the <h2></h2> tags and it will be “ ESP32 Brightness Control Web Server ”

<h2>ESP32 Brightness Control Web Server</h2>

Then we will include a paragraph for the slider value. This will be found above the slider. You will be able to view the current slider value. This value will be able to configure the PWM duty cycle. It can take values from 0-255.

<p><span id="textslider_value">%SLIDERVALUE%</span></p>

We have used the %SLIDERVALUE% placeholder to denote the current value of the slider. This value will be replaced by the value which we stored in a string variable called ‘slider_value’ that we previously defined. We set its value to 0. You can use any other value according to your preference. So, when the webserver will load the %SLIDERVALUE% placeholder will get replaced by the value ‘0’. This will be the initial current slider value which will be first seen by the user. By moving the slider right and left the user will be able to increase/decrease this value.

Creating Slider
<p><input type="range" onchange="updateSliderPWM(this)" id="pwmSlider" min="0" max="255" value="%SLIDERVALUE%" step="1" class="slider"></p>

Next, we will have another paragraph in which we will create the slider. As you may notice the slider represents an input type in HTML. Thus, we have specified it inside the tag where the user will be able to enter some type of data. Further, the slider is identified as input field type ‘range’. This represents the range of the data that will be associated with the slider. We will call the updateSliderPWM() function and specify it in the onchange property. This will occur when the user will move the slider. Next in the ‘id’ attribute, we will specify ‘pwmSlider’ as the unique id to our slider.

We will use the following properties to specify the range in our slider:

  • min: This corresponds to the minimum value of the data. We are specifying it as 0.
  • max: This corresponds to the maximum value of the data. We are specifying it as 255.
  • value: This corresponds to the slider value which we have specified as %SLIDERVALUE%. Remember this was the placeholder that gets replaced by the string ‘slider_value’ which we set to ‘0’.
  • step: This corresponds to the interval between consecutive numbers. We have set it to 1. Thus values can be incremented/decremented by ±1.

Thus the range of values which is the PWM slider can take are from 0-255.

Updating the Slider Value through updateSliderPWM()

To update the value of the slider we will call the updateSliderPWM() function. This takes in a single argument called ‘element’. Whenever the user will move the slider on the webpage the following function will do its job.

function updateSliderPWM(element) {
  var slider_value = document.getElementById("pwmSlider").value;
  document.getElementById("textslider_value").innerHTML = slider_value;
  console.log(slider_value);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?value="+slider_value, true);
  xhr.send();
}

As you can see above, the current slider value is obtained by referring to the ‘id’ of the slider (pwmSlider) which we specified when creating the slider. This value is then updated to the value saved in the slider_value variable.

var sliderValue = document.getElementById("pwmSlider").value;
document.getElementById("textslider_value").innerHTML = sliderValue;

Inside this function we use the XMLHttpRequest. This will allow us to make an HTTP request in JavaScript.

To make the HTTP GET request to we will follow three steps:

Firstly, we will create an XMLHttpRequest as follows:

var xhr = new XMLHttpRequest();

Secondly, we will initialize the request by using the xhr.open() method. Inside it we will pass on three arguments. The first argument specifies the type of HTTP method which is GET in our case. The second argument is the URL to which are ESP32 will request upon. In our case, it is the /slider?value=slider_value URL. The last argument is true which specifies that the request is asynchronous.

 xhr.open("GET", "/slider?value="+slider_value, true);

Lastly, we will use xhr.send() to open the connection. Our server (ESP32) will now be able to receive the HTTP GET request whenever the slider will be moved.

xhr.send();

processor() function

Inside the processor() function, the placeholder %SLIDERVALUE% will get replaced with value saved in its particular variable (slider_value). This value saved in the ‘slider_value’ variable is then returned.

String processor(const String& var){
  if (var == "SLIDERVALUE"){
    return slider_value;
  }
  return String();
}

setup()

Inside the setup() function, we will open a serial connection at a baud rate of 115200.

Serial.begin(115200);

We will use the following functions to configure PWM in ESP32. First by using ledcSetup(), we will initialize the PWM parameters. This function takes in three parameters. The channel number, frequency, and the resolution of PWM channel. Secondly we will use ledcAttach() to attach the led pin to the channel. Lastly, by using ledcWrite() we will generate the PWM with the duty cycle value accessed from the slider value. The slider value converted to an integer is passed as a second parameter inside the function whereas the first parameter is the channel.

  ledcSetup(led_channel, frequency, resolution);
  ledcAttachPin(led_pin, led_channel);
  ledcWrite(led_channel, slider_value.toInt());

The following section of code will connect our ESP32 board with the local network whose network credentials we already specified above. We will use the WiFi.begin() function. The arguments will be the SSID and the password which we defined earlier in the code. After a successful connection is established, the IP address gets displayed on the web server. We will use this IP address to access our web server.

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

  Serial.println(WiFi.localIP());

ESP32 Handling Requests

In this section, we will discuss how our ESP32 board will handle the requests on the different URLs.

/root URL

Firstly, we will deal with the /root URL request which the ESP32 board will receive.
We will use the send_P() method. The handling function will respond to the client by using the send_P() method on the request object. This method will take in four parameters. The first is 200 which is the HTTP status code for ‘ok’. The second is “text/html” which will correspond to the content type of the response. The third input is the text saved on the index_html variable which will be sent. Finally, the last parameter is the processor function in which the placeholder will be replaced by its current value.

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

Moreover, we will also deal with how the ESP32 board responds to request received on the /slider URL. This will occur whenever the slider will be moved and a new slider value will be generated. This new slider value will get saved in the variable ‘message’. Additionally, the ledcWrite() function will also be called that will generate a PWM signal corresponding to the duty cycle value accessed from the slider_value.toInt(). The value will also get printed on our serial monitor.

  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String message;
    if (request->hasParam(input_parameter)) {
      message = request->getParam(input_parameter)->value();
      slider_value = message;
      ledcWrite(led_channel, slider_value.toInt());
    }
    else {
      message = "No message sent";
    }
    Serial.println(message);
    request->send(200, "text/plain", "OK");
  });

To start the server, we will call begin() on our server object.

server.begin();

Demonstration

Make sure you choose the correct board and COM port before uploading your code to the board. Go to Tools > Board and select ESP32 Dev Module.

select esp32 board

Next, go to Tools > Port and select the appropriate port through which your board is connected.

Selecting COM PORT ESP32

Click on the upload button to upload the code into the ESP32 development board.
After you have uploaded your code to the development board, press its ENABLE button.

ESP32 enable reset button

In your Arduino IDE, open up the serial monitor and you will be able to see the IP address of your ESP32 module.

ESP32 Slider Web server (LED brightness control) demo serial monitor
Serial Monitor

Type that IP address in a web browser and press enter. The web server will open up.

ESP32 Slider Web server (LED brightness control)
Web Server

Now move the slider thumb to the right and the slider value will start increasing. This will generate a PWM signal with the corresponding duty cycle value. Hence, the LED will start getting brighter. Once the value reaches 255, the LED will be brightest. Then start, moving the slider thumb to the left thereby decreasing the slider value. This will cause the LED to fade until it turns OFF when the value reaches 0 again.

Have a look at the demonstration video below:

You may also like to read:

Leave a Comment