{"id":169410,"date":"2025-05-08T13:39:26","date_gmt":"2025-05-08T13:39:26","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=169410"},"modified":"2025-05-08T13:39:29","modified_gmt":"2025-05-08T13:39:29","slug":"esp32-web-server-beginners-guide","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-web-server-beginners-guide\/","title":{"rendered":"Building an ESP32 Web Server: The Complete Guide for Beginners"},"content":{"rendered":"\n<p>New to ESP32 web servers? You\u2019re in the right place. In this detailed guide, we\u2019ll cover the basic concepts you need to know to build web servers with the ESP32, so you can control and monitor its outputs remotely. We\u2019ll start with the essential theory, then move on to practical examples to help you apply what you\u2019ve learned.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Building an ESP32 Web Server: The Complete Guide for Beginners\" class=\"wp-image-169530\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?w=1920&amp;quality=100&amp;strip=all&amp;ssl=1 1920w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?resize=1536%2C864&amp;quality=100&amp;strip=all&amp;ssl=1 1536w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>We&#8217;ll cover creating a web server to control outputs and display sensor readings, adding authentication to the web server, setting the ESP32 as an access point, and more. To make things as simple as possible, we&#8217;ll use the built-in <span class=\"rnthl rntliteral\">WebServer.h<\/span> library.<\/p>\n\n\n\n<div class=\"wp-block-group rntbo rntclgray\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p><strong>New to the ESP32? We recommend following these getting-started guides first:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/getting-started-with-esp32\/\" title=\"\">Getting Started with the ESP32 Development Board<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-useful-wi-fi-functions-arduino\/\" title=\"\">ESP32 Useful Wi-Fi Library Functions (Arduino IDE)<\/a><\/li>\n<\/ul>\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Tutorial Overview<\/h2>\n\n\n\n<p>In this tutorial, we&#8217;ll cover the following subjects. If you&#8217;re new to ESP32 web servers and you want to learn more about this subject, we recommend following these sections in order.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"#intro-web-servers\" title=\"\">Introducing Web Servers (Basic Concepts)<\/a><\/li>\n\n\n\n<li><a href=\"#esp32-web-server-outputs\" title=\"\">ESP32 Web Server Example (Control Outputs)<\/a><\/li>\n\n\n\n<li><a href=\"#web-server-authentication\" title=\"\">Add Authentication to your Web Server<\/a><\/li>\n\n\n\n<li><a href=\"#access-point\" title=\"\">ESP32 Web Server (Access Point)<\/a><\/li>\n\n\n\n<li><a href=\"#esp32-web-server-display-readings\" title=\"\">ESP32 Web Server Example (Display Sensor Readings)<\/a><\/li>\n<\/ol>\n\n\n\n<p class=\"rntbox rntclgray\"><strong>Recommended eBook<\/strong>: <a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\" title=\"\">Build Web Servers with the ESP32 and ESP8266 (3rd edition)<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>This tutorial focuses on programming the ESP32 using the Arduino core. Before proceeding, you should have the ESP32 Arduino core installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven\u2019t already.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-esp32-arduino-ide-2-0\/\">Installing the ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux instructions)<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"intro-web-servers\">1) Introducing Web Servers<\/h2>\n\n\n\n<p>In simple terms, a web server is a \u201ccomputer\u201d that delivers web pages. It stores the website\u2019s files, including HTML documents and related assets like images, CSS style sheets, fonts, and other files. When a user makes a request, the server sends those files to the user\u2019s web browser.<\/p>\n\n\n\n<p>When you access a web page in your browser, you\u2019re actually sending a request to a server using the Hypertext Transfer Protocol (HTTP). This protocol handles how information is requested and delivered on the Internet. The server then responds by sending the web page you asked for\u2014also through HTTP.<\/p>\n\n\n\n<p>To better understand how all of this works with your ESP32 boards, let\u2019s go over some terms you\u2019ve probably heard before\u2014but might not fully understand yet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Request-response<\/h3>\n\n\n\n<p>Request-response is a message exchange pattern, in which a requestor (your browser) sends a request message to a replier system (the ESP32 as a web server) that receives and processes the request, and returns a message in response.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"770\" height=\"191\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/request-response-esp32.png?resize=770%2C191&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Request Response\" class=\"wp-image-169419\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/request-response-esp32.png?w=770&amp;quality=100&amp;strip=all&amp;ssl=1 770w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/request-response-esp32.png?resize=300%2C74&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/request-response-esp32.png?resize=768%2C191&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 770px) 100vw, 770px\" \/><\/figure><\/div>\n\n\n<p>In most of our projects, the response message will be a web page that displays the latest sensor readings or that provides an interface to control outputs.<\/p>\n\n\n\n<p>This is a simple, yet powerful messaging pattern, especially in client-server architectures.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Client-Server<\/h3>\n\n\n\n<p>When you type a URL in your browser, your device (the client) sends a request to a server using the Hypertext Transfer Protocol (HTTP). The server receives the request and responds\u2014also via HTTP\u2014by sending back the web page. This exchange between clients and servers happens over a computer network.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"271\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/esp32-client-server-communication-f.png?resize=750%2C271&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Client Server Communication explained\" class=\"wp-image-169421\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/esp32-client-server-communication-f.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/esp32-client-server-communication-f.png?resize=300%2C108&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>In simple terms, clients make requests to servers. Servers handle the clients\u2019 requests.&nbsp;<\/p>\n\n\n\n<p>Throughout this tutorial, we\u2019ll treat the ESP32 as the server, and you\u2014using your browser\u2014as the client. In our projects, there\u2019s only one server (the ESP32 board), but there can be multiple clients. These could be different web browsers on various devices like computers, smartphones, or tablets, all connected to the same network, or even multiple browser tabs opened on the same device.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IP Address<\/h3>\n\n\n\n<p>An IP address is a numerical label assigned to each device connected to a computer network.<\/p>\n\n\n\n<p>It is a series of four values separated by periods, with each value ranging from 0 to 255. Here\u2019s an example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>192.168.1.75<\/code><\/pre>\n\n\n\n<p>At home, your devices are connected to a private network through your router (local network). All devices connected to your router are part of your local network. Inside this network, each device has its own IP address.<\/p>\n\n\n\n<p>Devices connected to the same router can access each other via the IP address. Devices outside your local network can\u2019t access your local devices using their local IP address.<\/p>\n\n\n\n<p>When you connect your ESP32 boards to your router, they become part of your local network. So, your ESP32 boards are assigned an IP address.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"359\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-explaining-IP-address.png?resize=750%2C359&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Explaining IP Addresses\" class=\"wp-image-169423\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-explaining-IP-address.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-explaining-IP-address.png?resize=300%2C144&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>On your local network, the IP address of the ESP32 (and other devices) is assigned by the router using something called DHCP (Dynamic Host Configuration Protocol). You don\u2019t need to worry about the details. You just need to know that DHCP automatically assigns an IP address and other network settings to each device on the network.<\/p>\n\n\n\n<p>The router keeps track of every device on the network and maps an IP address to each device every time it joins the network. Two devices on the same network can\u2019t have the same IP address.<\/p>\n\n\n\n<p>Again, when the ESP32 is connected to your router, the IP address it gets is a local address. This means you can only access it from devices that are also connected to the same network. As shown in the previous image, you can access the ESP32 using a computer or smartphone that\u2019s on the same network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wi-Fi Station and Wi-Fi Access Point<\/h3>\n\n\n\n<p>The ESP32 board can act as Wi-Fi Station, Access Point, or both.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Wi-Fi Station<\/h4>\n\n\n\n<p>When the ESP32 is set to work as a Wi-Fi station, it connects to an existing network, like your home router. In this setup, the router gives the ESP32 a unique local IP address. You can then communicate with the ESP32 from other devices (like your phone or computer) that are connected to the same network, simply by using that IP address.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"605\" height=\"320\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Wi-Fi-Station.png?resize=605%2C320&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Station Explained\" class=\"wp-image-169424\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Wi-Fi-Station.png?w=605&amp;quality=100&amp;strip=all&amp;ssl=1 605w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Wi-Fi-Station.png?resize=300%2C159&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 605px) 100vw, 605px\" \/><\/figure><\/div>\n\n\n<p>Since the router is also connected to the internet, we can request information from the internet using our ESP32 boards like data from APIs, publish data to online platforms, use icons and images from the internet in our web server pages, or include JavaScript libraries.&nbsp;<\/p>\n\n\n\n<p>However, in some cases, we may not have a router nearby to connect the ESP32. In this scenario, you must set your ESP32 board as an access point.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Access Point<\/h4>\n\n\n\n<p>When your ESP32 is set up as an Access Point, other devices (such as your smartphone, tablet, or computer) can connect to it without the need for a router; the ESP controls its own Wi-Fi network.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"605\" height=\"320\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?resize=605%2C320&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Access Point\" class=\"wp-image-169425\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?w=605&amp;quality=100&amp;strip=all&amp;ssl=1 605w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?resize=300%2C159&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 605px) 100vw, 605px\" \/><\/figure><\/div>\n\n\n<p>Unlike a router, an ESP32 Access Point doesn\u2019t connect further to a wired network or the Internet, so you can\u2019t access external libraries, publish sensor readings to the cloud, or use services like mail.&nbsp;In most of our examples, we usually set the ESP32 boards as stations. You can easily modify our examples and set the boards as access points instead if that\u2019s more suitable for your projects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Client-Server Communication<\/h3>\n\n\n\n<p>There are several ways to communicate between the client and the server: HTTP Polling, Server-Sent Events (SSE), and WebSocket. We\u2019ll focus on HTTP Polling, which is the easiest protocol to get started with and better understand how web servers work.<\/p>\n\n\n\n<p>We have an eBook dedicated to web servers that explores in great detail those three communication protocols:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\" title=\"\">Build Web Servers with ESP32 and ESP8266 eBook<\/a><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">HTTP Polling<\/h4>\n\n\n\n<p>In HTTP polling, the client repeatedly asks the server for new information. When the server receives a request, it responds with the requested data. The server only sends information when the client asks for it.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"637\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-HTTP-Polling.jpg?resize=750%2C637&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Client-Server Communication HTTP Polling\" class=\"wp-image-169426\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-HTTP-Polling.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-HTTP-Polling.jpg?resize=300%2C255&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>For example, your browser sends a request to the ESP32, and it responds with whatever you\u2019ve programmed it to provide, like a web page showing the latest sensor readings. To keep the web page updated using this method, the browser needs to keep sending requests at regular intervals.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ESP32 Web Server<\/h3>\n\n\n\n<p>Let\u2019s take a look at a practical example with the ESP32 that acts as a web server in the local network.<\/p>\n\n\n\n<p>Typically, a web server with the ESP32 in the local network looks like this: the ESP32 running as a web server is connected via Wi-Fi to your router. Your computer, smartphone, or tablet, are also connected to your router via Wi-Fi or Ethernet cable. So, the ESP32 and your browser are on the same network.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"200\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Client-Connected-1.png?resize=750%2C200&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Acting as a Web Server\" class=\"wp-image-169429\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Client-Connected-1.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Client-Connected-1.png?resize=300%2C80&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>When you type the ESP32 IP address in your browser, you are sending an HTTP request to your ESP32. Then, the ESP32 responds with a response that can contain a value, a reading, HTML text to display a web page, or any other data.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"214\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Clients-Request-Response.png?resize=750%2C214&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server Request Response\" class=\"wp-image-169430\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Clients-Request-Response.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Wi-Fi-Clients-Request-Response.png?resize=300%2C86&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"esp32-web-server-outputs\">2) ESP32 Web Server Example (Control Outputs)<\/h2>\n\n\n\n<p>Based on what we\u2019ve learned so far about web servers and the ESP32, how can you put it all together to build IoT projects? Since the ESP32 has GPIO pins, you can connect sensors, actuators, and other devices, and then control or monitor them through a web interface.<\/p>\n\n\n\n<p>Here\u2019s an example of a web server we\u2019ve built to control an output. The following web page shows up when you enter the ESP32 IP address in a browser.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"459\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=712%2C459&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server - Control Outputs Example\" class=\"wp-image-169431\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?w=712&amp;quality=100&amp;strip=all&amp;ssl=1 712w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=300%2C193&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 712px) 100vw, 712px\" \/><\/figure><\/div>\n\n\n<p>When you press the ON button, the URL changes to the ESP IP address followed by <span class=\"rnthl rntliteral\">\/on<\/span>. The ESP receives a request on that new URL, it checks which URL is being requested, and changes the LED state accordingly.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Press the ON button &gt; request: <span class=\"rnthl rntliteral\">\/on<\/span> &gt; LED turns on<\/li>\n<\/ul>\n\n\n\n<p>When you press the OFF button, a new request is made to the ESP32 in the <span class=\"rnthl rntliteral\">\/off<\/span> URL. The ESP checks once again which URL is being requested and turns the LED off.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Press the OFF button &gt; request: <span class=\"rnthl rntliteral\">\/off<\/span> &gt; LED turns off<\/li>\n<\/ul>\n\n\n\n<p>The same concept can be applied to control multiple outputs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Project Overview<\/h3>\n\n\n\n<p>Before going straight to the project, it is important to outline what our web server will do so that it is easier to follow and understand the steps later on.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The web server you\u2019ll build controls two LEDs: one connected to the ESP32 GPIO 26, and another one to GPIO 27.<\/li>\n\n\n\n<li>You can access the ESP32 web server by typing the ESP32 IP address in a browser on the local network.<\/li>\n\n\n\n<li>By clicking the buttons on your web server, you can instantly change the state of each LED.<\/li>\n<\/ul>\n\n\n\n<p>This is a simple example that illustrates how to build a web server that controls two LEDs. The idea is to replace those LEDs with a&nbsp;<a href=\"https:\/\/makeradvisor.com\/tools\/5v-2-channel-relay-module-optocoupler\/\" target=\"_blank\" rel=\"noreferrer noopener\">relay<\/a>, or any other electronic components you want to control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wiring the Circuit<\/h3>\n\n\n\n<p>Start by building the circuit. Connect two LEDs to your ESP32 as shown in the following schematic diagram\u2014with one LED connected to GPIO 26 and another to GPIO 27.<\/p>\n\n\n\n<p>Here\u2019s a list of parts you need to assemble the circuit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 DOIT DEVKIT V1 Board<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/3mm-5mm-leds-kit-storage-box\/\" target=\"_blank\" rel=\"noreferrer noopener\">2x 5mm LED<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/resistors-kits\/\" target=\"_blank\" rel=\"noreferrer noopener\">2x 220 Ohm resistor (or similar value)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"552\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-connected-to-two-LEDs.png?resize=750%2C552&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Connected to Two LEDs\" class=\"wp-image-169432\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-connected-to-two-LEDs.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-connected-to-two-LEDs.png?resize=300%2C221&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntclgreen\">Recommended reading: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-pinout-reference-gpios\/\" title=\"\">ESP32 Pinout Reference: Which GPIO pins should you use?<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Building the Web Server<\/h3>\n\n\n\n<p>After wiring the circuit, the next step is uploading the code to your ESP32. Copy the code below to your Arduino IDE, but don\u2019t upload it yet. You need to make some changes to make it work.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  \n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  https:\/\/RandomNerdTutorials.com\/esp32-web-server-beginners-guide\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n#include &lt;WiFi.h&gt;\n#include &lt;WebServer.h&gt;\n\n\/\/ Replace with your network credentials\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\n\n\/\/ Assign output variables to GPIO pins\nconst int output26 = 26;\nconst int output27 = 27;\nString output26State = &quot;off&quot;;\nString output27State = &quot;off&quot;;\n\n\/\/ Create a web server object\nWebServer server(80);\n\n\/\/ Function to handle turning GPIO 26 on\nvoid handleGPIO26On() {\n  output26State = &quot;on&quot;;\n  digitalWrite(output26, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 26 off\nvoid handleGPIO26Off() {\n  output26State = &quot;off&quot;;\n  digitalWrite(output26, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 on\nvoid handleGPIO27On() {\n  output27State = &quot;on&quot;;\n  digitalWrite(output27, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 off\nvoid handleGPIO27Off() {\n  output27State = &quot;off&quot;;\n  digitalWrite(output27, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle the root URL and show the current states\nvoid handleRoot() {\n  String html = &quot;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1\\&quot;&gt;&quot;;\n  html += &quot;&lt;link rel=\\&quot;icon\\&quot; href=\\&quot;data:,\\&quot;&gt;&quot;;\n  html += &quot;&lt;style&gt;html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}&quot;;\n  html += &quot;.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}&quot;;\n  html += &quot;.button2 { background-color: #555555; }&lt;\/style&gt;&lt;\/head&gt;&quot;;\n  html += &quot;&lt;body&gt;&lt;h1&gt;ESP32 Web Server&lt;\/h1&gt;&quot;;\n\n  \/\/ Display GPIO 26 controls\n  html += &quot;&lt;p&gt;GPIO 26 - State &quot; + output26State + &quot;&lt;\/p&gt;&quot;;\n  if (output26State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  \/\/ Display GPIO 27 controls\n  html += &quot;&lt;p&gt;GPIO 27 - State &quot; + output27State + &quot;&lt;\/p&gt;&quot;;\n  if (output27State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  html += &quot;&lt;\/body&gt;&lt;\/html&gt;&quot;;\n  server.send(200, &quot;text\/html&quot;, html);\n}\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Initialize the output variables as outputs\n  pinMode(output26, OUTPUT);\n  pinMode(output27, OUTPUT);\n  \/\/ Set outputs to LOW\n  digitalWrite(output26, LOW);\n  digitalWrite(output27, LOW);\n\n  \/\/ Connect to Wi-Fi network\n  Serial.print(&quot;Connecting to &quot;);\n  Serial.println(ssid);\n  WiFi.begin(ssid, password);\n  while (WiFi.status() != WL_CONNECTED) {\n    delay(500);\n    Serial.print(&quot;.&quot;);\n  }\n  Serial.println(&quot;&quot;);\n  Serial.println(&quot;WiFi connected.&quot;);\n  Serial.println(&quot;IP address: &quot;);\n  Serial.println(WiFi.localIP());\n\n  \/\/ Set up the web server to handle different routes\n  server.on(&quot;\/&quot;, handleRoot);\n  server.on(&quot;\/26\/on&quot;, handleGPIO26On);\n  server.on(&quot;\/26\/off&quot;, handleGPIO26Off);\n  server.on(&quot;\/27\/on&quot;, handleGPIO27On);\n  server.on(&quot;\/27\/off&quot;, handleGPIO27Off);\n\n  \/\/ Start the web server\n  server.begin();\n  Serial.println(&quot;HTTP server started&quot;);\n}\n\nvoid loop() {\n  \/\/ Handle incoming client requests\n  server.handleClient();\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Web_Server_Control_Outputs.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Setting Your Network Credentials<\/h4>\n\n\n\n<p>You have to modify the following&nbsp;lines with your network credentials: SSID and password.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \" REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Getting the ESP32 IP Address<\/h4>\n\n\n\n<p>Now, you can upload the code, and it will work straight away. Don\u2019t forget to check if you have the right board and COM port selected.<\/p>\n\n\n\n<p>Open the Serial Monitor at a baud rate of 115200.<\/p>\n\n\n\n<p>The ESP32 connects to Wi-Fi and prints its IP address on the Serial Monitor. Copy that IP address because you need it to access the ESP32 web server.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"666\" height=\"375\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-get-ip-address-serial-monitor.png?resize=666%2C375&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server - Get IP address on Serial Monitor\" class=\"wp-image-169433\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-get-ip-address-serial-monitor.png?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-get-ip-address-serial-monitor.png?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p><strong>Note:<\/strong> If nothing shows up on the Serial Monitor, press the ESP32 \u201cEN\u201d button (ENABLE\/RESET button next to the microUSB port).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Accessing the Web Server<\/h3>\n\n\n\n<p>Open your browser, paste the ESP32 IP address, and you\u2019ll see the following page.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"459\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=712%2C459&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Basic Web Server Example - control outputs\" class=\"wp-image-169431\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?w=712&amp;quality=100&amp;strip=all&amp;ssl=1 712w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=300%2C193&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 712px) 100vw, 712px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">Testing the Web Server<\/h3>\n\n\n\n<p>Let\u2019s test the web server. Click the button to turn GPIO 26 ON. You can see on the Serial Monitor that the ESP32 receives a request on the <strong>\/26\/on<\/strong> URL.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"459\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-control-output-explained.png?resize=712%2C459&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Basic Web Server Example - control outputs\" class=\"wp-image-169439\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-control-output-explained.png?w=712&amp;quality=100&amp;strip=all&amp;ssl=1 712w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-web-server-control-output-explained.png?resize=300%2C193&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 712px) 100vw, 712px\" \/><\/figure><\/div>\n\n\n<p>When the ESP receives that request, it turns the LED attached to GPIO 26 ON, and its state is also updated on the web page. Test the button for GPIO 27 and see that it works similarly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-basic-web-server.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Basic Web Server - How it Works\" class=\"wp-image-169441\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-basic-web-server.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-basic-web-server.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>You can also access the web server on your smartphone as long as it is connected to the same network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the Code Works<\/h3>\n\n\n\n<p>Now, let\u2019s take a closer look at the code to see how it works.<\/p>\n\n\n\n<p>The first thing you need to do is include the necessary libraries for Wi-Fi connectivity and set up a web server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;WiFi.h&gt;\n#include &lt;WebServer.h&gt;<\/code><\/pre>\n\n\n\n<p>As mentioned previously, you need to insert your ssid and password in the following lines inside the double quotes.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \" REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<p>Assign GPIO pins to each of your outputs. In this example, we are using GPIO 26 and GPIO 27, but you can use any other suitable GPIOs.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const int output26 = 26;\nconst int output27 = 27;<\/code><\/pre>\n\n\n\n<p>Then, create variables to store the states of those outputs. You can add more outputs by defining additional variables.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String output26State = \"off\";\nString output27State = \"off\";<\/code><\/pre>\n\n\n\n<p>Create a web server object on port 80 called <span class=\"rnthl rntliteral\">server<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create a web server object\nWebServer server(80);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">setup()<\/h3>\n\n\n\n<p>Now, let\u2019s go into the <span class=\"rnthl rntliteral\">setup()<\/span>. First, we start a serial communication at a baud rate of 115200 for debugging purposes.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>You also define your GPIOs as OUTPUTs and set them to <span class=\"rnthl rntliteral\">LOW<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize the output variables as outputs\npinMode(output26, OUTPUT);\npinMode(output27, OUTPUT);\n\/\/ Set outputs to LOW\ndigitalWrite(output26, LOW);\ndigitalWrite(output27, LOW);<\/code><\/pre>\n\n\n\n<p>The following lines begin the Wi-Fi connection with <span class=\"rnthl rntliteral\">WiFi.begin(ssid, password)<\/span>, wait for a successful connection, and print the ESP32 IP address in the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Connect to Wi-Fi network\nSerial.print(\"Connecting to \");\nSerial.println(ssid);\nWiFi.begin(ssid, password);\nwhile (WiFi.status() != WL_CONNECTED) {\n  delay(500);\n  Serial.print(\".\");\n}\nSerial.println(\"\");\nSerial.println(\"WiFi connected.\");\nSerial.println(\"IP address: \");\nSerial.println(WiFi.localIP());<\/code><\/pre>\n\n\n\n<p class=\"rntbox rntclgreen\">Read this guide to learn more about <strong>Wi-Fi with the ESP32<\/strong>: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-useful-wi-fi-functions-arduino\/\">ESP32 Useful Wi-Fi Library Functions (Arduino IDE)<\/a>.<\/p>\n\n\n\n<p>Finally, we set up the web server and define the routes it should handle. These routes will be requested when you click on the different buttons on the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set up the web server to handle different routes\nserver.on(\"\/\", handleRoot);\nserver.on(\"\/26\/on\", handleGPIO26On);\nserver.on(\"\/26\/off\", handleGPIO26Off);\nserver.on(\"\/27\/on\", handleGPIO27On);\nserver.on(\"\/27\/off\", handleGPIO27Off);\n\n\/\/ Start the web server\nserver.begin();\nSerial.println(\"HTTP server started\");<\/code><\/pre>\n\n\n\n<p>For example, when you make a request on the root <strong>\/<\/strong> URL (you simply paste the ESP32 IP address on the web browser), it will run the <span class=\"rnthl rntliteral\">handleRoot()<\/span> function. When you click on the GPIO 26 ON button, it will make a request on the <strong>\/26\/on<\/strong> route and the board will run the <span class=\"rnthl rntliteral\">handleGPIO26On()<\/span> function, and so on\u2026 Those functions are defined at the beginning of the code before the <span class=\"rnthl rntliteral\">setup()<\/span>. We\u2019ll take a look at them next.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">loop()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, we continuously listen for incoming client requests. This ensures that the ESP32 is always ready to respond to requests from your browser.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.handleClient();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Handling GPIO Control<\/h4>\n\n\n\n<p>On the web page, you\u2019ve seen that you have four buttons to control the GPIOs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GPIO 26 ON button &gt; request: <strong>\/26\/on<\/strong> &gt; function: <span class=\"rnthl rntliteral\">handleGPIO26On()<\/span><\/li>\n\n\n\n<li>GPIO 26 OFF button &gt; request: <strong>\/26\/off<\/strong> &gt; function: <span class=\"rnthl rntliteral\">handleGPIO26Off()<\/span><\/li>\n\n\n\n<li>GPIO 27 ON button &gt; request: <strong>\/27\/on<\/strong> &gt; function: <span class=\"rnthl rntliteral\">handleGPIO27On()<\/span><\/li>\n\n\n\n<li>GPIO 27 OFF button &gt; request: <strong>\/27\/off<\/strong> &gt; function: <span class=\"rnthl rntliteral\">handleGPIO27Off()<\/span><\/li>\n<\/ul>\n\n\n\n<p>Let\u2019s take a look at the GPIO 26 ON button. When you click that button, it makes a request to the ESP32 on the <strong>\/26\/on<\/strong> URL. When that happens, the <span class=\"rnthl rntliteral\">handleGPIO26On()<\/span> function will run.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Function to handle turning GPIO 26 on\nvoid handleGPIO26On() {\n  output26State = \"on\";\n  digitalWrite(output26, HIGH);\n  handleRoot();\n}<\/code><\/pre>\n\n\n\n<p>That function updates the state of the GPIO on the <span class=\"rnthl rntliteral\">output26State<\/span> variable and turns the GPIO on. Finally, it calls the <span class=\"rnthl rntliteral\">handleRoot()<\/span> function to display the web page with the right GPIO state.<\/p>\n\n\n\n<p>This works similarly to the other buttons and corresponding routes and functions. Notice that all of these functions call the <span class=\"rnthl rntliteral\">handleRoot()<\/span> function to display the web page with the right GPIO states.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Function to handle turning GPIO 26 off\nvoid handleGPIO26Off() {\n  output26State = \"off\";\n  digitalWrite(output26, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 on\nvoid handleGPIO27On() {\n  output27State = \"on\";\n  digitalWrite(output27, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 off\nvoid handleGPIO27Off() {\n  output27State = \"off\";\n  digitalWrite(output27, LOW);\n  handleRoot();\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Displaying the Web Page<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">handleRoot()<\/span> function is responsible for generating the web page. It sends the HTML and CSS needed to build the page. The HTML and CSS text required to build the web page are saved in the <span class=\"rnthl rntliteral\">html<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Function to handle the root URL and show the current states\nvoid handleRoot() {\n  String html = \"&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta name=\\\"viewport\\\"\n                 content=\\\"width=device-width, initial-scale=1\\\"&gt;\";\n  html += \"&lt;link rel=\\\"icon\\\" href=\\\"data:,\\\"&gt;\";\n  html += \"&lt;style&gt;html { font-family: Helvetica; display: inline-block; \n           margin: 0px auto; text-align: center;}\";\n  html += \".button { background-color: #4CAF50; border: none; color: white; \n           padding: 16px 40px; text-decoration: none; font-size: 30px; \n           margin: 2px; cursor: pointer;}\";\n  html += \".button2 { background-color: #555555; }&lt;\/style&gt;&lt;\/head&gt;\";\n  html += \"&lt;body&gt;&lt;h1&gt;ESP32 Web Server&lt;\/h1&gt;\";\n\n  \/\/ Display GPIO 26 controls\n  html += \"&lt;p&gt;GPIO 26 - State \" + output26State + \"&lt;\/p&gt;\";\n  if (output26State == \"off\") {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/26\/on\\\"&gt;&lt;button class=\\\"button\\\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n  } else {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/26\/off\\\"&gt;&lt;button \n             class=\\\"button button2\\\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n  }\n\n  \/\/ Display GPIO 27 controls\n  html += \"&lt;p&gt;GPIO 27 - State \" + output27State + \"&lt;\/p&gt;\";\n  if (output27State == \"off\") {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/27\/on\\\"&gt;&lt;button class=\\\"button\\\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n  } else {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/27\/off\\\"&gt;&lt;button \n             class=\\\"button button2\\\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n  }\n\n  html += \"&lt;\/body&gt;&lt;\/html&gt;\";\n  server.send(200, \"text\/html\", html);\n}\n<\/code><\/pre>\n\n\n\n<p>Note that to generate the buttons, we use <span class=\"rnthl rntliteral\">if<\/span> and <span class=\"rnthl rntliteral\">else<\/span> statements to display the correct button and state according to the current state of the GPIOs.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Display GPIO 26 controls\nhtml += \"&lt;p&gt;GPIO 26 - State \" + output26State + \"&lt;\/p&gt;\";\nif (output26State == \"off\") {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/26\/on\\\"&gt;&lt;button class=\\\"button\\\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n} else {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/26\/off\\\"&gt;&lt;button class=\\\"button button2\\\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n}\n\n\/\/ Display GPIO 27 controls\nhtml += \"&lt;p&gt;GPIO 27 - State \" + output27State + \"&lt;\/p&gt;\";\nif (output27State == \"off\") {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/27\/on\\\"&gt;&lt;button class=\\\"button\\\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n} else {\n    html += \"&lt;p&gt;&lt;a href=\\\"\/27\/off\\\"&gt;&lt;button class=\\\"button button2\\\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;\";\n}<\/code><\/pre>\n\n\n\n<p>Finally, the web page is sent to the client:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.send(200, \"text\/html\", HTML);<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"web-server-authentication\">3) Add Authentication to your Web Server<\/h2>\n\n\n\n<p>In some projects, you might want to keep your ESP32 web server protected with username and password. The following code protects access to the web server with a username and password.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  \n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  https:\/\/RandomNerdTutorials.com\/esp32-web-server-beginners-guide\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n#include &lt;WiFi.h&gt;\n#include &lt;WebServer.h&gt;\n\n\/\/ Replace with your network credentials\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\n\n\/\/ Username and password for web page access\nconst char* http_username = &quot;admin&quot;;\nconst char* http_password = &quot;admin&quot;;\n\n\/\/ Assign output variables to GPIO pins\nconst int output26 = 26;\nconst int output27 = 27;\nString output26State = &quot;off&quot;;\nString output27State = &quot;off&quot;;\n\n\/\/ Create a web server object\nWebServer server(80);\n\n\/\/ Function to authenticate user\nbool isAuthenticated() {\n  if (!server.authenticate(http_username, http_password)) {\n    server.requestAuthentication();\n    return false;\n  }\n  return true;\n}\n\n\/\/ Function to handle turning GPIO 26 on\nvoid handleGPIO26On() {\n  if (!isAuthenticated()) return;\n  output26State = &quot;on&quot;;\n  digitalWrite(output26, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 26 off\nvoid handleGPIO26Off() {\n  if (!isAuthenticated()) return;\n  output26State = &quot;off&quot;;\n  digitalWrite(output26, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 on\nvoid handleGPIO27On() {\n  if (!isAuthenticated()) return;\n  output27State = &quot;on&quot;;\n  digitalWrite(output27, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 off\nvoid handleGPIO27Off() {\n  if (!isAuthenticated()) return;\n  output27State = &quot;off&quot;;\n  digitalWrite(output27, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle the root URL and show the current states\nvoid handleRoot() {\n  if (!isAuthenticated()) return;\n\n  String html = &quot;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1\\&quot;&gt;&quot;;\n  html += &quot;&lt;link rel=\\&quot;icon\\&quot; href=\\&quot;data:,\\&quot;&gt;&quot;;\n  html += &quot;&lt;style&gt;html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}&quot;;\n  html += &quot;.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}&quot;;\n  html += &quot;.button2 { background-color: #555555; }&lt;\/style&gt;&lt;\/head&gt;&quot;;\n  html += &quot;&lt;body&gt;&lt;h1&gt;ESP32 Web Server&lt;\/h1&gt;&quot;;\n\n  \/\/ Display GPIO 26 controls\n  html += &quot;&lt;p&gt;GPIO 26 - State &quot; + output26State + &quot;&lt;\/p&gt;&quot;;\n  if (output26State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  \/\/ Display GPIO 27 controls\n  html += &quot;&lt;p&gt;GPIO 27 - State &quot; + output27State + &quot;&lt;\/p&gt;&quot;;\n  if (output27State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  html += &quot;&lt;\/body&gt;&lt;\/html&gt;&quot;;\n  server.send(200, &quot;text\/html&quot;, html);\n}\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Initialize the output variables as outputs\n  pinMode(output26, OUTPUT);\n  pinMode(output27, OUTPUT);\n  \/\/ Set outputs to LOW\n  digitalWrite(output26, LOW);\n  digitalWrite(output27, LOW);\n\n  \/\/ Connect to Wi-Fi network\n  Serial.print(&quot;Connecting to &quot;);\n  Serial.println(ssid);\n  WiFi.begin(ssid, password);\n  while (WiFi.status() != WL_CONNECTED) {\n    delay(500);\n    Serial.print(&quot;.&quot;);\n  }\n  Serial.println(&quot;&quot;);\n  Serial.println(&quot;WiFi connected.&quot;);\n  Serial.println(&quot;IP address: &quot;);\n  Serial.println(WiFi.localIP());\n\n  \/\/ Set up the web server to handle different routes with authentication\n  server.on(&quot;\/&quot;, handleRoot);\n  server.on(&quot;\/26\/on&quot;, handleGPIO26On);\n  server.on(&quot;\/26\/off&quot;, handleGPIO26Off);\n  server.on(&quot;\/27\/on&quot;, handleGPIO27On);\n  server.on(&quot;\/27\/off&quot;, handleGPIO27Off);\n\n  \/\/ Start the web server\n  server.begin();\n  Serial.println(&quot;HTTP server started&quot;);\n}\n\nvoid loop() {\n  \/\/ Handle incoming client requests\n  server.handleClient();\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Web_Server_Authentication.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Adding Username and Password<\/h3>\n\n\n\n<p>The following code protects your web server with username and password. By default, the username is&nbsp;<em>admin<\/em>, and the password is&nbsp;<em>admin<\/em>. <\/p>\n\n\n\n<p>You can add the desired username and password in the following lines.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* http_username = \"admin\";\nconst char* http_password = \"admin\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handling Authentication and Requests<\/h3>\n\n\n\n<p>To protect your web page, we added a simple authentication mechanism. Before accessing any part of the web server, the user must enter the correct username and password. This is handled by the <span class=\"rnthl rntliteral\">isAuthenticated()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>bool isAuthenticated() {\n  if (!server.authenticate(http_username, http_password)) {\n    server.requestAuthentication();\n    return false;\n  }\n  return true;\n}<\/code><\/pre>\n\n\n\n<p>This function checks if the user is authenticated. If not, it prompts the user to enter the credentials. The function returns true if the user is authenticated, or <span class=\"rnthl rntliteral\">false<\/span> otherwise.<\/p>\n\n\n\n<p>Before sending any response to the client, we check if the user is authenticated. Notice the use of the following line before returning anything to the client.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (!isAuthenticated()) return;<\/code><\/pre>\n\n\n\n<p>For example, in the case of the <span class=\"rnthl rntliteral\">handleGPIO26On()<\/span> function:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Function to handle turning GPIO 26 on\nvoid handleGPIO26On() {\n  if (!isAuthenticated()) return;\n  output26State = \"on\";\n  digitalWrite(output26, HIGH);\n  handleRoot();\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Testing the Web Server<\/h3>\n\n\n\n<p>Now, when you try to access your ESP32 IP address, you\u2019ll be required to enter your username and password. Then, press the \u201c<strong>Sign in<\/strong>\u201d button:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"699\" height=\"435\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-with-Authentication.png?resize=699%2C435&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server with Authentication\" class=\"wp-image-169446\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-with-Authentication.png?w=699&amp;quality=100&amp;strip=all&amp;ssl=1 699w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-with-Authentication.png?resize=300%2C187&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 699px) 100vw, 699px\" \/><\/figure><\/div>\n\n\n<p>You\u2019ll be able to access the web page to control outputs.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"459\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=712%2C459&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Basic Web Server Example - control outputs\" class=\"wp-image-169431\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?w=712&amp;quality=100&amp;strip=all&amp;ssl=1 712w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-Example-Control-Outputs.png?resize=300%2C193&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 712px) 100vw, 712px\" \/><\/figure><\/div>\n\n\n<p>If you enter the wrong password, the Sign in box will show up again.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"access-point\">4) ESP32 Web Server Access Point (AP)<\/h2>\n\n\n\n<p>Imagine that you want to control your ESP32 using a web page on your smartphone, but the ESP32 doesn&#8217;t have access to a router to connect to the internet (so, you can&#8217;t set it as a wi-fi station like in previous examples). In that case, you can set the ESP32 as an access point.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"605\" height=\"320\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?resize=605%2C320&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server Access Point AP\" class=\"wp-image-169425\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?w=605&amp;quality=100&amp;strip=all&amp;ssl=1 605w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-access-point.png?resize=300%2C159&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 605px) 100vw, 605px\" \/><\/figure><\/div>\n\n\n<p>When your ESP32 is set up as an Access Point, other devices (such as your smartphone, tablet, or computer) can connect to it without the need for a router. Let&#8217;s see how to implement that.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Set the ESP32 as an Access Point<\/h3>\n\n\n\n<p>To set the ESP32 as an access point, set the Wi-Fi mode to access point as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.mode(WIFI_AP);<\/code><\/pre>\n\n\n\n<p>And then, use the <span class=\"rnthl rntliteral\">softAP()<\/span> method as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.softAP(ssid, password);<\/code><\/pre>\n\n\n\n<p><span class=\"rnthl rntliteral\">ssid<\/span> is the name you want to give to the ESP32 access point, and the <span class=\"rnthl rntliteral\">password<\/span> parameter is the password for the access point. If you don\u2019t want to set a password, set it to <span class=\"rnthl rntliteral\">NULL<\/span>.<\/p>\n\n\n\n<p>There are also other optional parameters you can pass to the <span class=\"rnthl rntliteral\">softAP()<\/span> method. Here are all the parameters:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.softAP(const char* ssid, const char* password, int channel, int ssid_hidden, int max_connection);<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">ssid<\/span>: name for the access point \u2013 maximum of 63 characters;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">password<\/span>:&nbsp;minimum of 8 characters; set to&nbsp;NULL&nbsp;if you want the access point to be open;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">channel<\/span>: Wi-Fi channel number (1-13)<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">ssid_hidden<\/span>: (0 = broadcast SSID, 1 = hide SSID)<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">max_connection<\/span>: maximum simultaneous connected clients (1-4)<\/li>\n<\/ul>\n\n\n\n<p>The following code creates the same web server built previously, but in access point mode.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  \n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  https:\/\/RandomNerdTutorials.com\/esp32-web-server-beginners-guide\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n#include &lt;WiFi.h&gt;\n#include &lt;WebServer.h&gt;\n\n\/\/ Replace with SSID and PASSWORD for the ESP32 ACCESS POINT\n\/\/ (for testing you can leave the default)\nconst char* ssid = &quot;ESP32_ACCESS_POINT&quot;;\nconst char* password = &quot;pass123456&quot;;\n\n\/\/ Assign output variables to GPIO pins\nconst int output26 = 26;\nconst int output27 = 27;\nString output26State = &quot;off&quot;;\nString output27State = &quot;off&quot;;\n\n\/\/ Create a web server object\nWebServer server(80);\n\n\/\/ Function to handle turning GPIO 26 on\nvoid handleGPIO26On() {\n  output26State = &quot;on&quot;;\n  digitalWrite(output26, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 26 off\nvoid handleGPIO26Off() {\n  output26State = &quot;off&quot;;\n  digitalWrite(output26, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 on\nvoid handleGPIO27On() {\n  output27State = &quot;on&quot;;\n  digitalWrite(output27, HIGH);\n  handleRoot();\n}\n\n\/\/ Function to handle turning GPIO 27 off\nvoid handleGPIO27Off() {\n  output27State = &quot;off&quot;;\n  digitalWrite(output27, LOW);\n  handleRoot();\n}\n\n\/\/ Function to handle the root URL and show the current states\nvoid handleRoot() {\n  String html = &quot;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1\\&quot;&gt;&quot;;\n  html += &quot;&lt;link rel=\\&quot;icon\\&quot; href=\\&quot;data:,\\&quot;&gt;&quot;;\n  html += &quot;&lt;style&gt;html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}&quot;;\n  html += &quot;.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}&quot;;\n  html += &quot;.button2 { background-color: #555555; }&lt;\/style&gt;&lt;\/head&gt;&quot;;\n  html += &quot;&lt;body&gt;&lt;h1&gt;ESP32 Web Server&lt;\/h1&gt;&quot;;\n\n  \/\/ Display GPIO 26 controls\n  html += &quot;&lt;p&gt;GPIO 26 - State &quot; + output26State + &quot;&lt;\/p&gt;&quot;;\n  if (output26State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/26\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  \/\/ Display GPIO 27 controls\n  html += &quot;&lt;p&gt;GPIO 27 - State &quot; + output27State + &quot;&lt;\/p&gt;&quot;;\n  if (output27State == &quot;off&quot;) {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/on\\&quot;&gt;&lt;button class=\\&quot;button\\&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  } else {\n    html += &quot;&lt;p&gt;&lt;a href=\\&quot;\/27\/off\\&quot;&gt;&lt;button class=\\&quot;button button2\\&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;&lt;\/p&gt;&quot;;\n  }\n\n  html += &quot;&lt;\/body&gt;&lt;\/html&gt;&quot;;\n  server.send(200, &quot;text\/html&quot;, html);\n}\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Initialize the output variables as outputs\n  pinMode(output26, OUTPUT);\n  pinMode(output27, OUTPUT);\n  \/\/ Set outputs to LOW\n  digitalWrite(output26, LOW);\n  digitalWrite(output27, LOW);\n\n  \/\/ Set the ESP32 as access point\n  Serial.print(&quot;Setting as access point &quot;);\n  WiFi.mode(WIFI_AP);\n  WiFi.softAP(ssid, password);\n\n  Serial.println(&quot;&quot;);\n  Serial.println(&quot;ESP32 Wi-Fi Access Point ready!&quot;);\n  IPAddress IP = WiFi.softAPIP();\n  Serial.print(&quot;AP IP address: &quot;);\n  Serial.println(IP);\n\n  \/\/ Set up the web server to handle different routes\n  server.on(&quot;\/&quot;, handleRoot);\n  server.on(&quot;\/26\/on&quot;, handleGPIO26On);\n  server.on(&quot;\/26\/off&quot;, handleGPIO26Off);\n  server.on(&quot;\/27\/on&quot;, handleGPIO27On);\n  server.on(&quot;\/27\/off&quot;, handleGPIO27Off);\n\n  \/\/ Start the web server\n  server.begin();\n  Serial.println(&quot;HTTP server started&quot;);\n}\n\nvoid loop() {\n  \/\/ Handle incoming client requests\n  server.handleClient();\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Web_Server_Access_Point.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>You can upload the previous code to your board.<\/p>\n\n\n\n<p>The ESP32 will set up its own Wi-Fi network. Now, to access the web server, you need to connect your computer, or smartphone to the ESP32 network.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"329\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Access-Point-Ready-Serial-Monitor.png?resize=750%2C329&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Setting the ESP32 as an Access Point\" class=\"wp-image-169486\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Access-Point-Ready-Serial-Monitor.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Access-Point-Ready-Serial-Monitor.png?resize=300%2C132&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>In your smartphone, go to the Wi-Fi settings and connect to the <span class=\"rnthl rntliteral\">ESP32_ACCESS_POINT<\/span>. The password is <span class=\"rnthl rntliteral\">pass123456<\/span> (if you left the default values). Then, open the browser and type the ESP32 IP address: 192.168.4.1.<\/p>\n\n\n\n<div class=\"wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular\"><div class=\"\"><div class=\"tiled-gallery__gallery\"><div class=\"tiled-gallery__row\"><div class=\"tiled-gallery__col\" style=\"flex-basis:33.33333%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Wi-Fi-Settings-ESP32-Access-Point.jpg?strip=info&#038;w=350&#038;ssl=1 350w\" alt=\"Connecting to the ESP32 Access Point\" data-height=\"758\" data-id=\"169484\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=169484#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Wi-Fi-Settings-ESP32-Access-Point.jpg\" data-width=\"350\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Wi-Fi-Settings-ESP32-Access-Point.jpg?ssl=1\" data-amp-layout=\"responsive\" aria-label=\"Open image 1 of 3 in full-screen\"\/><\/figure><\/div><div class=\"tiled-gallery__col\" style=\"flex-basis:33.33333%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Connecting-to-ESP32-Access-Point-Insert-Password.jpg?strip=info&#038;w=350&#038;ssl=1 350w\" alt=\"Inserting Password for ESP32 Access Point\" data-height=\"758\" data-id=\"169483\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=169483#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Connecting-to-ESP32-Access-Point-Insert-Password.jpg\" data-width=\"350\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Connecting-to-ESP32-Access-Point-Insert-Password.jpg?ssl=1\" data-amp-layout=\"responsive\" aria-label=\"Open image 2 of 3 in full-screen\"\/><\/figure><\/div><div class=\"tiled-gallery__col\" style=\"flex-basis:33.33333%\"><figure class=\"tiled-gallery__item\"><img decoding=\"async\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-in-access-point-mode.jpg?strip=info&#038;w=350&#038;ssl=1 350w\" alt=\"ESP32 Web Server on Access Point\" data-height=\"758\" data-id=\"169485\" data-link=\"https:\/\/randomnerdtutorials.com\/?attachment_id=169485#main\" data-url=\"https:\/\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-in-access-point-mode.jpg\" data-width=\"350\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Server-in-access-point-mode.jpg?ssl=1\" data-amp-layout=\"responsive\" aria-label=\"Open image 3 of 3 in full-screen\"\/><\/figure><\/div><\/div><\/div><\/div><\/div>\n\n\n\n<p>Now, you should be able to control the ESP32 without the need for a router. You&#8217;re using the ESP32 Wi-Fi network.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"esp32-web-server-display-readings\">5) ESP32 Web Server Example (Display Sensor Readings)<\/h2>\n\n\n\n<p>To finish our ESP32 web servers tutorial, we&#8217;ll create a simple web server example to display sensor readings. We&#8217;ll display temperature, humidity, and pressure readings from a BME280 sensor, but you can use any other sensor.<\/p>\n\n\n\n<p class=\"rntbox rntclblue\"><strong>Recommended reading:<\/strong> <a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\" title=\"\">ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"616\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/06\/bme280-sensor.jpg?resize=1024%2C616&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"BME280 Temperature, Humidity, and pressure sensor\" class=\"wp-image-86437\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/06\/bme280-sensor.jpg?resize=1024%2C616&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/06\/bme280-sensor.jpg?resize=300%2C180&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/06\/bme280-sensor.jpg?resize=768%2C462&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/06\/bme280-sensor.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntclgray\">We have guides for more than 30 sensors and modules with the ESP32, check them out here: <strong><a href=\"https:\/\/randomnerdtutorials.com\/esp32-guides-sensors-modules\/\" title=\"\">ESP32: 30+ Free Guides for Sensors and Modules<\/a><\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Project Overview<\/h3>\n\n\n\n<p>In this example, we&#8217;ll display sensor readings from a BME280 on a table. The web page to display the readings will look as follows:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"916\" height=\"500\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-with-BME280-Web-Server-Sensors-Readings.png?resize=916%2C500&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 web Server display BME 280 sensor readings\" class=\"wp-image-169513\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-with-BME280-Web-Server-Sensors-Readings.png?w=916&amp;quality=100&amp;strip=all&amp;ssl=1 916w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-with-BME280-Web-Server-Sensors-Readings.png?resize=300%2C164&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-with-BME280-Web-Server-Sensors-Readings.png?resize=768%2C419&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 916px) 100vw, 916px\" \/><\/figure><\/div>\n\n\n<p>The temperature, humidity, pressure, and altitude values will be concatenated with the HTML string. When the ESP32 receives a request on the root URL, we&#8217;ll send the HTML string concatenated with the current sensor readings. <\/p>\n\n\n\n<p>To get new readings, you need to refresh the web page to make a new request.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a Table in HTML<\/h3>\n\n\n\n<p>In this example, we\u2019re displaying the BME280 sensor readings on a table. So, we need to write HTML text to build a table.<\/p>\n\n\n\n<p>To create a table in HTML you use the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;table&gt;&nbsp;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/table&gt;<\/span>&nbsp;<\/span> tags.<\/p>\n\n\n\n<p>To create a row, you use the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;tr&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/tr&gt;<\/span><\/span> tags. The table heading is defined using the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;th&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/th&gt;<\/span><\/span> tags, and each table cell is defined using the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;td&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/td&gt;<\/span><\/span> tags.<\/p>\n\n\n\n<p>To create a table for our readings, you use the following html text:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;table&gt;\n  &lt;tr&gt;\n    &lt;th&gt;MEASUREMENT&lt;\/th&gt;\n    &lt;th&gt;VALUE&lt;\/th&gt;\n  &lt;\/tr&gt;\n  &lt;tr&gt;\n    &lt;td&gt;Temp. Celsius&lt;\/td&gt;\n    &lt;td&gt;--- *C&lt;\/td&gt;\n  &lt;\/tr&gt;\n  &lt;tr&gt;\n    &lt;td&gt;Temp. Fahrenheit&lt;\/td&gt;\n    &lt;td&gt;--- *F&lt;\/td&gt;\n  &lt;\/tr&gt;\n  &lt;tr&gt;\n    &lt;td&gt;Pressure&lt;\/td&gt;\n    &lt;td&gt;--- hPa&lt;\/td&gt;\n  &lt;\/tr&gt;\n  &lt;tr&gt;\n    &lt;td&gt;Approx. Altitude&lt;\/td&gt;\n    &lt;td&gt;--- meters&lt;\/td&gt;&lt;\/tr&gt;\n  &lt;tr&gt;\n    &lt;td&gt;Humidity&lt;\/td&gt;\n    &lt;td&gt;--- %&lt;\/td&gt;\n  &lt;\/tr&gt;\n&lt;\/table&gt;<\/code><\/pre>\n\n\n\n<p>We create the header of the table with a cell called <span class=\"rnthl rntliteral\">MEASUREMENT<\/span>, and another named <span class=\"rnthl rntliteral\">VALUE<\/span>.<\/p>\n\n\n\n<p>Then, we create six rows to display each of the readings using the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;tr&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/tr&gt;<\/span><\/span>tags. Inside each row, we create two cells, using the&nbsp;<span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;td&gt;<\/span><\/span>&nbsp;and&nbsp;<span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/td&gt;<\/span><\/span> tags, one with the name of the measurement and another to hold the measurement value. The three dashes \u201c- &#8211; -\u201d should then be replaced with the actual measurements from the BME280 sensor.<\/p>\n\n\n\n<p>You can save this text as&nbsp;<em>table.html<\/em>, drag the file into your browser, and see the result. The previous HTML text creates the following table.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"662\" height=\"318\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Basic-HTML-Table-for-BME280-Sensor-Readings.png?resize=662%2C318&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Basic HTML Table for Sensor Readings\" class=\"wp-image-169519\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Basic-HTML-Table-for-BME280-Sensor-Readings.png?w=662&amp;quality=100&amp;strip=all&amp;ssl=1 662w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Basic-HTML-Table-for-BME280-Sensor-Readings.png?resize=300%2C144&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 662px) 100vw, 662px\" \/><\/figure><\/div>\n\n\n<p>The table doesn\u2019t have any applied styles. You can use CSS to style the table. You can read the tutorial in the following link to learn how to style a table:&nbsp;<a href=\"https:\/\/www.w3schools.com\/cSS\/css_table.asp\" target=\"_blank\" rel=\"noreferrer noopener\">CSS Styling Tables<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wiring the Circuit<\/h3>\n\n\n\n<p>Wire the sensor to the ESP32 SDA and SCL pins, as shown in the following schematic diagram. For the ESP32 Devkit board, the default pins are GPIO 21 (SDA) and GPIO 22 (SCL). Double-check the default pins for the board you\u2019re using.<\/p>\n\n\n\n<p>Here\u2019s a list of parts you need to build this circuit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 DOIT DEVKIT V1 Board<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/bme280-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME280 sensor module<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"675\" height=\"670\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=675%2C670&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with BME280 Wiring Diagram\" class=\"wp-image-99755\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?w=675&amp;quality=100&amp;strip=all&amp;ssl=1 675w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=300%2C298&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=150%2C150&amp;quality=100&amp;strip=all&amp;ssl=1 150w\" sizes=\"(max-width: 675px) 100vw, 675px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">Display BME280 Sensor Readings on Web Server &#8211; Code<\/h3>\n\n\n\n<p>Now that you know how to build a table to display the results and the basics of creating a web server, it\u2019s time for the code to create it. If you\u2019ve followed the previous examples, you&#8217;ll be familiar with most lines of code.<\/p>\n\n\n\n<p>Copy the following code to your Arduino IDE. Before uploading, you need to insert your SSID and password.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*  \n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  https:\/\/RandomNerdTutorials.com\/esp32-web-server-beginners-guide\/\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n*\/\n\n#include &lt;WiFi.h&gt;\n#include &lt;Wire.h&gt;\n#include &lt;Adafruit_BME280.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &lt;WebServer.h&gt;\n\n#define SEALEVELPRESSURE_HPA (1013.25)\n\nAdafruit_BME280 bme; \/\/ I2C\n\n\/\/ Replace with your network credentials\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\n\n\/\/ Create an instance of the WebServer on port 80\nWebServer server(80);\n\nvoid handleRoot() {\n  String html = &quot;&lt;!DOCTYPE html&gt;&lt;html&gt;&quot;;\n  html += &quot;&lt;head&gt;&lt;meta name=\\&quot;viewport\\&quot; content=\\&quot;width=device-width, initial-scale=1\\&quot;&gt;&quot;;\n  html += &quot;&lt;link rel=\\&quot;icon\\&quot; href=\\&quot;data:,\\&quot;&gt;&quot;;\n  html += &quot;&lt;style&gt;body { text-align: center; font-family: \\&quot;Trebuchet MS\\&quot;, Arial;}&quot;;\n  html += &quot;table { border-collapse: collapse; width:60%; margin-left:auto; margin-right:auto; }&quot;;\n  html += &quot;th { padding: 10px; background-color: #0043af; color: white; }&quot;;\n  html += &quot;tr { border: 1px solid #ddd; padding: 10px; }&quot;;\n  html += &quot;tr:hover { background-color: #bcbcbc; }&quot;;\n  html += &quot;td { border: none; padding: 8px; }&quot;;\n  html += &quot;.sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }&lt;\/style&gt;&lt;\/head&gt;&quot;;\n  html += &quot;&lt;body&gt;&lt;h1&gt;ESP32 with BME280&lt;\/h1&gt;&quot;;\n  html += &quot;&lt;table&gt;&lt;tr&gt;&lt;th&gt;MEASUREMENT&lt;\/th&gt;&lt;th&gt;VALUE&lt;\/th&gt;&lt;\/tr&gt;&quot;;\n  html += &quot;&lt;tr&gt;&lt;td&gt;Temp. Celsius&lt;\/td&gt;&lt;td&gt;&lt;span class=\\&quot;sensor\\&quot;&gt;&quot;;\n  html += String(bme.readTemperature());\n  html += &quot; *C&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&quot;;\n  html += &quot;&lt;tr&gt;&lt;td&gt;Temp. Fahrenheit&lt;\/td&gt;&lt;td&gt;&lt;span class=\\&quot;sensor\\&quot;&gt;&quot;;\n  html += String(1.8 * bme.readTemperature() + 32);\n  html += &quot; *F&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&quot;;\n  html += &quot;&lt;tr&gt;&lt;td&gt;Pressure&lt;\/td&gt;&lt;td&gt;&lt;span class=\\&quot;sensor\\&quot;&gt;&quot;;\n  html += String(bme.readPressure() \/ 100.0F);\n  html += &quot; hPa&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&quot;;\n  html += &quot;&lt;tr&gt;&lt;td&gt;Approx. Altitude&lt;\/td&gt;&lt;td&gt;&lt;span class=\\&quot;sensor\\&quot;&gt;&quot;;\n  html += String(bme.readAltitude(SEALEVELPRESSURE_HPA));\n  html += &quot; m&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&quot;;\n  html += &quot;&lt;tr&gt;&lt;td&gt;Humidity&lt;\/td&gt;&lt;td&gt;&lt;span class=\\&quot;sensor\\&quot;&gt;&quot;;\n  html += String(bme.readHumidity());\n  html += &quot; %&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt;&lt;\/body&gt;&lt;\/html&gt;&quot;;\n\n  \/\/ Send the response to the client\n  server.send(200, &quot;text\/html&quot;, html);\n}\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Initialize the BME280 sensor\n  if (!bme.begin(0x76)) {\n    Serial.println(&quot;Could not find a valid BME280 sensor, check wiring!&quot;);\n    while (1);\n  }\n\n  \/\/ Connect to Wi-Fi\n  Serial.print(&quot;Connecting to &quot;);\n  Serial.println(ssid);\n  WiFi.begin(ssid, password);\n  while (WiFi.status() != WL_CONNECTED) {\n    delay(500);\n    Serial.print(&quot;.&quot;);\n  }\n  \n  Serial.println(&quot;&quot;);\n  Serial.println(&quot;WiFi connected.&quot;);\n  Serial.println(&quot;IP address: &quot;);\n  Serial.println(WiFi.localIP());\n\n  \/\/ Set up the routes\n  server.on(&quot;\/&quot;, handleRoot);\n  \n  \/\/ Start the server\n  server.begin();\n}\n\nvoid loop() {\n  server.handleClient();\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Web_Server_Display_Sensor_Readings.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Modify the following lines to include your SSID and password.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<p>Then, check that you have the right board and COM port selected, and upload the code to your ESP32. After uploading, open the serial monitor at a baud rate of 115200, and copy the ESP32 IP address.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"278\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Getting-the-ESP32-IP-address-Serial-Monitor.png?resize=750%2C278&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Getting the ESP32 IP Address on Serial Monitor\" class=\"wp-image-169521\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Getting-the-ESP32-IP-address-Serial-Monitor.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/Getting-the-ESP32-IP-address-Serial-Monitor.png?resize=300%2C111&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Open your browser, paste the IP address, and you should see the latest sensor readings. To update the readings, you just need to refresh the web page.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Display BME280 Sensor Readings on Web Server\" class=\"wp-image-169522\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">How the code works<\/h3>\n\n\n\n<p>This sketch is very similar to the previous sketches in this tutorial. You must be familiar with most parts of the code. First, you include the <span class=\"rnthl rntliteral\">WiFi<\/span> and <span class=\"rnthl rntliteral\">WebServer<\/span> libraries to create the web server and the needed libraries to read from the BME280 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;WiFi.h&gt;\n#include &lt;Wire.h&gt;\n#include &lt;Adafruit_BME280.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &lt;WebServer.h&gt;<\/code><\/pre>\n\n\n\n<p>The following line defines a variable to save the pressure at the sea level. For more accurate altitude estimation, replace the value with the current sea level pressure at your location.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define SEALEVELPRESSURE_HPA (1013.25)<\/code><\/pre>\n\n\n\n<p>In the following line, you create an <span class=\"rnthl rntliteral\">Adafruit_BME280<\/span> object called <span class=\"rnthl rntliteral\">bme<\/span> that by default establishes a communication with the sensor using I2C.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Adafruit_BME280 bme; \/\/ I2C<\/code><\/pre>\n\n\n\n<p class=\"rntbox rntclgreen\">Learn more about I2C with the ESP32: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-i2c-communication-arduino-ide\/\">ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)<\/a>.<\/p>\n\n\n\n<p>As mentioned previously, you need to insert your ssid and password in the following lines inside the double quotes.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \" REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<p>Then, initialize a server on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WebServer server(80);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the&nbsp;<span class=\"rnthl rntliteral\">setup()<\/span>,&nbsp;we start a serial communication at a baud rate of 115200 for debugging purposes.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>You check that the BME280 sensor was successfully initialized.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (!bme.begin(0x76)) {\n&nbsp; Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");\n&nbsp; while (1);\n}<\/code><\/pre>\n\n\n\n<p>The following lines begin the Wi-Fi connection with <span class=\"rnthl rntliteral\">WiFi.begin(ssid, password)<\/span> , wait for a successful connection, and print the ESP IP address in the serial monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Connect to Wi-Fi\nSerial.print(\"Connecting to \");\nSerial.println(ssid);\nWiFi.begin(ssid, password);\nwhile (WiFi.status() != WL_CONNECTED) {\n&nbsp; delay(500);\n&nbsp; Serial.print(\".\");\n}\nSerial.println(\"\");\nSerial.println(\"WiFi connected.\");\nSerial.println(\"IP address: \");\nSerial.println(WiFi.localIP());<\/code><\/pre>\n\n\n\n<p>Finally, we set up the web server and define the routes it should handle. This web server will just handle the root URL to display the latest sensor readings. When you access the root URL, it will call the <span class=\"rnthl rntliteral\">handleRoot()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set up the routes\nserver.on(\"\/\", handleRoot);\n\/\/ Start the server\nserver.begin();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Displaying the Web Page<\/h4>\n\n\n\n<p>In this example, the <span class=\"rnthl rntliteral\">handleRoot()<\/span> function is responsible for generating the web page. It sends the HTML and CSS needed to build the page. The HTML and CSS text required to build the web page are saved on the <span class=\"rnthl rntliteral\">html<\/span> variable. We concatenate the current sensor readings with the <span class=\"rnthl rntliteral\">html<\/span> variable. This way, the HTML sent to the web browser will contain the latest sensor readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void handleRoot() {\n  String html = \"&lt;!DOCTYPE html&gt;&lt;html&gt;\";\n  html += \"&lt;head&gt;&lt;meta name=\\\"viewport\\\" \n           content=\\\"width=device-width, initial-scale=1\\\"&gt;\";\n  html += \"&lt;link rel=\\\"icon\\\" href=\\\"data:,\\\"&gt;\";\n  html += \"&lt;style&gt;body { text-align: center; \n           font-family: \\\"Trebuchet MS\\\", Arial;}\";\n  html += \"table { border-collapse: collapse; width:50%; \n           margin-left:auto; margin-right:auto; }\";\n  html += \"th { padding: 10px; background-color: #0043af; color: white; }\";\n  html += \"tr { border: 1px solid #ddd; padding: 12px; }\";\n  html += \"tr:hover { background-color: #bcbcbc; }\";\n  html += \"td { border: none; padding: 10px; }\";\n  html += \".sensor { color:white; font-weight: bold; \n           background-color: #bcbcbc; padding: 1px; }&lt;\/style&gt;&lt;\/head&gt;\";\n  html += \"&lt;body&gt;&lt;h1&gt;ESP32 with BME280&lt;\/h1&gt;\";\n  html += \"&lt;table&gt;&lt;tr&gt;&lt;th&gt;MEASUREMENT&lt;\/th&gt;&lt;th&gt;VALUE&lt;\/th&gt;&lt;\/tr&gt;\";\n  html += \"&lt;tr&gt;&lt;td&gt;Temp. Celsius&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\n  html += String(bme.readTemperature());\n  html += \" *C&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;\";\n  html += \"&lt;tr&gt;&lt;td&gt;Temp. Fahrenheit&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\n  html += String(1.8 * bme.readTemperature() + 32);\n  html += \" *F&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;\";\n  html += \"&lt;tr&gt;&lt;td&gt;Pressure&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\n  html += String(bme.readPressure() \/ 100.0F);\n  html += \" hPa&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;\";\n  html += \"&lt;tr&gt;&lt;td&gt;Approx. Altitude&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\n  html += String(bme.readAltitude(SEALEVELPRESSURE_HPA));\n  html += \" m&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;\";\n  html += \"&lt;tr&gt;&lt;td&gt;Humidity&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\n  html += String(bme.readHumidity());\n  html += \" %&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt;&lt;\/body&gt;&lt;\/html&gt;\";\n\n  \/\/ Send the response to the client\n  server.send(200, \"text\/html\", html);\n}<\/code><\/pre>\n\n\n\n<p>To display the sensor readings on the table, we just need to send them between the corresponding <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;td&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/td&gt;&nbsp;<\/span><\/span>tags. For example, to display the temperature:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>html += \"&lt;tr&gt;&lt;td&gt;Temp. Celsius&lt;\/td&gt;&lt;td&gt;&lt;span class=\\\"sensor\\\"&gt;\";\nhtml += String(bme.readTemperature());\nhtml += \" *C&lt;\/span&gt;&lt;\/td&gt;&lt;\/tr&gt;\";<\/code><\/pre>\n\n\n\n<p><strong>Note<\/strong>: the&nbsp;<span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;span&gt;<\/span><\/span>&nbsp;tag is useful to style a particular part of a text. In this case, we\u2019re using the&nbsp;<span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;span&gt;<\/span><\/span>&nbsp;tag to include the sensor reading in a class called \u201csensor\u201d. This is useful to style that particular part of the text using CSS.<\/p>\n\n\n\n<p>By default, the table displays the temperature readings in both Celsius degrees and Fahrenheit.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Display BME280 Sensor Readings on Web Server\" class=\"wp-image-169522\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Display-Sensor-Readings-on-Web-Server-BME280.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>To get the latest sensor readings, you just need to refresh the web page. To automatically update the readings, you need to learn other communication protocols like <a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\" title=\"\">server-sent events<\/a> or <a href=\"https:\/\/randomnerdtutorials.com\/esp32-websocket-server-sensor\/\" title=\"\">websockets<\/a>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-websocket-server-sensor\/\">ESP32 WebSocket Server: Display Sensor Readings<\/a><\/li>\n<\/ul>\n\n\n\n<p>To learn more in-depth about creating ESP32 web servers, we recommend taking a look at our eBook: <a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\" title=\"\">Build Web Servers with ESP32 and ESP8266 eBook (3rd Edition)<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you learned about building web servers with the ESP32. We covered basic concepts, and we took a look at basic examples to get you started. We hope you&#8217;ve found this guide useful and that you&#8217;re able to modify the examples for your projects.<\/p>\n\n\n\n<p>If you&#8217;re new to the ESP32 and web servers, we recommend the following eBooks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\" title=\"\">Learn ESP32 with Arduino IDE eBook<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\" title=\"\">Build Web Servers with ESP32 and ESP8266 eBook<\/a><\/li>\n<\/ul>\n\n\n\n<p>Here are other tutorials you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-async-web-server-espasyncwebserver-library\/\">ESP32 Async Web Server \u2013 Control Outputs with Arduino IDE (ESPAsyncWebServer library)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-websocket-sliders\/\">ESP32 Web Server (WebSocket) with Multiple Sliders: Control LEDs Brightness (PWM)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-websocket-server-sensor\/\">ESP32 WebSocket Server: Display Sensor Readings<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\" title=\"\"><strong>More ESP32 Tutorials and Projects&#8230;<\/strong><\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>New to ESP32 web servers? You\u2019re in the right place. In this detailed guide, we\u2019ll cover the basic concepts you need to know to build web servers with the ESP32, &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"Building an ESP32 Web Server: The Complete Guide for Beginners\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-beginners-guide\/#more-169410\" aria-label=\"Read more about Building an ESP32 Web Server: The Complete Guide for Beginners\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":169530,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,277,299,264],"tags":[],"class_list":["post-169410","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/ESP32-Web-Servers-Beginners-guide.jpg?fit=1920%2C1080&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/169410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=169410"}],"version-history":[{"count":21,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/169410\/revisions"}],"predecessor-version":[{"id":169906,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/169410\/revisions\/169906"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/169530"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=169410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=169410"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=169410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}