ESP8266 NodeMCU Web Server (WebSocket) with Multiple Sliders: Control LEDs Brightness (PWM)

This tutorial shows how to build a web server with the ESP8266 NodeMCU board that displays a web page with multiple sliders. The sliders control the duty cycle of different PWM signals to control the brightness of multiple LEDs. Instead of LEDs, you can use this project to control DC motors or other actuators that require a PWM signal. The communication between the clients and the ESP8266 is done using the WebSocket protocol. Additionally, whenever there’s a change, all clients update their slider values simultaneously.

ESP8266 Web Server WebSocket with Multiple Sliders: Control LEDs Brightness PWM

You can also modify the code presented in this tutorial to add sliders to your projects to set threshold values or any other values you need to use in your code.

For this project, the ESP8266 board will be programmed using the Arduino core. You can either use the Arduino IDE, VS Code with PlatformIO, or any other suitable IDE.

To better understand how this project works, we recommend taking a look at the following tutorials:

* This project shows how to build a web server with one slider, but it uses HTTP requests—in this tutorial, we’ll use WebSocket protocol.

We have a similar tutorial for the ESP32 board:

Project Overview

The following image shows the web page we’ll build for this project:

ESP8266 NodeMCU Web Server WebSocket Project overview sliders PWM
  • The web page contains three cards;
  • Each card has a paragraph to display the card title (Fader 1, Fader 2, Fader 3);
  • There’s a range slider in each card that you can move to set the brightness of the corresponding LED;
  • In each card, another paragraph displays the current LED brightness (in percentage);
  • When you set a new position for the slider, it updates all clients (if you have multiple web browser tabs opened (or multiple devices), they update almost simultaneously whenever there’s a change).

How it Works?

  • The ESP hosts a web server that displays a web page with three sliders;
  • When you set a new position for a slider, the client sends the slider number and slider value to the server via WebSocket protocol. For example, if you set slider number 3 to position number 40, it would send this message 3s40 to the server.
ESP8266 NodeMCU Web Server Multiple Sliders How it Works
  • The server (ESP) receives the slider number and corresponding value and adjusts the PWM duty cycle accordingly. Additionally, it also notifies all the other clients with the new current slider values—this allows us to have all clients updated almost instantaneously.
ESP8266 NodeMCU Slider Web Server Notify All Clients Websocket
  • The ESP8266 outputs the PWM signal with the corresponding duty cycle to control the LED brightness. A duty cycle of 0% means the LED is completely off, a duty cycle of 50% means the LED is half-lit, and a duty cycle of 100% means the LED is lit;
ESP8266 NodeMCU Brightness duty cycle web server PWM example
  • Whenever you open a new web browser window (this is when a new client connects), it will send a message to the ESP8266 (also through WebSocket protocol) with the message getValues. When the ESP8266 gets this message, it sends the current slider values. This way, whenever you open a new tab, it always shows the current and updated values.
ESP8266 NodeMCU Multiple Sliders Web Server New Client Update Values

Prerequisites

Before proceeding with this tutorial, make sure you check all the following prerequisites.

1) Parts Required

To follow this project you need:

You don’t need three LEDs to test this project, you can simply see the results in the Serial Monitor or use other actuators that required a PWM signal to operate.

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

2) Arduino IDE and ESP8266 Boards Add-on

We’ll program the ESP8266 using Arduino IDE. So, you must have the ESP8266 add-on installed. Follow the next tutorial if you haven’t already:

If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP8266:

3) Filesystem Uploader Plugin

To upload the HTML, CSS, and JavaScript files needed to build this project to the ESP8266 flash memory (LittleFS), we’ll use a plugin for Arduino IDE: LittleFS Filesystem uploader. Follow the next tutorial to install the filesystem uploader plugin if you haven’t already:

If you’re using VS Code with the PlatformIO extension, read the following tutorial to learn how to upload files to the filesystem:

4) Libraries

To build this project, you need to install the following libraries:

You can install the first library using the Arduino Library Manager. Go to Sketch Include Library > Manage Libraries and search for the library name.

The ESPAsyncWebServer and ESPAsynTCP 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 SketchInclude Library > Add .zip Library and select the libraries you’ve just downloaded.

Installing Libraries (VS Code + PlatformIO)

If you’re programming the ESP8266 using PlatformIO, you should add the following lines to the platformio.ini file to include the libraries (also change the Serial Monitor speed to 115200 and set the littleFS filesystem):

