Language: EN


Turn on an LED with PWM by Web form in ESP8266 or ESP32

We continue with the previous tutorial where we saw how to turn on an LED on an ESP8266 and the ESP32 through a web form, expanding the example to add PWM control to vary the intensity.

We will refer to the ESP8266, but the same code is compatible for the ESP32, adjusting the name of the libraries. At the end you have the code for both the ESP8266 and the ESP32.

We have two entries, one about how to receive text, and another on how to interpret the received text to turn on or off an LED, seeing how to use a web form as an easy way to transmit information between the client’s web page and the ESP8266 as a backend.

In this tutorial, we are going to act on an LED in the ESP8266 using PWM to vary the intensity of an LED. This will allow us to show how to interpret numbers and also, we are going to add our first fragment of JavaScript!

These examples dedicated to web forms are very simple, but they allow us to progressively introduce concepts such as Endpoint, Backend, html, JavaScript… which are the basis of what, in future entries, will be called AJAX or Websockets.

And enough with the introduction. Let’s get to work!

Our main program remains exactly the same as in the previous example.

#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>

#include "config.h"  // Replace with your network data
#include "Server.hpp"
#include "ESP8266_Utils.hpp"

void setup(void)


void loop(void)

On the other hand, the ‘Server.hpp’ file is also very similar to the previous one.

AsyncWebServer server(80);

void handleFormPWM(AsyncWebServerRequest *request)
 String pwmValue = request->arg("pwmValue");

 // Not inverted
 //analogWrite(LED_BUILTIN, pwmValue.toInt());
 // Inverted
 analogWrite(LED_BUILTIN, 1023 - pwmValue.toInt());

void InitServer()
  server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");

  server.on("/LED", HTTP_POST, handleFormPWM);

  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(400, "text/plain", "Not found");

    Serial.println("HTTP server started");

We continue to have an Endpoint for ‘/LED’ under POST requests, associated with the callback function ‘handleFormPWM(…)‘. In this function, we read the parameter sent by the form ‘pwmValue’ and convert it to an integer with the ‘toInt()’ method of the String class.

In this example, we invert the received value since the LED integrated on the board has inverted output (HIGH is off, and LOW is on). If you use another PWM output, use the commented code without inversion.

Regarding the ‘index.html’ page located in ‘data’, which will be uploaded to the SPIFFS, it looks like this

<!DOCTYPE html>
<html class="no-js" lang="">
      <meta charset="utf-8">
      <meta http-equiv="x-ua-compatible" content="ie=edge">
      <title>ESP8266 Forms</title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
    <h1>Form example</h1>
    <form action="/LED" method="post">
      <input type="range" id="pwmInput" name="pwmValue" min="0" max="1023" value="500" onchange="updateTextInput(this.value);" >
      <input type="number" id="textInput" onchange="updateTextInput(this.value);" value="500" style="width:50px">
      <label for="pwmInput">PWM</label><br>
      <button type="submit" value="Submit">Send</button>
      function updateTextInput(val) {
        document.getElementById('pwmInput').value = val; 
        document.getElementById('textInput').value = val; 

Where we have a slider ‘pwmInput’ that goes from 0 to 1023 (the PWM range in the ESP8266) and whose default value is 500. We also have a text box ‘textInput’. The user can change the PWM value either using the slider or the text box and both values are ‘linked’.

For this ‘linking’ between the slider and the text box, we add a little JavaScript. In this case, it is so simple that we add it in the ‘.html’ itself. Although normally we would have the JavaScript as independent files, as we will see in future entries.

In this script, we simply have a function updateTextInput(…) where we display the value in the browser console and synchronize the values of the slider and the text box.

On the other hand, we have this function associated with the ‘onchange(…)’ events of both the slider and the textbox. Both functions send the value of their own control.

Finally, upon sending by clicking the “Send” button, the form is sent to the ‘/LED’ Endpoint under the POST method that we have defined in the ESP8266, passing the parameter pwmValue associated with the slider with ‘name=“pwmValue”‘.


If we now access our (yes, once again, very ugly) web page, we will see our slider and our textbox. We can drag the slider or directly write the value we want in the textbox, and we see that the other control is updated correctly.


If we press the send button, we will see the value we have chosen displayed in the browser console and in the serial port of the ESP8266. And, of course, the LED integrated on the board lights up with the intensity we have chosen.


Wasn’t that difficult, right? Well, a simple example in which we are already seeing all the usual parts in a project with ESP8266 (html methods, backend, endpoint, javascript… you can see it, right?)

In the next entry, we will see how to do the same with an RGB LED, and it will be the last entry that we will dedicate to Web forms, to then move on and get into more sophisticated mechanisms like Ajax. See you soon!

Download the code

All the code from this post is available for download on Github.


Version for ESP8266:

Version for ESP32: