In this tutorial you’ll build a web server to control the ESP32 or ESP8266 NodeMCU outputs with a pulse using Arduino IDE. The pulse width (“timer”) can be adjusted using a slider on the web page. When you click the ON button, the ESP sets the output state to HIGH for the number of seconds defined in the slider. This can be specially useful to control appliances that need a HIGH signal for a predetermined number of seconds to actuate.
The ESP32/ESP8266 boards will be programmed using Arduino IDE. So make sure you have these boards installed:
- Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux)
- Installing ESP8266 Board in Arduino IDE (Windows, Mac OS X, Linux)
Project Overview
The following image shows an overview on how this project works.
- The ESP32/ESP8266 hosts a web server that allows you to control an output with a pulse;
- The web server contains a slider that allows you to define the pulse width (how many seconds the output should be HIGH);
- There’s an ON/OFF button. Set it to ON to send the pulse. After that, you’ll see a timer decreasing for the duration of the pulse width;
- When the timer is over, the output is set to LOW, and the web server button goes back to OFF state;
- This web server can be useful to control devices that need a pulse to activate like garage door openers, for example.
Installing Libraries – Async Web Server
To build the web server you need to install the following libraries:
- ESP32: install the ESPAsyncWebServer and the AsyncTCP libraries.
- ESP8266: install the ESPAsyncWebServer and the ESPAsyncTCP libraries.
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.
Code
Copy the following code to your Arduino IDE.
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-web-server-timer-pulse/
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
// Import required libraries
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
const char* PARAM_INPUT_1 = "state";
const char* PARAM_INPUT_2 = "value";
const int output = 2;
String timerSliderValue = "10";
// Create AsyncWebServer object on port 80
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>ESP Web Server</title>
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 2.4rem;}
p {font-size: 2.2rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 34px}
.slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
input:checked+.slider {background-color: #2196F3}
input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
.slider2 { -webkit-appearance: none; margin: 14px; width: 300px; height: 20px; background: #ccc;
outline: none; -webkit-transition: .2s; transition: opacity .2s;}
.slider2::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 30px; height: 30px; background: #2f4468; cursor: pointer;}
.slider2::-moz-range-thumb { width: 30px; height: 30px; background: #2f4468; cursor: pointer; }
</style>
</head>
<body>
<h2>ESP Web Server</h2>
<p><span id="timerValue">%TIMERVALUE%</span> s</p>
<p><input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2"></p>
%BUTTONPLACEHOLDER%
<script>
function toggleCheckbox(element) {
var sliderValue = document.getElementById("timerSlider").value;
var xhr = new XMLHttpRequest();
if(element.checked){ xhr.open("GET", "/update?state=1", true); xhr.send();
var count = sliderValue, timer = setInterval(function() {
count--; document.getElementById("timerValue").innerHTML = count;
if(count == 0){ clearInterval(timer); document.getElementById("timerValue").innerHTML = document.getElementById("timerSlider").value; }
}, 1000);
sliderValue = sliderValue*1000;
setTimeout(function(){ xhr.open("GET", "/update?state=0", true);
document.getElementById(element.id).checked = false; xhr.send(); }, sliderValue);
}
}
function updateSliderTimer(element) {
var sliderValue = document.getElementById("timerSlider").value;
document.getElementById("timerValue").innerHTML = sliderValue;
var xhr = new XMLHttpRequest();
xhr.open("GET", "/slider?value="+sliderValue, true);
xhr.send();
}
</script>
</body>
</html>
)rawliteral";
// Replaces placeholder with button section in your web page
String processor(const String& var){
//Serial.println(var);
if(var == "BUTTONPLACEHOLDER"){
String buttons = "";
String outputStateValue = outputState();
buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label></p>";
return buttons;
}
else if(var == "TIMERVALUE"){
return timerSliderValue;
}
return String();
}
String outputState(){
if(digitalRead(output)){
return "checked";
}
else {
return "";
}
return "";
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(output, OUTPUT);
digitalWrite(output, LOW);
// 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_P(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/update?state=<inputMessage>
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/update?state=<inputMessage>
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
digitalWrite(output, inputMessage.toInt());
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
// Send a GET request to <ESP_IP>/slider?value=<inputMessage>
server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/slider?value=<inputMessage>
if (request->hasParam(PARAM_INPUT_2)) {
inputMessage = request->getParam(PARAM_INPUT_2)->value();
timerSliderValue = inputMessage;
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
// Start server
server.begin();
}
void loop() {
}
You just need to enter your network credentials (SSID and password) and the web server will work straight away. The code is compatible with both the ESP32 and ESP8266 boards and controls the on-board LED GPIO 2 – you can change the code to control any other GPIO.
How the Code Works
We’ve already explained in great details how web servers like this work in previous tutorials (DHT Temperature Web Server or Relay Web Server), so we’ll just take a look at the relevant parts for this project.
Network Credentials
As said previously, insert your network credentials in the following lines:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Slider Label
Above the slider, there’s a number showing the current slider value.
<p><span id="timerValue">%TIMERVALUE%</span> s</p>
By default, the slider value is set to the %TIMERVALUE% placeholder.
The %TIMERVALUE% is a placeholder that will be replaced with the value stored in the timerSliderValue variable which is set to 10 by default. But you can change that in the following line:
String timerSliderValue = "10";
This will also be changed when you move the slider. When the slider is moved, it calls a JavaScript function that updates the slider value.
Slider
The following line creates the slider.
<input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2">
Let’s break this down into smaller sections.
In HTML, a slider is an input type. The <input> tag specifies an input field where the user can enter data. The slider is an input field of type range. There are many other input field types.
<input type="range">
The default range of the slider is 0 to 100. You can use the following attributes to customize the slider settings:
- max: specifies the maximum value allowed. In our example, we’re setting it to 20, but you can change that value.
- min: specifies the minimum value. In this case, we’re setting it to 1.
- step: specifies the number interval. It’s set to 1.
- value: specifies the default value of the slider. In this case, it is equal to %TIMERVALUE%.
<input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2">
The %TIMERVALUE% is a placeholder that will be replaced with an actual value. In the code, it will be replaced with the value of the timerSliderValue variable that is set to 10 by default. But you can change that in the following line:
String timerSliderValue = "10";
The slider has two more attributes: id and onchange.
- id: specifies a unique id for an HTML element (slider). The id allows us to manipulate the element using CSS or JavaScript.
- onchange: is an event attribute that occurs when we change the value of the element (the slider). When you move the slider, it calls the updateSliderTimer() function.
Update Slider Value (JavaScript)
When you move the slider, the updateSliderTimer() function is executed.
It gets the current slider value by referring to its id timerSlider:
var sliderValue = document.getElementById("timerSlider").value;
Updates the slider label to the current slider value by referring to its id timerValue:
document.getElementById("timerValue").innerHTML = sliderValue;
Then, it makes a request on the /slider?value=sliderValue URL. Where the sliderValue is equal to the current slider value.
Then, the ESP32/ESP8266 handles what happens when it receives a request on that URL.
Control the Output with Timer (JavaScript)
When you click the ON/OFF button to control the output, it calls the toogleCheckbox() JavaScript function.
This function gets the current value of the slider label:
var sliderValue = document.getElementById("timerSlider").value;
Makes a request on the /update?state=1 URL so that the ESP knows it needs to set the output to HIGH.
if(element.checked){ xhr.open("GET", "/update?state=1", true); xhr.send();
The following lines decrease the slider label value every second creating the countdown timer.
var count = sliderValue, timer = setInterval(function() {
count--; document.getElementById("timerValue").innerHTML = count;
if(count == 0){ clearInterval(timer); document.getElementById("timerValue").innerHTML = document.getElementById("timerSlider").value; }
}, 1000);
When the timer hits zero, the label value gets back to its original value and a request is made on the /update?state=0 URL, so that the ESP knows it is time to set the output to LOW. The button on the web server gets back to the off state.
setTimeout(function(){ xhr.open("GET", "/update?state=0", true);
document.getElementById(element.id).checked = false; xhr.send(); }, sliderValue);
Handle Requests
The ESP32/ESP8266 needs to handle what happens when it receives a request on a certain URL.
Root URL
When you access the root URL /, send the HTML text saved on the index_html variable. All placeholders are replaced with the actual values by the processor() function.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
Control Output State
The following lines handle what happens when you receive a request on the /update?state=1 and /update?state=0 URLs. It sets the output state to HIGH or LOW accordingly.
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/update?state=<inputMessage>
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
digitalWrite(output, inputMessage.toInt());
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
The output state is updated in the following line:
digitalWrite(output, inputMessage.toInt());
Update Slider Value
Every time you drag the slider, the ESP receives a request with the new value. We store the new slider value and print it in the Serial Monitor.
// Send a GET request to <ESP_IP>/slider?value=<inputMessage>
server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/slider?value=<inputMessage>
if (request->hasParam(PARAM_INPUT_2)) {
inputMessage = request->getParam(PARAM_INPUT_2)->value();
timerSliderValue = inputMessage;
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
Demonstration
Upload the code to your ESP32 or ESP8266 NodeMCU board. Then, open the Serial Monitor and press the on-board RST/EN button to get is IP address.
Open a browser in your local network and type the ESP IP address. The following page should load.
Drag the slider to adjust the pulse width, and then, click the ON/OFF button. The output (in this case GPIO 2 – built-in LED) will stay on for the period of time you’ve set on the slider.
Watch the next quick video for a live demonstration:
Wrapping Up
In this tutorial you’ve learned how to build a web server to control an output with a pulse. The pulse width can be adjusted with a slider on the web server page. This can be specially useful to control certain appliances that need a HIGH signal for a determined number of seconds to actuate, like garage door openers.
We hope you liked this tutorial. We have more web server projects you may like:
- Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE
- ESP32/ESP8266 Web Server HTTP Authentication (Username and Password Protected)
- ESP32/ESP8266: Control Outputs with Web Server and a Physical Button Simultaneously
- ESP32/ESP8266 Web Server: Control Outputs with Momentary Switch
Learn more about the ESP32 and ESP8266 board with our resources.
This is great, could I do this using Mosquito?
Yes, but unfortunately I don’t have any tutorials on that exact subject.
You can find all our projects here: https://randomnerdtutorials.com/projects-esp32/
Dear, first of all I want to congratulate you on your projects, they are very good and excellently explained.
I would like to know if it is possible to develop this same project but as Access Point?
I do not have internet in the place where I am going to implement it.
Greetings from Argentina!!!
(leocataleo at gmail.com)
Hi.
Yes, you can do that.
Check these tutorials to learn how to set an access point with the ESP32 or ESP8266:
– ESP8266 : https://randomnerdtutorials.com/esp8266-nodemcu-access-point-ap-web-server/
– ESP32: https://randomnerdtutorials.com/esp32-access-point-ap-web-server/
Regards,
Sara
that great Project thanks, is it possible to add script correction? when i am clicking stop, not responding an light is on. i mean emergency stop.
thanks
function toggleCheckbox(element) {
var sliderValue = document.getElementById(“timerSlider”).value;
var xhr = new XMLHttpRequest();
if(element.checked){ xhr.open(“GET”, “/update?state=1”, true); xhr.send();
var count = sliderValue, timer = setInterval(function() {
count–;
document.getElementById(“timerValue”).innerHTML = count;
// turn off relay if slider is set back to “off”
if(!element.checked){count = 0;
xhr.open(“GET”, “/update?state=0”, true);
xhr.send();
//
}
if(count == 0){ clearInterval(timer); document.getElementById(“timerValue”).innerHTML = document.getElementById(“timerSlider”).value; }
}, 1000);
sliderValue = sliderValue*1000;
setTimeout(function(){ xhr.open(“GET”, “/update?state=0”, true);
document.getElementById(element.id).checked = false; xhr.send(); }, sliderValue);
}
}
Hi,
One more amazing project, I will try to do. I hope that you continue with many demonstration like it.
Hi Rui.
Really very useful tutorial, just what I need, to control some of my circuits.
Thanks to you and Sara.
Thanks to your tutorial. is there a way or tutorial that combines with wifimanager.
Hi.
This is the only tutorial we have about wifimanager: https://randomnerdtutorials.com/wifimanager-with-esp8266-autoconnect-custom-parameter-and-manage-your-ssid-and-password/
Regards,
Sara
Sorry, and thak you for the help. But if I want to stop the timer qith the button before the end, how i can do it ?
In my project i want to start a relay which should stop at the end of the timer, but i want also that i can stop it when i want.
Thank you.
Hi.
I wanted to know if you could help me as I am using an esp8266 and it works, but the downside is that it works the other way around instead of turning it off.
You could help me since I don’t know where to modify it.
Hi.
In the code, simply replace the LOW with HIGH, and the HIGH with LOW.
Regards,
Sara
molto interessante e utile.Vorrei sapere se devo comandare più led come devo modificare lo sketch.Grazie
Hi Rui.
This is really really very useful tutorial. Thank you very much for this.
I had a query, Just wanted to know how can I change the timer value from Seconds to MiliSeconds.
Can you please help me on this.
Thanks,
Sudhakar
Thanks, I figured it out. Changed the multiplication value for count & sliderValue multiplier. It seems to be working..
Please let me know if this is correct.
Thanks,
Sudhakar
Hola Rui, falta una lÃnea en el código. Me estaba volviendo loco pq no la veÃa.
Qual és la lÃnea que falta?
i found Jørgen B.’s 3d buttons work here too.
just replace the css
codepen.io/Stockin/pen/qJqVYy
Great tutorial. Thank you very much.
Hello
great tutorial but there is some issue i still can’t figure it out.
1. i already able to change from second to minutes but the countdown not follow the minutes and still in second mode. which section i need to change so the countdown follow the minute setting?
note i change in this section to change from s to m sliderValue = (sliderValue1000)60;
2. which section i need to change if i want to make this project to automatically activate without change the slide button?
3. lastly how to implement eeprom to this project? to be honest i really don’t know how >.< which part need to be add to the eeprom how to deal with the string since eeprom can’t work with string as long as i know. or there is another way to save the latest slider value so when the device is off and on it automatically start count from the latest slider input (related with question number 2)
i’m really grateful and really appreciate it if you can answer my question.
Hi.
To save strings permanently, you can use the preferences library: https://randomnerdtutorials.com/esp32-save-data-permanently-preferences/
Regards,
Sara
Great lesson! Your work greatly facilitates my life :). I am looking for something similar, but for working with large time intervals and with the possibility of stopping the timer after N hours. This can be useful for creating a timer for watering plants. For example: feeding a high level to GPIO25, every 48 hours and holding a high level for three hours.
what about more than 1 output
I somehow bump into problem with your work… I can’t access the web server the esp32 was making…
But somehow I manage to open it by making an access point.
Using this code
const char* sid = “AP name”;
const char* pasword = “AP password”;
WiFi.softAP(sid, pasword);
IPAddress myIP = WiFi.softAPIP();
Serial.print(“AP IP address: “);
Serial.println(myIP);
server.begin();
hello, I would like to do a countdown: enter a value on a web page and with a go/stop button retrieve the value in the esp32 to manage the countdown part every second. I don’t see how to retrieve this value and transfer it to my esp32 program. Can you help me?
can you help me, how do I do it if I want to use serial bluetooth for delay settings using multiple relay outputs and push-bottom pull_up mode?
Hello, hoping you and your love ones are all in good health.
This is my second project using Platformio on VS, i did the blinking led from your tutorial.
Then i went for this one, i copy pasted the raw code and when I try to upload
I get this :
Building in release mode
Compiling .pio/build/esp32dev/src/main.cpp.o
src/main.cpp: In function ‘String processor(const String&)’:
src/main.cpp:90:31: error: ‘outputState’ was not declared in this scope
String outputStateValue = outputState();
^~~~~~~~~~~
src/main.cpp:90:31: note: suggested alternative: ‘outputStateValue’
String outputStateValue = outputState();
^~~~~~~~~~~
outputStateValue
*** [.pio/build/esp32dev/src/main.cpp.o] Error 1
========================================================================================== [FAILED] Took 1.49 seconds
Hi.
In VS Code, all function definitions should show up before the setup() and it matters the order in which they appear (contrary to the arduino IDE).
Move the String outputState(){
if(digitalRead(output)){
return “checked”;
}
else {
return “”;
}
return “”;
}
To a place before the processor() function.
Regards,
Sara
Hi how do i change the gpio to d1 for example.
(Thank u)
Hi i found it if the board is esp8266 need to change the const int output = 2; to
const int output = 5; as gpio 5 is d1 for esp8266
Bonjour
quelle valeurs faut-il modifier pour obtenir un timer en minutes.
merci
Hello,
what values should be modified to obtain a timer in minutes.
THANKS