monitor_speed = 115200
board_build.filesystem = littlefs
lib_deps = ESP Async WebServer
  arduino-libraries/Arduino_JSON @ 0.1.0

Schematic Diagram

Wire three LEDs to the ESP8266. We’re using GPIOs 12 (D6), 13 (D7), and 14 (D5). You can use any other suitable GPIOs.

ESP8266 NodeMCU connected to three LEDs Schematic diagram wiring circuit

Recommended reading: ESP8266 Pinout Reference: Which GPIO pins should you use?

Organizing Your Files

To keep the project organized and make it easier to understand, we’ll create four files to build the web server:

  • Arduino sketch that handles the web server;
  • index.html: to define the content of the web page;
  • sytle.css: to style the web page;
  • script.js: to program the behavior of the web page—handle what happens when you move the slider, send, receive and interpret the messages received via WebSocket protocol.
ESP8266 NodeMCU organizing your Files arduino sketch index html style css script js

You should save the HTML, CSS, and JavaScript files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP8266 filesystem (LittleFS).

You can download all project files:

HTML File

Copy the following to the index.html file.

<!-- Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-web-server-websocket-sliders/ -->

<!DOCTYPE html>
<html>
<head>
    <title>ESP IOT DASHBOARD</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="topnav">
        <h1>Multiple Sliders</h1>
    </div>
    <div class="content">
        <div class="card-grid">
            <div class="card">
                <p class="card-title">Fader 1</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 2</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider2" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue2"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 3</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider3" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue3"></span> &percnt;</p>
            </div>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

View raw code

Let’s take a quick look at the most relevant parts of the HTML file.

Creating a Slider

The following tags create the card for the first slider (Fader 1).

<div class="card">
  <p class="card-title">Fader 1</p>
  <p class="switch">
    <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
  </p>
  <p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>
</div>

The first paragraph displays a title for the card (Fader 1). You can change the text to whatever you want.

<p class="card-title">Fader 1</p>

To create a slider in HTML you use the <input> tag. The <input> tag specifies a field where the user can enter data.

There are a wide variety of input types. To define a slider, use the type attribute with the range value. In a slider, you also need to define the minimum and the maximum range using the min and max attributes (in this case, 0 and 100, respectively).

You also need to define other attributes like:

  • the step attribute specifies the interval between valid numbers. In our case, we set it to 1;
  • the class to style the slider (class=”slider”);
  • the id so that we can manipulate the slider value using JavaScript (id=”slider1″);
  • the onchange attribute to call a function (updateSliderPWM(this)) when you set a new position for the slider. This function (defined in the JavaScript file) sends the current slider value via the WebSocket protocol to the client. The this keyword refers to the HTML slider element.

The slider is inside a paragraph with the switch class name. So, here are the tags that actually create the slider.

<p class="switch">
  <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
</p>

Finally, there’s a paragraph with a <span> tag, so that we can insert the current slider value in that paragraph by referring to its id (id=”sliderValue1″).

<p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>

Creating More Sliders

To create more sliders, you need to copy all the HTML tags that create the complete card. First, however, you need to consider that you need a unique id for each slider and slider value. In our case, we have three sliders with the following ids: slider1, slider2, slider3, and three placeholders for the slider value with the following ids: sliderValue1, sliderValue2, sliderValue3.

For example, here’s the card for slider number 2.

<div class="card">
  <p class="card-title"> Fader 2</p>
  <p class="switch">
    <input type="range" onchange="updateSliderPWM(this)" id="slider2" min="0" max="100" step="1" value ="0" class="slider">
  </p>
  <p class="state">Brightness: <span id="sliderValue2"></span> &percnt;</p>
</div>

CSS File

Copy the following to the style.css file.

/*  Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-web-server-websocket-sliders/  */

