{"id":170074,"date":"2025-07-29T15:24:12","date_gmt":"2025-07-29T15:24:12","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=170074"},"modified":"2025-07-31T14:10:59","modified_gmt":"2025-07-31T14:10:59","slug":"esp32-web-server-littlefs","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-web-server-littlefs\/","title":{"rendered":"ESP32 Web Server using LittleFS Filesystem (serve files from filesystem)"},"content":{"rendered":"\n<p>In this tutorial, you\u2019ll learn how to build a web server with the ESP32 that serves HTML and CSS files stored in the LittleFS filesystem. Instead of embedding the HTML and CSS directly in the Arduino sketch, we\u2019ll store them as separate files, making the code cleaner and easier to manage.<\/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\/05\/ESP32-LittleFS-Web-Server.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server using LittleFS Filesystem (serve files from the filesystem)\" class=\"wp-image-170099\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.jpg?w=1920&amp;quality=100&amp;strip=all&amp;ssl=1 1920w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.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>The web server we\u2019ll build shows how to control the ESP32 outputs and how to display sensor readings. As an example, we\u2019ll control an LED and display sensor readings from a BME280 sensor.<\/p>\n\n\n\n<p>You can use the concepts learned in this tutorial to control any output or display <a href=\"https:\/\/randomnerdtutorials.com\/esp32-guides-sensors-modules\/\" title=\"\">sensor readings from other sensors<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Table of Contents<\/h2>\n\n\n\n<p>In this tutorial, we&#8217;ll cover the following topics:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#littlefs-intro\" title=\"\">Introducing LittleFS<\/a><\/li>\n\n\n\n<li><a href=\"#web-servers-intro\" title=\"\">Introducing ESP32 Web Servers<\/a><\/li>\n\n\n\n<li><a href=\"#project-overview\" title=\"\">Project Overview<\/a><\/li>\n\n\n\n<li><a href=\"#prerequisites\" title=\"\">Prerequisites<\/a><\/li>\n\n\n\n<li><a href=\"#organizing-your-files\" title=\"\">Organizing Your Files<\/a><\/li>\n\n\n\n<li><a href=\"#html-file\" title=\"\">Creating the HTML File<\/a><\/li>\n\n\n\n<li><a href=\"#css-file\" title=\"\">Creating the CSS File<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"littlefs-intro\">Introducing LittleFS<\/h2>\n\n\n\n<p>LittleFS is a lightweight filesystem created for microcontrollers. It lets you access the flash memory as you do in a standard file system on your computer, but it\u2019s simpler and more limited. You can <a href=\"https:\/\/randomnerdtutorials.com\/esp32-write-data-littlefs-arduino\/\" title=\"\">read, write, close, and delete files<\/a>. Using <a href=\"https:\/\/randomnerdtutorials.com\/esp32-littlefs-arduino-ide\/\" title=\"\">LittleFS with the ESP32 boards<\/a> is useful to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create configuration files with settings;<\/li>\n\n\n\n<li>Save data permanently, even after a restart or power loss;<\/li>\n\n\n\n<li>Create files to store small amounts of data instead of using a microSD card;<\/li>\n\n\n\n<li>Save HTML, CSS, and JavaScript files to build a web server interface (like we&#8217;ll do in this tutorial);<\/li>\n\n\n\n<li>Store images, figures, and icons for your web pages or display modules;<\/li>\n\n\n\n<li>Log sensor readings or application data locally;<\/li>\n\n\n\n<li>And much more, depending on your project needs.<\/li>\n<\/ul>\n\n\n\n<p>It\u2019s especially helpful in projects where you need to manage data directly on the ESP32 without relying on external storage devices.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"web-servers-intro\">Introducing ESP32 Web Servers<\/h2>\n\n\n\n<p>In simple terms, a web server is a \u201ccomputer\u201d that delivers web pages to users. It stores a website\u2019s files, including HTML documents and related assets like images, CSS style sheets, fonts, and other resources. <\/p>\n\n\n\n<p>When a user makes a request\u2014usually by entering a URL or clicking a link\u2014the web server responds by sending the requested files to the user&#8217;s web browser using the HTTP (Hypertext Transfer Protocol). HTTP is the foundation of data communication on the web, defining how messages are formatted and transmitted between clients (like your browser) and servers.<\/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 this example, the ESP32 acts as a web server. It stores the HTML and CSS files needed to build the web page on the LittleFS filesystem. When you access the ESP32 IP address in your browser, an HTTP request is sent to the ESP32. The ESP32 then responds with the HTML and CSS files, which the browser uses to render the page. These files can also include dynamic content, such as sensor readings and the current state of outputs, which are updated each time a new request is made.<\/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>If you&#8217;re new to ESP32 web servers, we recommend reading the following guide:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-beginners-guide\/\" title=\"\">Building an ESP32 Web Server: The Complete Guide for Beginners<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"project-overview\">Project Overview<\/h2>\n\n\n\n<p>Before going straight to the project, it\u2019s important to outline what our web server will do, so that it\u2019s easier to understand. The following picture shows the interface we&#8217;ll build with the files from the ESP32 filesystem.<\/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=\"766\" height=\"521\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?resize=766%2C521&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server with Files from LittleFS - Control Outputs and Monitor Sensors\" class=\"wp-image-170100\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?w=766&amp;quality=100&amp;strip=all&amp;ssl=1 766w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?resize=300%2C204&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 766px) 100vw, 766px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>The web server controls an LED connected to the ESP32 GPIO 2. This is the ESP32 on-board LED. You can control any other GPIO;<\/li>\n\n\n\n<li>The web server page shows two buttons: ON and OFF, to turn GPIO 2 on and off;<\/li>\n\n\n\n<li>The web server page also shows the current GPIO state;<\/li>\n\n\n\n<li>You\u2019ll also use a <a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\" title=\"\">BME280 sensor<\/a> to display sensor readings (temperature, humidity, and pressure).<\/li>\n<\/ul>\n\n\n\n<p>The following figure shows a simplified diagram to demonstrate how everything works.<\/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=\"576\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Project-Overview.jpg?resize=1024%2C576&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Project Overview\" class=\"wp-image-170101\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Project-Overview.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Project-Overview.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Project-Overview.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Project-Overview.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>Before proceeding with this project, make sure you check all the following prerequisites.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Install ESP32 Board in Arduino IDE 2<\/h3>\n\n\n\n<p>We\u2019ll program the ESP8266 using Arduino IDE, so you must have the ESP32 boards installed. Follow the next tutorial if you haven&#8217;t already:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-esp32-arduino-ide-2-0\/\" title=\"\">Installing ESP32 Board in Arduino IDE 2 (Windows, Mac OS X, Linux)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Install the ESP32 Filesystem Uploader Plugin<\/h3>\n\n\n\n<p>To upload files to the ESP32 LittleFS Filesystem, you need to install the Filesystem Uploader Plugin. Install it in your Arduino IDE 2:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\">Arduino IDE 2: Install ESP32 LittleFS Uploader (Upload Files to the Filesystem)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Installing Libraries<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Async Web Server Libraries<\/h4>\n\n\n\n<p>We&#8217;ll build the web server using the following libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer&nbsp;<\/a>by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> by ESP32Async<\/li>\n<\/ul>\n\n\n\n<p>You can install these libraries in the Arduino Library Manager. Open the Library Manager by clicking the Library icon on the left sidebar.<\/p>\n\n\n\n<p>Search for <span class=\"rnthl rntliteral\">ESPAsyncWebServer<\/span> and install the <strong>ESPAsyncWebServer by ESP32Async<\/strong>.<\/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=\"586\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-ESPAsyncWebServer-Library-ArduinoIDE-2-f.png?resize=666%2C586&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Installing ESPAsyncWebServer ESP32 Arduino IDE\" class=\"wp-image-167890\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-ESPAsyncWebServer-Library-ArduinoIDE-2-f.png?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-ESPAsyncWebServer-Library-ArduinoIDE-2-f.png?resize=300%2C264&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p>Then, install the AsyncTCP library. Search for <span class=\"rnthl rntliteral\">AsyncTCP<\/span> and install the <strong>AsyncTCP by ESP32Async<\/strong>.<\/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=\"722\" height=\"586\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-AsyncTCP-Library-ArduinoIDE.png?resize=722%2C586&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Installing AsyncTCP ESP32 Arduino IDE\" class=\"wp-image-167886\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-AsyncTCP-Library-ArduinoIDE.png?w=722&amp;quality=100&amp;strip=all&amp;ssl=1 722w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/Install-AsyncTCP-Library-ArduinoIDE.png?resize=300%2C243&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 722px) 100vw, 722px\" \/><\/figure><\/div>\n\n\n<h4 class=\"wp-block-heading\">Installing BME280 libraries<\/h4>\n\n\n\n<p>In this tutorial, we\u2019ll display readings from a BME280 sensor (<a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\" title=\"\">Guide with the ESP32<\/a>). You need to install the following libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_BME280_Library\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_BME280_Library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_Sensor<\/a><\/li>\n<\/ul>\n\n\n\n<p>You can install these libraries through the Arduino IDE Libraries Manager. Go to&nbsp;<strong>Sketch&nbsp;<\/strong>&gt;&nbsp;<strong>Include Libraries<\/strong>&nbsp;&gt;&nbsp;<strong>Manage Libraries<\/strong>. Then, search for the libraries\u2019 names to install them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parts Required<\/h2>\n\n\n\n<p>To proceed with this project, you need the following parts:<\/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=\"noopener\" title=\"\">ESP32&nbsp;Board<\/a> (read&nbsp;<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noopener\" title=\"\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/bme280-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME280 sensor module<\/a>&nbsp;(<a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\" title=\"\">Guide for BME280<\/a>)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/3mm-5mm-leds-kit-storage-box\/\" target=\"_blank\" rel=\"noreferrer noopener\">5 mm LED<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/resistors-kits\/\" target=\"_blank\" rel=\"noreferrer noopener\">330 Ohm resistor<\/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\n<h2 class=\"wp-block-heading\">Wiring the Circuit<\/h2>\n\n\n\n<p>Connect all the components by following this diagram:<\/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=\"954\" height=\"962\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LED-BME280-Circuit_bb.png?resize=954%2C962&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 with LED and BME280 Circuit Diagram\" class=\"wp-image-170102\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LED-BME280-Circuit_bb.png?w=954&amp;quality=100&amp;strip=all&amp;ssl=1 954w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LED-BME280-Circuit_bb.png?resize=298%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 298w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LED-BME280-Circuit_bb.png?resize=150%2C150&amp;quality=100&amp;strip=all&amp;ssl=1 150w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LED-BME280-Circuit_bb.png?resize=768%2C774&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 954px) 100vw, 954px\" \/><\/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<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>BME280<\/strong><\/td><td><strong>ESP32<\/strong><\/td><\/tr><tr><td>Vin<\/td><td>3V3<\/td><\/tr><tr><td>GND<\/td><td>GND<\/td><\/tr><tr><td>SCL<\/td><td>GPIO 22<\/td><\/tr><tr><td>SDA<\/td><td>GPIO 21<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"organizing-your-files\">Organizing Your Files<\/h2>\n\n\n\n<p>To build the web server you need three different files. The Arduino sketch, the HTML file, and the CSS file. The HTML and CSS files should be saved inside a folder called&nbsp;<span class=\"rnthl rntliteral\"><strong>data<\/strong><\/span>&nbsp;inside the Arduino sketch folder, as shown below.<\/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=\"708\" height=\"370\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?resize=708%2C370&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Folder Organization\" class=\"wp-image-170103\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?w=708&amp;quality=100&amp;strip=all&amp;ssl=1 708w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?resize=300%2C157&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 708px) 100vw, 708px\" \/><\/figure><\/div>\n\n\n<p><strong>You can download all project files:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/refs\/heads\/master\/Projects\/ESP32\/ESP32_Web_Server_LittleFS\/ESP32_Web_Server_LittleFS.zip\" target=\"_blank\" rel=\"noopener\" title=\"\">Click here to download all the project files<\/a><\/strong><\/li>\n<\/ul>\n\n\n\n<p>Then, you need to upload the filesystem image before uploading the code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"html-file\">Creating the HTML File<\/h2>\n\n\n\n<p>Create an&nbsp;<em>index.html<\/em>&nbsp;file with the following content:<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;!-- \n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-server-littlefs\/ \n--&gt;\n&lt;html&gt;\n&lt;head&gt;\n  &lt;meta charset=&quot;UTF-8&quot;&gt;\n  &lt;title&gt;ESP32 LittleFS Web Server&lt;\/title&gt;\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\n  &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;h1&gt;ESP32 LittleFS Web Server&lt;\/h1&gt;\n\n  &lt;p&gt;\ud83d\udca1 GPIO state&lt;strong&gt; %STATE%&lt;\/strong&gt;&lt;\/p&gt;\n\n  &lt;p&gt;\n    &lt;a href=&quot;\/on&quot;&gt;&lt;button class=&quot;button&quot;&gt;ON&lt;\/button&gt;&lt;\/a&gt;\n    &lt;a href=&quot;\/off&quot;&gt;&lt;button class=&quot;button button2&quot;&gt;OFF&lt;\/button&gt;&lt;\/a&gt;\n  &lt;\/p&gt;\n\n  &lt;p&gt;\n    &lt;span class=&quot;sensor-labels&quot;&gt;\ud83c\udf21\ufe0f Temperature&lt;\/span&gt;\n    &lt;span id=&quot;temperature&quot;&gt;%TEMPERATURE%&lt;\/span&gt;\n    &lt;sup class=&quot;units&quot;&gt;&amp;deg;C&lt;\/sup&gt;\n  &lt;\/p&gt;\n\n  &lt;p&gt;\n    &lt;span class=&quot;sensor-labels&quot;&gt;\ud83d\udca7 Humidity&lt;\/span&gt;\n    &lt;span id=&quot;humidity&quot;&gt;%HUMIDITY%&lt;\/span&gt;\n    &lt;sup class=&quot;units&quot;&gt;&amp;#37;&lt;\/sup&gt;\n  &lt;\/p&gt;\n\n  &lt;p&gt;\n    &lt;span class=&quot;sensor-labels&quot;&gt;\u2195\ufe0f Pressure&lt;\/span&gt;\n    &lt;span id=&quot;pressure&quot;&gt;%PRESSURE%&lt;\/span&gt;\n    &lt;sup class=&quot;units&quot;&gt;hPa&lt;\/sup&gt;\n  &lt;\/p&gt;\n&lt;\/body&gt;\n\n&lt;script&gt;\n  const updateSensorValue = (id, endpoint) =&gt; {\n    fetch(endpoint)\n      .then(response =&gt; {\n        if (!response.ok) {\n          throw new Error(`Failed to fetch ${endpoint}`);\n        }\n        return response.text();\n      })\n      .then(data =&gt; {\n        document.getElementById(id).textContent = data;\n      })\n      .catch(error =&gt; {\n        console.error(error);\n        document.getElementById(id).textContent = 'Error';\n      });\n  };\n\n  \/\/ Update all sensors every 10 seconds\n  setInterval(() =&gt; {\n    updateSensorValue('temperature', '\/temperature');\n    updateSensorValue('humidity', '\/humidity');\n    updateSensorValue('pressure', '\/pressure');\n  }, 10000);\n  \n&lt;\/script&gt;\n&lt;\/html&gt;\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_LittleFS\/data\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Head of the HTML File<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">&lt;head&gt;<\/span> section of the HTML file, we should add the metadata. The following line specifies the encoding for the document. UTF-8 is the most popular and supports all characters from all languages, including emojis.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;meta charset=\"UTF-8\"&gt;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">&lt;title&gt;<\/span> element defines the title of the webpage that appears in the browser tab.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;title&gt;ESP32 LittleFS Web Server&lt;\/title&gt;<\/code><\/pre>\n\n\n\n<p>We use the following line to make the web page responsive on all devices.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;<\/code><\/pre>\n\n\n\n<p>We won&#8217;t use a favicon for our web page. So, to prevent requests for the favicon, we can do as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;link rel=\"icon\" href=\"data:,\"&gt;<\/code><\/pre>\n\n\n\n<p>Because we\u2019re using CSS and HTML in different files, we need to reference the CSS file in the HTML text.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;link rel=\"stylesheet\" type=\"text\/css\" href=\"style.css\"&gt;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">&lt;link&gt;<\/span> tag tells the HTML file that you\u2019re using an external style sheet to format how the page looks. The <span class=\"rnthl rntliteral\">rel<\/span> attribute specifies the nature of the external file, in this case that it is a stylesheet\u2014the CSS file\u2014that will be used to alter the appearance of the page.<\/p>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">type<\/span> attribute is set to <span class=\"rnthl rntliteral\">\u201ctext\/css\u201d<\/span> to indicate that you\u2019re using a CSS file for the styles. The <span class=\"rnthl rntliteral\">href<\/span> attribute indicates the file location; since both the CSS and HTML files will be in the same folder, you just need to reference the filename: <em>style.css<\/em>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Body of the HTML File<\/h4>\n\n\n\n<p>In the following line, we write the first heading of our web page. In this case, we have \u201cESP32 LittleFS Web Server\u201d. You can change the heading to any text:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;h1&gt;ESP32 LittleFS Web Server&lt;\/h1&gt;<\/code><\/pre>\n\n\n\n<p>Then, add a paragraph with the text \u201cGPIO state \u201d followed by the GPIO state. Because the GPIO state changes accordingly to the state of the GPIO, we can add a placeholder that will then be replaced with whatever value we set on the Arduino sketch.<\/p>\n\n\n\n<p>To add a placeholder use <strong>%<\/strong> signs. To create a placeholder for the state, you can use <span class=\"rnthl rntliteral\">%STATE%<\/span>, for example.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;\ud83d\udca1 GPIO state&lt;strong&gt; %STATE%&lt;\/strong&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>You attribute a value to the <span class=\"rnthl rntliteral\">STATE<\/span> placeholder in the Arduino sketch\u2014we&#8217;ll see how that works later.<\/p>\n\n\n\n<p>Then, create an ON and an OFF buttons. When you click the <strong>on <\/strong>button, we redirect the web page to to root URL followed by <span class=\"rnthl rntliteral\">\/on<\/span> url. When you click the off button, you are redirected to the <span class=\"rnthl rntliteral\">\/off<\/span> url.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;a href=\"\/on\"&gt;&lt;button class=\"button\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;\n&lt;a href=\"\/off\"&gt;&lt;button class=\"button button2\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;<\/code><\/pre>\n\n\n\n<p>Finally, create three paragraphs to display the temperature, humidity, and pressure.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;\n  &lt;a href=\"\/on\"&gt;&lt;button class=\"button\"&gt;ON&lt;\/button&gt;&lt;\/a&gt;\n  &lt;a href=\"\/off\"&gt;&lt;button class=\"button button2\"&gt;OFF&lt;\/button&gt;&lt;\/a&gt;\n&lt;\/p&gt;\n\n&lt;p&gt;\n  &lt;span class=\"sensor-labels\"&gt;\ud83c\udf21\ufe0f Temperature&lt;\/span&gt;\n  &lt;span id=\"temperature\"&gt;%TEMPERATURE%&lt;\/span&gt;\n  &lt;sup class=\"units\"&gt;&amp;deg;C&lt;\/sup&gt;\n&lt;\/p&gt;\n\n&lt;p&gt;\n  &lt;span class=\"sensor-labels\"&gt;\ud83d\udca7 Humidity&lt;\/span&gt;\n  &lt;span id=\"humidity\"&gt;%HUMIDITY%&lt;\/span&gt;\n  &lt;sup class=\"units\"&gt;&amp;#37;&lt;\/sup&gt;\n&lt;\/p&gt;\n\n&lt;p&gt;\n  &lt;span class=\"sensor-labels\"&gt;\u2195\ufe0f Pressure&lt;\/span&gt;\n  &lt;span id=\"pressure\"&gt;%PRESSURE%&lt;\/span&gt;\n  &lt;sup class=\"units\"&gt;hPa&lt;\/sup&gt;\n&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>We use the <span class=\"rnthl rntliteral\">%TEMPERATURE%<\/span>, <span class=\"rnthl rntliteral\">%HUMIDITY%<\/span> and <span class=\"rnthl rntliteral\">%PRESSURE%<\/span> placeholders. These will then be replaced by the actual sensor readings in the Arduino sketch.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Automatic Updates<\/h4>\n\n\n\n<p>We also add a bit of JavaScript to our HTML file that is responsible for updating the readings every ten seconds without the need to refresh the web page. It should go between <span class=\"rnthl rntliteral\">&lt;script&gt;<\/span><span class=\"rnthl rntliteral\">&lt;\/script&gt;<\/span> tags.<\/p>\n\n\n\n<p>Basically, we fetch sensor data from the server (ESP32) and update the content of the corresponding HTML elements on the page.<\/p>\n\n\n\n<p>We create a function called <span class=\"rnthl rntliteral\">updateSensorValue<\/span> that takes two parameters: <span class=\"rnthl rntliteral\">id<\/span> (the id of the HTML element on the page), and the <span class=\"rnthl rntliteral\">endpoint<\/span> (URL from which to fetch sensor data\u2014for example, to get temperature values, we make a request on the <span class=\"rnthl rntliteral\">\/temperature<\/span> URL).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>const updateSensorValue = (id, endpoint) =&gt; {\n  fetch(endpoint)\n    .then(response =&gt; {\n      if (!response.ok) {\n        throw new Error(`Failed to fetch ${endpoint}`);\n      }\n      return response.text();\n    })\n    .then(data =&gt; {\n      document.getElementById(id).textContent = data;\n    })\n    .catch(error =&gt; {\n      console.error(error);\n      document.getElementById(id).textContent = 'Error';\n    });\n};<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">fetch(endpoint)<\/span> command function sends a request to the server (the ESP32).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code> fetch(endpoint)<\/code><\/pre>\n\n\n\n<p>If the server responds successfully, the function retrieves the response data (in text format) and updates the text content of the HTML element with the given id.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>  }\n  return response.text();\n})\n  .then(data =&gt; {\n     document.getElementById(id).textContent = data;\n   })<\/code><\/pre>\n\n\n\n<p>If something goes wrong (like the server is down or the request fails), it shows <span class=\"rnthl rntliteral\">Error<\/span> in the HTML element and logs the error to the console.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>.catch(error =&gt; {\n  console.error(error);\n  document.getElementById(id).textContent = 'Error';\n});<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Updating Sensor Readings<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">setInterval()<\/span> function is used to call the <span class=\"rnthl rntliteral\">updateSensorValue()<\/span> function every 10 seconds. It fetches the data for temperature, humidity, and pressure and places the readings on the right HTML elements.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code> \/\/ Update all sensors every 10 seconds\n setInterval(() =&gt; {\n   updateSensorValue('temperature', '\/temperature');\n   updateSensorValue('humidity', '\/humidity');\n   updateSensorValue('pressure', '\/pressure');\n }, 10000);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"css-file\">Creating the CSS File<\/h2>\n\n\n\n<p>Create the<em>&nbsp;style.css <\/em>file&nbsp;with the following content:<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">\/***\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-server-littlefs\/ \n***\/\n\nhtml {\n  font-family: Arial;\n  display: inline-block;\n  margin: 0 auto;\n  text-align: center;\n  background-color: #f4f4f4;\n  color: #333;\n}\n\nh1 {\n  color: #0F3376;\n  padding: 2vh 1rem;\n  font-size: 2rem;\n  margin-bottom: 1rem;\n}\n\np {\n  font-size: 1.5rem;\n  margin: 1rem auto;\n  max-width: 600px;\n}\n\n.button {\n  display: inline-block;\n  background-color: #00A676;\n  border: none;\n  border-radius: 6px;\n  color: white;\n  padding: 14px 32px;\n  text-decoration: none;\n  font-size: 1.2rem;\n  margin: 6px 10px;\n  cursor: pointer;\n  transition: background-color 0.3s ease, transform 0.1s ease;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);\n  width: 160px;\n}\n\n.button:hover {\n  background-color: #0077a8;\n  transform: translateY(-1px);\n}\n\n.button2 {\n  background-color: #f44336;\n}\n\n.button2:hover {\n  background-color: #d9362c;\n}\n\n.units {\n  font-size: 1.2rem;\n  color: #555;\n  margin-left: 4px;\n}\n\n.sensor-labels {\n  font-size: 1.5rem;\n  font-weight: bold;\n  vertical-align: middle;\n  padding-bottom: 15px;\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_LittleFS\/data\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>This is just a basic CSS file to set the font size, style, and color of the buttons and align the page. We won\u2019t explain how CSS works. A good place to learn about CSS is the&nbsp;<a href=\"https:\/\/www.w3schools.com\/css\/default.asp\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">W3Schools website<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ESP32 Web Server with Files from LittleFS (Arduino Sketch)<\/h2>\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=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Control-Outputs-Monitor-Sensors-demonstration.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Control Outputs and Monitor Sensors\" class=\"wp-image-170115\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Control-Outputs-Monitor-Sensors-demonstration.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Control-Outputs-Monitor-Sensors-demonstration.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Copy the following code to the Arduino IDE or&nbsp;<strong><a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/refs\/heads\/master\/Projects\/ESP32\/ESP32_Web_Server_LittleFS\/ESP32_Web_Server_LittleFS.zip\" target=\"_blank\" rel=\"noopener\" title=\"\">download all project files here<\/a><\/strong>. Then, you need to type your network credentials (SSID and password) to connect the ESP32 to your local network.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-web-server-littlefs\/ \r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*\/\r\n\r\n\/\/ Import required libraries\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;ESPAsyncWebServer.h&gt;\r\n#include &lt;AsyncTCP.h&gt;\r\n#include &lt;FS.h&gt;\r\n#include &lt;LittleFS.h&gt;\r\n#include &lt;Wire.h&gt;\r\n#include &lt;Adafruit_Sensor.h&gt;\r\n#include &lt;Adafruit_BME280.h&gt;\r\n\r\nAdafruit_BME280 bme; \/\/ I2C\r\n\/\/Adafruit_BME280 bme(BME_CS); \/\/ hardware SPI\r\n\/\/Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); \/\/ software SPI\r\n\r\n\/\/ Replace with your network credentials\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\n\/\/ Set LED GPIO\r\nconst int ledPin = 2;\r\n\/\/ Stores LED state\r\nString ledState;\r\n\r\n\/\/ Create AsyncWebServer object on port 80\r\nAsyncWebServer server(80);\r\n\r\nString getTemperature() {\r\n  float temperature = bme.readTemperature();\r\n  \/\/ Read temperature as Fahrenheit (isFahrenheit = true)\r\n  \/\/float t = dht.readTemperature(true);\r\n  Serial.println(temperature);\r\n  return String(temperature);\r\n}\r\n  \r\nString getHumidity() {\r\n  float humidity = bme.readHumidity();\r\n  Serial.println(humidity);\r\n  return String(humidity);\r\n}\r\n\r\nString getPressure() {\r\n  float pressure = bme.readPressure()\/ 100.0F;\r\n  Serial.println(pressure);\r\n  return String(pressure);\r\n}\r\n\r\n\/\/ Replaces placeholder with LED state value\r\nString processor(const String&amp; var){\r\n  Serial.println(var);\r\n  if(var == &quot;STATE&quot;){\r\n    if(digitalRead(ledPin)){\r\n      ledState = &quot;ON&quot;;\r\n    }\r\n    else{\r\n      ledState = &quot;OFF&quot;;\r\n    }\r\n    Serial.println(ledState);\r\n    return ledState;\r\n  }\r\n  else if (var == &quot;TEMPERATURE&quot;){\r\n    return getTemperature();\r\n  }\r\n  else if (var == &quot;HUMIDITY&quot;){\r\n    return getHumidity();\r\n  }\r\n  else if (var == &quot;PRESSURE&quot;){\r\n    return getPressure();\r\n  }\r\n  return String();\r\n}\r\n \r\nvoid setup(){\r\n  \/\/ Serial port for debugging purposes\r\n  Serial.begin(115200);\r\n  pinMode(ledPin, OUTPUT);\r\n\r\n  \/\/ Initialize the sensor\r\n  if (!bme.begin(0x76)) {\r\n    Serial.println(&quot;Could not find a valid BME280 sensor, check wiring!&quot;);\r\n    while (1);\r\n  }\r\n\r\n  \/\/ Initialize LittleFS\r\n  if(!LittleFS.begin()){\r\n    Serial.println(&quot;An Error has occurred while mounting LittleFS&quot;);\r\n    return;\r\n  }\r\n\r\n  \/\/ Connect to Wi-Fi\r\n  WiFi.begin(ssid, password);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(1000);\r\n    Serial.println(&quot;Connecting to WiFi..&quot;);\r\n  }\r\n\r\n  \/\/ Print ESP32 Local IP Address\r\n  Serial.println(WiFi.localIP());\r\n\r\n  \/\/ Route for root \/ web page\r\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(LittleFS, &quot;\/index.html&quot;, String(), false, processor);\r\n  });\r\n  \r\n  \/\/ Route to load style.css file\r\n  server.on(&quot;\/style.css&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(LittleFS, &quot;\/style.css&quot;, &quot;text\/css&quot;);\r\n  });\r\n\r\n  \/\/ Route to set GPIO to HIGH\r\n  server.on(&quot;\/on&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    digitalWrite(ledPin, HIGH);    \r\n    request-&gt;send(LittleFS, &quot;\/index.html&quot;, String(), false, processor);\r\n  });\r\n  \r\n  \/\/ Route to set GPIO to LOW\r\n  server.on(&quot;\/off&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    digitalWrite(ledPin, LOW);    \r\n    request-&gt;send(LittleFS, &quot;\/index.html&quot;, String(), false, processor);\r\n  });\r\n\r\n  server.on(&quot;\/temperature&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(200, &quot;text\/plain&quot;, getTemperature().c_str());\r\n  });\r\n  \r\n  server.on(&quot;\/humidity&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(200, &quot;text\/plain&quot;, getHumidity().c_str());\r\n  });\r\n  \r\n  server.on(&quot;\/pressure&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(200, &quot;text\/plain&quot;, getPressure().c_str());\r\n  });\r\n\r\n  \/\/ Start server\r\n  server.begin();\r\n}\r\n \r\nvoid loop(){\r\n  \r\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_LittleFS\/ESP32_Web_Server_LittleFS.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the code works<\/h3>\n\n\n\n<p>Continue reading to learn how the code works, or skip to the next section.<\/p>\n\n\n\n<p>First, include the necessary libraries:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;WiFi.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;FS.h&gt;\n#include &lt;LittleFS.h&gt;\n#include &lt;Wire.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &lt;Adafruit_BME280.h&gt;<\/code><\/pre>\n\n\n\n<p>You need to type your network credentials in the following variables:<\/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>Create an instance that refers to the BME280 sensor called <span class=\"rnthl rntliteral\">bme<\/span>:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Adafruit_BME280 bme; \/\/ I2C<\/code><\/pre>\n\n\n\n<p>Next, create a variable that refers to GPIO 2 called <span class=\"rnthl rntliteral\">ledPin<\/span>, and a String variable to hold the led state: <span class=\"rnthl rntliteral\">ledState<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const int ledPin = 2;\nString ledState;<\/code><\/pre>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">AsynWebServer<\/span> object called <span class=\"rnthl rntliteral\">server<\/span> that is listening on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncWebServer server(80);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Get Sensor Readings<\/h4>\n\n\n\n<p>We create three functions to return the sensor readings as strings: the <span class=\"rnthl rntliteral\">getTemperature()<\/span>, <span class=\"rnthl rntliteral\">getHumidity()<\/span> and <span class=\"rnthl rntliteral\">getPressure()<\/span> functions.<\/p>\n\n\n\n<p>Here&#8217;s how the <span class=\"rnthl rntliteral\">getTemperature()<\/span> function looks like (the other functions are similar).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String getTemperature() {\n  float temperature = bme.readTemperature();\n  \/\/ Read temperature as Fahrenheit (isFahrenheit = true)\n  \/\/float temperature = 1.8 * bme.readTemperature() + 32;\n  Serial.println(temperature);\n  return String(temperature);\n}<\/code><\/pre>\n\n\n\n<p>If you want to display temperature in Fahrenheit degrees, you just need to uncomment the corresponding line in the <span class=\"rnthl rntliteral\">getTemperature()<\/span> function:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>float temperature = 1.8 * bme.readTemperature() + 32;<\/code><\/pre>\n\n\n\n<p>To learn more about interfacing the BME280 sensor with the ESP32, you can read the following tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\" title=\"\">ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)<\/a><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">processor()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">processor()<\/span> function attributes a value to the placeholders we\u2019ve created on the HTML file. It accepts as argument the placeholder and should return a String that will replace the placeholder. The <span class=\"rnthl rntliteral\">processor()<\/span> function should have the following structure:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String processor(const String&amp; var){\n  Serial.println(var);\n  if(var == \"STATE\"){\n    if(digitalRead(ledPin)){\n      ledState = \"ON\";\n    }\n    else{\n      ledState = \"OFF\";\n    }\n    Serial.print(ledState);\n    return ledState;\n  }\n  else if (var == \"TEMPERATURE\"){\n    return getTemperature();\n  }\n  else if (var == \"HUMIDITY\"){\n    return getHumidity();\n  }\n  else if (var == \"PRESSURE\"){\n    return getPressure();\n  }\n}<\/code><\/pre>\n\n\n\n<p>This function first checks if the placeholder is the <span class=\"rnthl rntliteral\">STATE<\/span> we\u2019ve created on the HTML file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(var == \"STATE\"){<\/code><\/pre>\n\n\n\n<p>If it is, then, according to the LED state, we set the <span class=\"rnthl rntliteral\">ledState<\/span> variable to either ON or OFF.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(digitalRead(ledPin)){\n  ledState = \"ON\";\n}\nelse{\n  ledState = \"OFF\";\n}<\/code><\/pre>\n\n\n\n<p>Finally, we return the <span class=\"rnthl rntliteral\">ledState<\/span> variable. This replaces the <span class=\"rnthl rntliteral\">STATE<\/span> placeholder with the <span class=\"rnthl rntliteral\">ledState<\/span> string value.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>return ledState;<\/code><\/pre>\n\n\n\n<p>If it finds the <span class=\"rnthl rntliteral\"><strong>%TEMPERATURE%<\/strong><\/span> placeholder, we return the temperature by calling the <span class=\"rnthl rntliteral\">getTemperature()<\/span> function created previously. <\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>else if (var == \"TEMPERATURE\"){\n  return getTemperature();\n}<\/code><\/pre>\n\n\n\n<p>The same happens for the <span class=\"rnthl rntliteral\">%HUMIDITY%<\/span> and <span class=\"rnthl rntliteral\">%PRESSURE%<\/span> placeholders by calling the corresponding functions:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>else if (var == \"TEMPERATURE\"){\n  return getTemperature();\n}\nelse if (var == \"HUMIDITY\"){\n  return getHumidity();\n}\nelse if (var == \"PRESSURE\"){\n  return getPressure();\n}  <\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, start by initializing the Serial Monitor and setting the GPIO as an output.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);\npinMode(ledPin, OUTPUT);<\/code><\/pre>\n\n\n\n<p>Initialize the BME280 sensor:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (!bme.begin(0x76)) {\n  Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");\n  while (1);\n}<\/code><\/pre>\n\n\n\n<p>Initialize LittleFS:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize LittleFS\nif(!LittleFS.begin()){\n  Serial.println(\"An Error has occurred while mounting LittleFS\");\n  return;\n}<\/code><\/pre>\n\n\n\n<p><strong>Wi-Fi connection<\/strong><\/p>\n\n\n\n<p>Connect to Wi-Fi and print the ESP32 IP address:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.begin(ssid, password);\nwhile (WiFi.status() != WL_CONNECTED) {\n  delay(1000);\n  Serial.println(\"Connecting to WiFi..\");\n}\nSerial.println(WiFi.localIP());<\/code><\/pre>\n\n\n\n<p><strong>Async Web Server<\/strong><\/p>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">ESPAsyncWebServer<\/span> library allows us to configure the routes where the server will be listening for incoming HTTP requests and execute functions when a request is received on that route. For that, use the <span class=\"rnthl rntliteral\">on<\/span> method on the <span class=\"rnthl rntliteral\">server<\/span> object as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Route for root \/ web page\nserver.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(LittleFS, \"\/index.html\", String(), false, processor);\n});<\/code><\/pre>\n\n\n\n<p>When the server receives a request on the root <span class=\"rnthl rntliteral\">\/<\/span> URL, it will send the&nbsp;<em>index.html<\/em> file to the client. The first argument is where it should look for the files\u2014in this case, the files are saved in <span class=\"rnthl rntliteral\">LittleFS<\/span>.<\/p>\n\n\n\n<p>The second argument is the file path in the LittleFS to be served when the root <span class=\"rnthl rntliteral\">\/<\/span> is requested.<\/p>\n\n\n\n<p><span class=\"rnthl rntliteral\">String()<\/span> is the MIME type. Passing an empty string means it will automatically detect the content type.<\/p>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">false<\/span> argument disables caching, and finally, the last argument of the&nbsp;<span class=\"rnthl rntliteral\">send()<\/span>&nbsp;function is the <span class=\"rnthl rntliteral\">processor<\/span>. This is a callback function lets you dynamically insert values into the HTML using template placeholders. We use this so that we can replace the placeholder with the value we want \u2013 in this case the <span class=\"rnthl rntliteral\">ledState<\/span>, and also send updated values when we refresh the web page.<\/p>\n\n\n\n<p>Because we\u2019ve referenced the CSS file in the HTML file, the client will make a request for the CSS file. When that happens, the CSS file is sent to the client:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/style.css\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(LittleFS, \"\/style.css\",\"text\/css\");\n});<\/code><\/pre>\n\n\n\n<p>You also need to define what happens on the&nbsp;<span class=\"rnthl rntliteral\">\/on<\/span>&nbsp;and&nbsp;<span class=\"rnthl rntliteral\">\/off<\/span>&nbsp;routes. When a request is made on those routes, the LED is either turned on or off, and the ESP32 serves the HTML file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/on\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  digitalWrite(ledPin, HIGH);\n  request-&gt;send(LittleFS, \"\/index.html\", String(),false, processor);\n});\nserver.on(\"\/off\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  digitalWrite(ledPin, LOW);\n  request-&gt;send(LittleFS, \"\/index.html\", String(),false, processor);\n});<\/code><\/pre>\n\n\n\n<p>In the HTML file, we&#8217;ve written a JavaScript code that requests the temperature, humidity and pressure on the <span class=\"rnthl rntliteral\">\/temperature<\/span>, <span class=\"rnthl rntliteral\">\/humidity<\/span>, <span class=\"rnthl rntliteral\">\/pressure<\/span> routes, respectively, every 10 seconds. So, we also need to handle what happens when we receive a request on those routes.<\/p>\n\n\n\n<p>We simply need to send the updated sensor readings. The updated sensor readings are returned by the <span class=\"rnthl rntliteral\">getTemperature()<\/span>, <span class=\"rnthl rntliteral\">getHumidity(),<\/span> and <span class=\"rnthl rntliteral\">getPressure()<\/span> functions we&#8217;ve created previously.<\/p>\n\n\n\n<p>The readings are plain text, and should be sent as a char, so we use the <span class=\"rnthl rntliteral\">c_str()<\/span> method.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/temperature\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(200, \"text\/plain\", getTemperature().c_str());\n});\n  \nserver.on(\"\/humidity\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(200, \"text\/plain\", getHumidity().c_str());\n});\n  \nserver.on(\"\/pressure\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(200, \"text\/plain\", getPressure().c_str());\n});<\/code><\/pre>\n\n\n\n<p>In the end, we use the <span class=\"rnthl rntliteral\">begin()<\/span> method on the <span class=\"rnthl rntliteral\">server<\/span> object, so that the server starts listening for incoming clients.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\n\n\n\n<p>Because this is an asynchronous web server, you can define all the requests in the <span class=\"rnthl rntliteral\">setup()<\/span>. Then, you can add other code to the <span class=\"rnthl rntliteral\">loop()<\/span> while the server is listening for incoming clients.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"upload-code-and-files\">Upload the Code and Files (Filesystem Image)<\/h2>\n\n\n\n<p>Now, you need to upload the files and code to your board. Follow the next instructions<\/p>\n\n\n\n<p><strong>1)<\/strong> Save your Arduino sketch.<\/p>\n\n\n\n<p><strong>2) <\/strong>Go to <strong>Sketch<\/strong> &gt; <strong>Show Sketch folder<\/strong>. The folder where your sketch is saved should open.<\/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=\"355\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Show-Sketch-Folder-f.jpg?resize=750%2C355&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Show Sketch Folder\" class=\"wp-image-170108\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Show-Sketch-Folder-f.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Show-Sketch-Folder-f.jpg?resize=300%2C142&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>3)<\/strong> Inside that folder, create a folder called <strong>data<\/strong>.<\/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=\"225\" height=\"139\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-Web-Server-Create-Data-Folder.jpg?resize=225%2C139&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Create Data Folder\" class=\"wp-image-170109\"\/><\/figure><\/div>\n\n\n<p><strong>4)<\/strong> Save the HTML and CSS files inside that <strong><em>data<\/em><\/strong> folder.<\/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=\"708\" height=\"370\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?resize=708%2C370&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Folder Organization\" class=\"wp-image-170103\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?w=708&amp;quality=100&amp;strip=all&amp;ssl=1 708w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/esp32-little-fs-web-server-folder-organization.png?resize=300%2C157&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 708px) 100vw, 708px\" \/><\/figure><\/div>\n\n\n<p><strong>5)<\/strong>&nbsp;Make sure you have the right board (<strong>Tools&nbsp;<\/strong>&gt;&nbsp;<strong>Board<\/strong>) and COM port selected (<strong>Tools&nbsp;<\/strong>&gt;&nbsp;<strong>Port<\/strong>).<\/p>\n\n\n\n<p><strong>6)<\/strong>&nbsp;Depending on the ESP32 board selected, you may need to select the desired flash size (some boards don\u2019t have that option, don\u2019t worry). In the Arduino IDE, in&nbsp;<strong>Tools<\/strong>&nbsp;&gt;&nbsp;<strong>Flash size<\/strong>, select the desired flash size (for example, 4MB SPIFFS).<\/p>\n\n\n\n<p><strong>7)<\/strong> Then, upload the files to the ESP32 board. Press [<strong>Ctrl<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on Windows or [<strong>\u2318<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on MacOS to open the command palette. Search for the <strong>Upload LittleFS to Pico\/ESP8266\/ESP32<\/strong> command and click on 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=\"732\" height=\"586\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/05\/Upload-littlefs-esp32-esp8266-pico-1.png?resize=732%2C586&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Upload LittleFS to Pico\/ESP8266\/ESP32\" class=\"wp-image-154913\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/05\/Upload-littlefs-esp32-esp8266-pico-1.png?w=732&amp;quality=100&amp;strip=all&amp;ssl=1 732w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/05\/Upload-littlefs-esp32-esp8266-pico-1.png?resize=300%2C240&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 732px) 100vw, 732px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntcred\"><strong>Important: <\/strong> ensure the Serial Monitor is closed. Otherwise, the upload will fail.<\/p>\n\n\n\n<p>After a few seconds, you should get the message &#8220;<strong>Completed upload.<\/strong> &#8220;. The files were successfully uploaded to the ESP32 filesystem.<\/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=\"203\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/littlefs-upload-completed-esp32-arduino-ide-2.png?resize=666%2C203&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"LittleFS upload success\" class=\"wp-image-170110\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/littlefs-upload-completed-esp32-arduino-ide-2.png?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/littlefs-upload-completed-esp32-arduino-ide-2.png?resize=300%2C91&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p><strong>8)<\/strong> Now, upload the code to the ESP32 by clicking on the upload 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=\"36\" height=\"39\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/05\/arduino-ide-2-upload-button.png?resize=36%2C39&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Arduino IDE 2 Upload Button\" class=\"wp-image-146269\"\/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>After uploading the code, open the Serial Monitor at a baud rate of 115200.<\/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=\"41\" height=\"39\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/02\/serial-monitor-logo-arduino-ide-2.png?resize=41%2C39&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Open Arduino IDE 2 Serial Monitor baud rate of 115200\" class=\"wp-image-148549\"\/><\/figure><\/div>\n\n\n<p>Press the ESP32 on-board &#8220;RST&#8221; button. It should print 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=\"666\" height=\"280\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-Get-Ip-Address-Serial-Monitor-LittleFS-Web-Server.jpg?resize=666%2C280&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server - Get IP Address on Serial Monitor\" class=\"wp-image-170111\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-Get-Ip-Address-Serial-Monitor-LittleFS-Web-Server.jpg?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-Get-Ip-Address-Serial-Monitor-LittleFS-Web-Server.jpg?resize=300%2C126&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p>Open a web browser on your local network and type the ESP32 IP address. You should get access to the web page. You can control the onboard LED by clicking on the ON and OFF buttons, and you can check the latest sensor readings. The readings are updated every 10 seconds.<\/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=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Demonstration.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 LittleFS Web Server Demonstration\" class=\"wp-image-170114\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Demonstration.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server-Demonstration.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>You can access the web server on your computer or smartphone as long as they are on the same network as the ESP32.<\/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=\"766\" height=\"521\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?resize=766%2C521&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server with Files from LittleFS - Control Outputs and Monitor Sensors\" class=\"wp-image-170100\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?w=766&amp;quality=100&amp;strip=all&amp;ssl=1 766w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/05\/ESP32-LittleFS-Web-Server.png?resize=300%2C204&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 766px) 100vw, 766px\" \/><\/figure><\/div>\n\n\n<p>Congratulations! You&#8217;ve successfully built a web server with files saved on LittleFS filesystem.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Troubleshooting &#8211; This page can&#8217;t be found<\/h3>\n\n\n\n<p>If you get this error, it probably means you didn&#8217;t upload the files to the ESP32 LittleFS filesystem or they are in the wrong location.<\/p>\n\n\n\n<p><a href=\"#upload-code-and-files\" title=\"\">Go back to this section<\/a> and make sure you follow all the steps.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you learned how to build an ESP32 web server that serves the HTML and CSS from the LittleFS filesystem. Saving those files on the filesystem is more practical than embedding them in the Arduino sketch.<\/p>\n\n\n\n<p>What you learned here can be applied to other web servers that use the ESPAsyncWebServer library. Don&#8217;t forget you need to create a folder called <em>data <\/em>inside your sketch folder. Then, inside that folder, you should save the files you want to upload to the filesystem. Then, don&#8217;t forget to upload the filesystem image using the LittleFS uploader plugin.<\/p>\n\n\n\n<p>If you\u2019re 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\/\">Learn ESP32 with Arduino IDE eBook<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\">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\/\"><strong>More ESP32 Tutorials and Projects\u2026<\/strong><\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, you\u2019ll learn how to build a web server with the ESP32 that serves HTML and CSS files stored in the LittleFS filesystem. Instead of embedding the HTML &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Web Server using LittleFS Filesystem (serve files from filesystem)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-littlefs\/#more-170074\" aria-label=\"Read more about ESP32 Web Server using LittleFS Filesystem (serve files from filesystem)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":170099,"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":[276,281,277,299,264],"tags":[],"class_list":["post-170074","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","category-esp32-project","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\/05\/ESP32-LittleFS-Web-Server.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\/170074","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=170074"}],"version-history":[{"count":13,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/170074\/revisions"}],"predecessor-version":[{"id":174775,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/170074\/revisions\/174775"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/170099"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=170074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=170074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=170074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}