html {
    font-family: Arial, Helvetica, sans-serif;
    display: inline-block;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  p {
    font-size: 1.4rem;
  }
  .topnav {
    overflow: hidden;
    background-color: #0A1128;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
  }
  .card-grid {
    max-width: 700px;
    margin: 0 auto;
    display: grid;
    grid-gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
  .card {
    background-color: white;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
  }
  .card-title {
    font-size: 1.2rem;
    font-weight: bold;
    color: #034078
  }
  .state {
    font-size: 1.2rem;
    color:#1282A2;
  }
  .slider {
    -webkit-appearance: none;
    margin: 0 auto;
    width: 100%;
    height: 15px;
    border-radius: 10px;
    background: #FFD65C;
    outline: none;
  }
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #034078;
    cursor: pointer;
  }
  .slider::-moz-range-thumb {
    width: 30px;
    height: 30px;
    border-radius: 50% ;
    background: #034078;
    cursor: pointer;
  }
  .switch {
    padding-left: 5%;
    padding-right: 5%;
  }

View raw code

Let’s take a quick look at the relevant parts of the CSS file that style the slider. In this example, we need to use the vendor prefixes for the appearance attribute.

.slider {
  -webkit-appearance: none;
  margin: 0 auto;
  width: 100%;
  height: 15px;
  border-radius: 10px;
  background: #FFD65C;
  outline: none;
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #034078;
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50% ;
  background: #034078;
  cursor: pointer;
}
.switch {
  padding-left: 5%;
  padding-right: 5%;
}

Vendor Prefixes

Vendor prefixes allow a browser to support new CSS features before they become fully supported. The most commonly used browsers use the following prefixes:

  • -webkit- Chrome, Safari, newer versions of Opera, almost all iOS browsers,
  • -moz- Firefox,
  • -o- Old versions of Opera,
  • -ms- Microsoft Edge and Internet Explorer.

Vendor prefixes are temporary. Once the properties are fully supported by the browser you use, you don’t need them. You can use the following reference to check if the property you’re using needs prefixes: http://shouldiprefix.com/

Let’s take a look at the .slider selector (styles the slider itself):

.slider {
  -webkit-appearance: none;
  margin: 0 auto;
  width: 100%;
  height: 15px;
  border-radius: 10px;
  background: #FFD65C;outline: none;
}

Setting -webkit-appearance to none overrides the default CSS styles applied to the slider in Google Chrome, Safari, and Android browsers.

-webkit-appearance: none;

Setting the margin to 0 auto aligns the slider inside its parent container.

margin: 0 auto;

The width of the slider is set to 100% and the height to 15px. The border-radius is set to 10px.

margin: 0 auto;
width: 100%;
height: 15px;
border-radius: 10px;

Set the background color for the slider and set the outline to none.

background: #FFD65C;
outline: none;

Then, format the slider handle. Use -webkit- for Chrome, Opera, Safari and Edge web browsers and -moz- for Firefox.

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #034078;
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50% ;
  background: #034078;
  cursor: pointer;
}

Set the -webkit-appearance and appearance properties to none to override default properties.

-webkit-appearance: none;
appearance: none;

Set a specific width, height and border-radius for the handler. Setting the same width and height with a border-radius of 50% creates a circle.

width: 30px;
height: 30px;
border-radius: 50%;

Then, set a color for the background and set the cursor to a pointer.

background: #034078;
cursor: pointer;

Feel free to play with the slider properties to give it a different look.

JavaScript File

Copy the following to the script.js file.

// Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-web-server-websocket-sliders/

var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onload);

function onload(event) {
    initWebSocket();
}

function getValues(){
    websocket.send("getValues");
}

function initWebSocket() {
    console.log('Trying to open a WebSocket connection…');
    websocket = new WebSocket(gateway);
    websocket.onopen = onOpen;
    websocket.onclose = onClose;
    websocket.onmessage = onMessage;
}

function onOpen(event) {
    console.log('Connection opened');
    getValues();
}

function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
}

function updateSliderPWM(element) {
    var sliderNumber = element.id.charAt(element.id.length-1);
    var sliderValue = document.getElementById(element.id).value;
    document.getElementById("sliderValue"+sliderNumber).innerHTML = sliderValue;
    console.log(sliderValue);
    websocket.send(sliderNumber+"s"+sliderValue.toString());
}

function onMessage(event) {
    console.log(event.data);
    var myObj = JSON.parse(event.data);
    var keys = Object.keys(myObj);

    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        document.getElementById(key).innerHTML = myObj[key];
        document.getElementById("slider"+ (i+1).toString()).value = myObj[key];
    }
}

View raw code

Here’s a list of what this code does:

  • initializes a WebSocket connection with the server;
  • sends a message to the server to get the current slider values;
  • uses the response to update the slider values on the web page;
  • handles data exchange through the WebSocket protocol.

Let’s take a look at this JavaScript code to see how it works.

The gateway is the entry point to the WebSocket interface. window.location.hostname gets the current page address (the web server IP address).

var gateway = ws://${window.location.hostname}/ws;

Create a new global variable called websocket.

var websocket;

Add an event listener that will call the onload function when the web page loads.

window.addEventListener('load', onload);

The onload() function calls the initWebSocket() function to initialize a WebSocket connection with the server.

function onload(event) {
  initWebSocket();
}

The initWebSocket() function initializes a WebSocket connection on the gateway defined earlier. We also assign several callback functions for when the WebSocket connection is opened, closed, or when a message is received.

function initWebSocket() {
  console.log('Trying to open a WebSocket connection…');
  websocket = new WebSocket(gateway);
  websocket.onopen = onOpen;
  websocket.onclose = onClose;
  websocket.onmessage = onMessage;
}

Note that when the websocket connection in open, we’ll call the getValues function.

function onOpen(event) {
  console.log('Connection opened');
  getValues();
}

The getValues() function sends a message to the server getValues to get the current value of all sliders. Then, we must handle what happens when we receive that message on the server side (ESP8266).

function getStates(){
  websocket.send("getValues");
}

We handle the messages received via websocket protocol on the onMessage() function.

function onMessage(event) {
  console.log(event.data);
  var myObj = JSON.parse(event.data);
  var keys = Object.keys(myObj);

  for (var i = 0; i < keys.length; i++){
    var key = keys[i];
    document.getElementById(key).innerHTML = myObj[key];
    document.getElementById("slider"+ (i+1).toString()).value = myObj[key];
  }
}

The server sends the states in JSON format, for example:

{
  sliderValue1: 20;
  sliderValue2: 50;
  sliderValue3: 0;
}

The onMessage() function simply goes through all the values and places them on the corresponding places on the HTML page.

The updateSliderPWM() function runs when you move the sliders.

function updateSliderPWM(element) {
  var sliderNumber = element.id.charAt(element.id.length-1);
  var sliderValue = document.getElementById(element.id).value;
  document.getElementById("sliderValue"+sliderNumber).innerHTML = sliderValue;
  console.log(sliderValue);
  websocket.send(sliderNumber+"s"+sliderValue.toString());
}

This function gets the value from the slider and updates the corresponding paragraph with the right value. This function also sends a message to the server so that the ESP8266 updates the LED brightness.

websocket.send(sliderNumber+"s"+sliderValue.toString());

The message is sent in the following format:

  • slidernumbersslidervalue

For example, if you move slider number 3 to position 40, it will send the following message:

3s40

Arduino Sketch

Copy the following code to your Arduino IDE or to the main.cpp file if you’re using PlatformIO.

/* 
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-web-server-websocket-sliders/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.h>

// 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);
// Create a WebSocket object

AsyncWebSocket ws("/ws");
// Set LED GPIO
const int ledPin1 = 14;
const int ledPin2 = 12;
const int ledPin3 = 13;

String message = "";
String sliderValue1 = "0";
String sliderValue2 = "0";
String sliderValue3 = "0";

int dutyCycle1;
int dutyCycle2;
int dutyCycle3;

//Json Variable to Hold Slider Values
JSONVar sliderValues;

//Get Slider Values
String getSliderValues(){
  sliderValues["sliderValue1"] = String(sliderValue1);
  sliderValues["sliderValue2"] = String(sliderValue2);
  sliderValues["sliderValue3"] = String(sliderValue3);

  String jsonString = JSON.stringify(sliderValues);
  return jsonString;
}

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
   Serial.println("LittleFS mounted successfully");
  }
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void notifyClients(String sliderValues) {
  ws.textAll(sliderValues);
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    message = (char*)data;
    if (message.indexOf("1s") >= 0) {
      sliderValue1 = message.substring(2);
      dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 1023);
      Serial.println(dutyCycle1);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (message.indexOf("2s") >= 0) {
      sliderValue2 = message.substring(2);
      dutyCycle2 = map(sliderValue2.toInt(), 0, 100, 0, 1023);
      Serial.println(dutyCycle2);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }    
    if (message.indexOf("3s") >= 0) {
      sliderValue3 = message.substring(2);
      dutyCycle3 = map(sliderValue3.toInt(), 0, 100, 0, 1023);
      Serial.println(dutyCycle3);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (strcmp((char*)data, "getValues") == 0) {
      notifyClients(getSliderValues());
    }
  }
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
      handleWebSocketMessage(arg, data, len);
      break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
      break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  initFS();
  initWiFi();

  initWebSocket();
  
  // Web Server Root URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html");
  });
  
  server.serveStatic("/", LittleFS, "/");

  // Start server
  server.begin();
}

void loop() {
  analogWrite(ledPin1, dutyCycle1);
  analogWrite(ledPin2, dutyCycle2);
  analogWrite(ledPin3, dutyCycle3);

  ws.cleanupClients();
}

View raw code

How the Code Works

Let’s take a quick look at the relevant parts for this project. To better understand how the code works, we recommend following this tutorial about WebSocket protocol with the ESP8266 and this tutorial about PWM with the ESP8266.

Insert your network credentials in the following variables to connect the ESP8266 to your local network:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

The getSliderValues() function creates a JSON string with the current slider values.

String getSliderValues(){
  sliderValues["sliderValue1"] = String(sliderValue1);
  sliderValues["sliderValue2"] = String(sliderValue2);
  sliderValues["sliderValue3"] = String(sliderValue3);

  String jsonString = JSON.stringify(sliderValues);
  return jsonString;
}

The notifyClients() function notifies all clients with the current slider values. Calling this function is what allows us to notify changes in all clients whenever you set a new position for a slider.

void notifyClients(String sliderValues) {
  ws.textAll(sliderValues);
}

The handleWebSocketMessage(), as the name suggests, handles what happens when the server receives a message from the client via WebSocket protocol. We’ve seen in the JavaScript file, that the server can receive the getValues message or a message with the slider number and the slider value.

When it receives the getValues message, it sends the current slider values.

if (strcmp((char*)data, "getValues") == 0) {
  notifyClients(getSliderValues());
}

If it receives another message, we check to which slider corresponds the message and update the corresponding duty cycle value. Finally, we notify all clients that a change occurred. Here’s an example for slider 1:

if (message.indexOf("1s") >= 0) {
  sliderValue1 = message.substring(2);
  dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 1023);
  Serial.println(dutyCycle1);
  Serial.print(getSliderValues());
  notifyClients(getSliderValues());
}

In the loop(), we update the duty cycle of the PWM channels to adjust the brightness of the LEDs.

void loop() {
  analogWrite(ledPin1, dutyCycle1);
  analogWrite(ledPin2, dutyCycle2);
  analogWrite(ledPin3, dutyCycle3);

  ws.cleanupClients();
}

Upload Code and Files

After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.

Arduino IDE Open Sketch Folder to create data folder

Inside that folder you should save the HTML, CSS and JavaScript files.

Then, upload the code to your ESP8266 board. Make sure you have the right board and COM port selected. Also, make sure you’ve added your network credentials.

Upload Arduino code

After uploading the code, you need to upload the files. Go to Tools ES8266 LittleFS Data Sketch Upload and wait for the files to be uploaded.

ESP8266 NodeMCU LittleFS Data Upload Arduino IDE

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP8266 EN/RST button, and it should print the ESP8266 IP address.

Demonstration

Open a browser on your local network and paste the ESP8266 IP address. You should get access to the web server page to control the brightness of the LEDs.

ESP8266 NodeMCU Web Server WebSocket with Multiple Sliders Control LEDs Brightness Demonstration

Move the sliders to control the brightness of the LEDs.

ESP8266 NodeMCU Multiple Sliders Web Server Webscocket Arduino

Open several tabs or connect to the web server using another device, and notice that the slider values update almost instantaneously whenever there’s a change.

You can watch the video demonstration (the following video shows a demonstration for the ESP32, but it works similarly with the ESP8266):


Wrapping Up

In this tutorial, you’ve learned how to build a web server with the ESP8266 that serves a web page with multiple sliders. The sliders allow you to control the brightness of LEDs connected to the ESP8266. In addition, we’ve used the WebSocket protocol to communicate between the ESP8266 and the clients.

To learn more about building web servers with the ESP8266 boards, we really recommend taking a look at our eBook:

Learn more about the ESP8266 with our resources:

Thank you for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

45 thoughts on “ESP8266 NodeMCU Web Server (WebSocket) with Multiple Sliders: Control LEDs Brightness (PWM)”

  1. Hi,
    Great little project!
    However, I’m more used to MicroPython than Arduino.
    What I have found amongst your projects is a very limited webserver for ESP8266/ESP32 in uPython and this much more powerful webserver for Arduino.
    I would love a tutorial with TinyWeb or other async webserver for uPython.

    Best Regards
    Niclas

    Reply
  2. Hello,
    I’ce tried adding the Code below in the void loop just before the analogWrite so I can controll the LED with a Button too and to my surprise it actually Works but the Web server doesn’t work anymore then. Does anybody know why?

    int sensorValue = 0;
    int buttonState = 0;

    sensorValue = analogRead(A0);
    if(sensorValue > 300 && buttonState == 1) {
    sliderValues = “0”;
    dutyCycle1 = 0;
    buttonState = 0;
    while(sensorValue > 300) {
    sensorValue = analogRead(A0);
    }
    } else if(sensorValue > 300 && buttonState == 0) {
    sliderValues = “100”;
    dutyCycle1 = 1023;
    buttonState = 1;
    while(sensorValue > 300) {
    sensorValue = analogRead(A0);
    }
    }

    Reply
  3. Nice good job…. Thank You…
    But i have one question, how to change value slider move without releasing mouse button

    Reply
    • Hi.
      In your HTML, instead of using “onchange”, use “oninput”.
      However, keep in mind that it will make a ton of requests and may crash the ESP8266.
      Regards,
      Sara

      Reply
      • thank you….
        I am imaging to make drone control using this project… may be some time You can make project ESP8266 quadcopter control by websocket or any realtime web technology

        Reply
  4. I followed it through and uploaded both webpage and sketch. Upon entry to the ip of the esp8266, Chrome says page can’t be found…

    Reply
  5. Hello!

    Fantastic tutorial! I’ve gotten this to work wonderfully and been able to modify to my needs for the most part. I am currently working on project to use an ESP8266 to control an actual stoplight in a classroom for a teacher friend. I am curious as how to use the ESP8266 with this project, but as an access point. Unfortunately, the school’s IT department changes the passwords frequently and having the teacher use his hotspot regularly to connect is not optimal. Any advice? TIA!

    Reply
  6. Hi Sara,

    I am using PlatformIO on VS Code, and could follow all the instructions to upload the html, css, and js files using LittleFS. And then upload the main.cpp code to my Mega+WiFi (ESP8266) board. I changed the baud rate to 115200, in the platformIO.ini file, as well as in the main.cpp part of the setup code “Serial.begin(115200);”.

    But when I run things on VS Code, weird characters appear in the Serial Monitor. Why? Any clues as to what I might be doing wrong, please?

    My other issue is, there’s no IP address to type into my browser, or other device, in the code. So, apart from the only clue being in the demo video “192.168.1.85” (which didn’t work when I tried), how do you connect? Or, more so, how does the ESP8266 know what address its supposed to be? How can I actually bring up the slider-webpage?

    Other than these issues, the example here (and others on this site too) are fantastic!! Love your work!

    Thanks
    Chris

    Reply
    • Hi Sara (or anyone else reading),

      So, to answer my own questions…

      Some context; I added two LED’s, added in the appropriate code, both in the index.html file, and in main.cpp, so, no issues there, per se.

      The Arduino Mega + WiFi board has a little 2×7 pin-header. Searching the internet, I found a sort-of schematic for it. GPIO pins on it are GPIO00, GPIO02, GPIO04, GPIO05, GPIO12 and GPIO16. I had to look at your “Blink sketch tutorial” once more (because I got that running with a baud rate of 115200), and play around with this 2×7 header and LEDs, to figure out those GPIOxx pins, what worked, and what didn’t in code.

      So, then I altered this examples pinning code. Making ledpin1 = 0, ledpin2 = 2, ledpin3 = 4, ledpin4 = 5, and ledpin5 = 12.

      I then re-read the code you’ve provided, and saw how the code finds the IP address and displays it on the Serial Monitor. However, when I built and uploaded the code to the Mega+WiFi board, on the Serial Monitor, it would say, “LittleFS mounted successfully”, then “Connecting to WiFi …….” and then the IP Address. And that’s it.

      What I’d done was give the index.html filename a capital ‘I’ (Index.html), so I changed it to index.html, as was described midway through this tutorial. I suppose there’s a spot in the code that looks for the title of this file, as with the css and js file names, and I could alter that. But, too hard, this was easier.

      Next, I re-Uploaded the Filesystem, this time with the correct file names (case-sensitive). And kicked the Mega+WiFi in the guts again….

      …And it WORKED!!!!! Wooooo!!!

      So, beloved kiddos, out there anywhere, remember, don’t panic, re-read the whole thing, twice, and check what you’re building/uploading. Cuz, small mistakes count. BUT, when all is good, fault-finding is a thing.

      Hope this helps someone out there! <3 <3

      Thanks
      Chris

      Reply
    • Hi.
      After uploading the code to your board, make sure you actually open the Serial Monitor on VS Code. Do you know how to do that?
      You can check our VS Code + PlatformIO guide: https://randomnerdtutorials.com/vs-code-platformio-ide-esp32-esp8266-arduino/
      Make sure you have saved the platformio.ini file with the baud rate before opening the serial monitor.
      After checking that everything is ok, and opening the serial monitor, reset the board by pressing the onboard RST button.
      The IP address should be displayed on the serial monitor, and it will be different than ours.
      You can also set a predetermined IP address:https://randomnerdtutorials.com/esp8266-nodemcu-static-fixed-ip-address-arduino/
      I hope this helps.
      Regards,
      Sara

      Reply
  7. On an Adafruit Feather HUZZAH ESP8266, the single button web socket tutorial worked. I also compiled and ran the single slider (not web-socket) and it worked. I want to combine a button and a slider using web socket. I tried the three slider web socket tutorial, and it will not compile. The LittleFS uploaded the files without issue. There is a problem with the Arduino_JSON.h file. I’m using Arduino 1.8.14 to compile.

    It would be helpful if you placed an example of the following into a tutorial:
    A web socket Arduino project that includes one button to toggle, one slider, and one dial to display server sent integer values from 1 to 180.

    Reply
    • Hi.
      When you say the code doesn’t compile, what is the error that you get?
      Make sure you have all the required libraries installed.
      Regards,
      Sara

      Reply
      • Hi Sara,

        The Arduino environment complains that it cannot find ArduinoJSON.h

        I have installed all the necessary libraries. And, others of your tutorials for websocket sketches using a RED LED toggle work, they compile and run. The single slider sketch compiles and runs. Only the three slider websocket sketch will not compile.

        I tried uninstalling and reinstalling the Arduino_JSON library from the github link included in your tutorial, and have invested 30 hours but have not succeeded yet.

        Is there a particular version of Arduino and that Arduino_JSON library you recommend?

        Than you.

        Jim

        Reply
      • Hi Sara,
        Here is the error message I received when attempting a different sketch. Looks like duplicate libraries are the issue, with one library containing a ‘-master’ suffix.

        (…)

        ESP8266_Gauges:15:10: fatal error: Adafruit_BME280.h: No such file or directory

        15 | #include <Adafruit_BME280.h>

        | ^~~~~~~~~~~~~~~~~~~

        compilation terminated.

        exit status 1

        Adafruit_BME280.h: No such file or directory

        Can you please advise?

        Thanks.

        Reply
      • Update: I got the three slider sketch to compile and load using Arduino 1.8.9, and deleted the Arduino_JSON library in “C:\Program Files (x86)\Arduino\libraries”. Finally, without that Arduino_JSON library, it works.

        I’ve had troubles before when adding zipped libraries that had a “-master” suffix in the folder name. Maybe that was the issue, but it is no longer hindering my progress.

        Thank you for the instructive tutorials on websocket data manipulation.

        -Jim

        Reply
  8. Hello Sara! I admire your talent! I’m trying to adapt this code for Micropyton, everything works, but I can’t translate the slider value into a variable, in the request (‘Content = %s’ %request) – b’GET /slider?value=740 there is data, but I can’t figure out how to get it, there is no information anywhere… Can JSON be used, or is there an easier way? Push me in the right direction, please!

    Reply
  9. What software do you use to create the web widgets ?
    I would like to change the sliders from side-to-side to up-n-down.
    How do you convert the widgets to an .h file ?
    Thanks

    Reply
  10. Hi sara. Thank you so much for this project and other ESP project. I want to use 20 khz frequency for my motors but although I increase the frequency with analogWriteFreq in the setup section, the PWM output is at a lower and irregular value instead of 20 KHZ.

    I’m having this problem probably because the ESP doesn’t have enough processing power, but I believe the problem will be solved if I reduce the processing load of the ESP.

    How can i get the server to fetch data every 10 seconds and what can I do better?
    I can’t use ESP32 unfortunately

    Thanks

    Reply
  11. Hi Rui, Sara!
    Thanks for this amazing project and I want to ask you, if I want to “slide” a power 220V leds, how can I proced? It’s ok to use triacs between esp and leds? Or better some power mosfets…I’d like to control 3 power leds of 50W/230V each.
    Thank you very much for your kindly answer!
    Greetings from Romania!
    Peter.

    Reply
  12. Hi
    I have a new laptop and not-so-new desktop using Dropbox to share files. I successfully uploaded ESP8266Blink from the laptop to test my system. I also built the project on the desktop and it works fine. On the laptop I can’t see the LittleFS data upload bar in Arduino IDE tools after trying many times. The Desktop is Windows10 but Laptop is Windows11.
    Do you think this is the problem ? And if so is there a workaround ?

    Reply
  13. Hi Sara
    Thanks for quick reply.
    Have followed your LittleFS tutorial exactly but still no LittleFS Data Uploader tool in Arduino on my laptop. Have repeated several times. Laptop is Windows11 and my Desktop is Window10 where I have successfully installed the LittleFS Data Upload tool some time ago for an ESP8266 I2S project.
    I read somewhere on web that Windows11 firewall blocks LittleFS Data Uploader.

    Reply
  14. Well, I have a strange behavior after following your excellent guide. I get the web server up and when I check serial monitor, I see the IP address of the esp8266 and deskside and mobile both show the sliders correctly. Thing is, the LEDs do not light up. When I press reset they do, and then go out. Moving the slider has no effect on them. I tested the LED individually and they are all fine. Im confused. Everything worked as described except that. I tried changing boards from NodeMCU1 to generic8266 with no change.

    Reply
      • Hi.
        Please double-check that you’ve created and uploaded all the required files to the board, including the Javascript file.
        Regards,
        Sara

        Reply
        • here are the files in the data folder of the sketch. I am assuming the script.js move over to the esp8266 since index.html displays with the correct style sheet and displays correctly on both browser and mobile. Is there a way to display the files on the MCU?

          01/29/2023 05:25 PM 1,796 index.html
          01/29/2023 05:30 PM 2,966 script.js
          01/29/2023 05:29 PM 1,482 style.css

          Reply
          • I FOUND THE ISSUE! I checked the file sizes from the .zip you provided and the ones in my sketch data folder and discovered I had pasted over the .js with the text from the .css. Problem solved! Thanks so much for this very fun project. I look forward to many more!!!!

  15. how to modify html and js for more than 9 sliders, 9 sliders are working fine but afterward if the slider id is 11 or 12, it is taking the ones place and updating the value of 1 or 2 respectively.

    Reply
      • Hi,
        if the intended range slider1 … slider9 is not sufficient, it can actually be extended by hexadecimal notation to slider0 … sliderF. To do this, however, line 49 in script.js must be changed as follows:
        document.getElementById(“slider “+ (i).toString(16)).value = myObj[key];
        Regards,
        Larsemann

        Reply
        • Sorry, I was wrong. You have to change more than only one line in script.js.
          These two functions have to look like this:

          function updateSliderPWM(element) {
          var sliderNumber = element.id.charAt(element.id.length-1);
          var sliderValue = document.getElementById(element.id).value;
          var slNumHexStrg = sliderNumber.toString(16).toUpperCase();

          document.getElementById("sliderValue" + slNumHexStrg).innerHTML = sliderValue;
          websocket.send(slNumHexStrg + "s" + sliderValue.toString());

          }

          function onMessage(event) {
          var myObj = JSON.parse(event.data);
          var keys = Object.keys(myObj);

          for (var i = 0; i < keys.length; i++){
          var key = keys[i];

          document.getElementById(key).innerHTML = myObj[key];
          document.getElementById("slider" + i.toString(16).toUpperCase()).value = myObj[key];
          }

          }

          Reply
  16. Hi,

    I kinda gave the same problem as above but need to go to 24 sliders or even more is there a solution for tied for hours but my knowledge is lacking

    kind regards martin

    Reply
    • Hi,
      I have made it this way (in .ino file):

      (C++, adruino file)
      if (message.indexOf(“sliderReds”) >= 0) {
      sliderRedValue = message.substring(sizeof(“sliderReds”) – 1);
      …rest of code…
      }

      (script.js file)
      function updateSlider(element) {
      var id = element.id;
      var sliderValue = document.getElementById(id).value;
      document.getElementById(id).innerHTML = sliderValue;
      websocket.send(id+”s”+sliderValue.toString());
      }
      function onMessage(event) {
      var myObj = JSON.parse(event.data);
      var keys = Object.keys(myObj);
      var values = Object.values(myObj);
      for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      document.getElementById(key).innerHTML = myObj[key];
      document.getElementById(key.slice(0, key.length-5)).value = myObj[key];
      }
      }

      My sliders IDs are “sliderRed”, “sliderGreen” and so on and then I am sending this ID with “s” and value (it may be even without letter “s” between ID and value) to ESP8266. On ESP8266 with usage of code above you cut substring from the index after “s” in “sliderReds” to the end (where value should be).

      Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.