{"id":151210,"date":"2024-05-16T13:31:38","date_gmt":"2024-05-16T13:31:38","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=151210"},"modified":"2025-03-14T12:15:41","modified_gmt":"2025-03-14T12:15:41","slug":"esp32-datalogger-download-data-file","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-datalogger-download-data-file\/","title":{"rendered":"ESP32 Datalogger: Download Data File via Web Server (Arduino IDE)"},"content":{"rendered":"\n<p>In this guide, we&#8217;ll build an ESP32 datalogger that also hosts a web server so that you can access and download the data remotely. The ESP32 will log data to a file hosted on a microSD card. You can access the web server with your computer or smartphone and download the file with the data remotely without the need to remove the microSD card from the ESP32.<\/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\/2024\/03\/ESP32-Datalogger-Download-File-Web-Server.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Download Data File via Web Server Arduino IDE\" class=\"wp-image-151242\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Download-File-Web-Server.jpg?w=1920&amp;quality=100&amp;strip=all&amp;ssl=1 1920w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Download-File-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\/2024\/03\/ESP32-Datalogger-Download-File-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\/2024\/03\/ESP32-Datalogger-Download-File-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\/2024\/03\/ESP32-Datalogger-Download-File-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><strong>Table of Contents:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#project-overview\" title=\"\">Project Overview<\/a><\/li>\n\n\n\n<li><a href=\"#prerequisites\" title=\"\">Prerequisites<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#arduino-idearduino-ide\" title=\"\">Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"#libraries\" title=\"\">Installing Libraries<\/a><\/li>\n\n\n\n<li><a href=\"#format-microsd-card\" title=\"\">Formatting the MicroSD Card<\/a><\/li>\n\n\n\n<li><a href=\"#parts-required\" title=\"\">Parts Required<\/a><\/li>\n\n\n\n<li><a href=\"#circuit-diagram\" title=\"\">Circuit Diagram<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#html-file\" title=\"\">HTML File<\/a><\/li>\n\n\n\n<li><a href=\"#code\" title=\"\">Datalogger and Web Server Code<\/a><\/li>\n\n\n\n<li><a href=\"#demonstration\" title=\"\">Demonstration<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"project-overview\">Project Overview<\/h2>\n\n\n\n<p>Here&#8217;s a quick overview of the features of this project.<\/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=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger: Download Data File via Web Server\" class=\"wp-image-151246\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.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<ul class=\"wp-block-list\">\n<li>The ESP32 will log data and the corresponding timestamp to a file on a microSD card.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We&#8217;ll log data from a BME280 sensor, but you can use any other sensor or <a href=\"https:\/\/randomnerdtutorials.com\/esp32-guides-sensors-modules\/\" title=\"\">sensors<\/a>.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We&#8217;ll save the timestamp in <a href=\"https:\/\/randomnerdtutorials.com\/epoch-unix-time-esp32-arduino\/\" title=\"\">epoch time<\/a>. Then, when processing the data, you can convert it to your local time.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The ESP32 will also host a web server that you can access on your computer or smartphone as long as they are on the same network.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The web server allows you to visualize the data saved on the file, download the data file to your computer, or delete the file from the microSD card.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The HTML file to build the ESP32 web server will also be hosted on the microSD card.<\/li>\n<\/ul>\n\n\n\n<p>In this example, we&#8217;ll get the time from the internet (NTP server), so the ESP32 needs to be connected to a router. If the board doesn&#8217;t have access to the internet, you can set it as an <a href=\"https:\/\/randomnerdtutorials.com\/esp32-access-point-ap-web-server\/\" title=\"\">access point<\/a>, and get the time from an RTC module (for example <a href=\"https:\/\/makeradvisor.com\/tools\/real-time-clock-module-ds1307\/\" target=\"_blank\" rel=\"noopener\" title=\"\">DS1307 <\/a>or DS3231).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>Before proceeding, make sure you check the follow the next prerequisites.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"arduino-ide\">Arduino IDE<\/h3>\n\n\n\n<p>We&#8217;ll program the ESP32 using Arduino IDE. So make sure you have the ESP32 boards installed. You can use Arduino IDE 2 or the legacy version.<\/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<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-the-esp32-board-in-arduino-ide-windows-instructions\/\" title=\"\">Installing the ESP32 Board in Arduino IDE (Arduino 1.8.x)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"libraries\">Libraries<\/h3>\n\n\n\n<p>You also 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> (Arduino Library Manager)<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Unified Sensor Driver<\/a> (it will install automatically with the Adafruit BME280, otherwise, you need to install it manually)<\/li>\n\n\n\n<li><strong><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer\u00a0<\/a>by ESP32Async<\/strong><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\"><strong>AsyncTCP<\/strong><\/a><strong> by ESP32Async<\/strong><\/li>\n<\/ul>\n\n\n\n<p id=\"block-ff352e7d-a8f9-4463-b910-bba6133ddd9a\">You can install the first two libraries using the Arduino Library Manager. Go to\u00a0<strong>Sketch\u00a0<\/strong>>\u00a0<strong>Include Library<\/strong>\u00a0>\u00a0<strong>Manage Libraries<\/strong>\u00a0and search for the library name.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"format-microsd-card\">Formatting the MicroSD Card<\/h3>\n\n\n\n<p>Before proceeding with the tutorial, make sure you&nbsp;<strong>format your microSD card as FAT32<\/strong>. Follow the next instructions to format your microSD card or use a software tool like&nbsp;<a href=\"https:\/\/www.sdcard.org\/downloads\/formatter\/\" target=\"_blank\" rel=\"noreferrer noopener\">SD Card Formatter<\/a>&nbsp;(compatible with Windows and Mac OS).<\/p>\n\n\n\n<p><strong>1.<\/strong>&nbsp;Insert the microSD card into your computer. Go to&nbsp;<strong>My Computer<\/strong>&nbsp;and right-click on the SD card. Select&nbsp;<strong>Format&nbsp;<\/strong>as shown in the figure below.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/rntlab.com\/wp-content\/uploads\/2018\/05\/format-SD-card-1.png?w=828&amp;quality=100&amp;strip=all&amp;ssl=1\" alt=\"MicroSD Card Module format sd card\" class=\"wp-image-11805\"\/><\/figure><\/div>\n\n\n<p><strong>2.&nbsp;<\/strong>A new window pops up. Select&nbsp;<strong>FAT32<\/strong>, press&nbsp;<strong>Start<\/strong>&nbsp;to initialize the formatting process, and follow the onscreen instructions.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/rntlab.com\/wp-content\/uploads\/2018\/05\/format-SD-card-2.png?w=828&amp;quality=100&amp;strip=all&amp;ssl=1\" alt=\"MicroSD Card Module format sd card\" class=\"wp-image-11806\"\/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"parts-required\">Parts Required<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-BME280-microSD-card-circuit.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME280 and microSD card circuit\" class=\"wp-image-151248\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-BME280-microSD-card-circuit.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-BME280-microSD-card-circuit.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 follow this tutorial, 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=\"noreferrer noopener\">ESP32 development board<\/a>&nbsp;(read:&nbsp;<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">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<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/sd-card-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">MicroSD Card Module<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/microsd-card-raspberry-pi-16gb-class-10\/\" target=\"_blank\" rel=\"noreferrer noopener\">MicroSD Card<\/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\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<\/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<h3 class=\"wp-block-heading\" id=\"circuit-diagram\">Circuit Diagram<\/h3>\n\n\n\n<p>For this example, wire the microSD card module and the BME280 sensor to the ESP32. Follow the next schematic diagram or the tables below to wire the circuit.<\/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=\"860\" height=\"926\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-microSD-card-BME280-circuit-diagram.png?resize=860%2C926&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 microSD card BME280 circuit diagram schematic\" class=\"wp-image-102366\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-microSD-card-BME280-circuit-diagram.png?w=860&amp;quality=100&amp;strip=all&amp;ssl=1 860w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-microSD-card-BME280-circuit-diagram.png?resize=279%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 279w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-microSD-card-BME280-circuit-diagram.png?resize=768%2C827&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/figure><\/div>\n\n\n<p>You can also take a look at the following tables:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>BME280<\/strong><\/td><td><strong>ESP32<\/strong><\/td><\/tr><tr><td>VIN<\/td><td><span class=\"rnthl rntcred\">3V3<\/span><\/td><\/tr><tr><td>GND<\/td><td><span class=\"rnthl rntcblack\">GND<\/span><\/td><\/tr><tr><td>SCL<\/td><td><span class=\"rnthl rntcyellow\">GPIO 22<\/span><\/td><\/tr><tr><td>SDA<\/td><td><span class=\"rnthl rntcyellow\">GPIO 21<\/span><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>microSD card module<\/strong><\/td><td><strong>ESP32<\/strong><\/td><\/tr><tr><td>3V3<\/td><td><span class=\"rnthl rntcred\">3.3V<\/span><\/td><\/tr><tr><td>CS<\/td><td><span class=\"rnthl rntclblue\">GPIO 5<\/span><\/td><\/tr><tr><td>MOSI<\/td><td><span class=\"rnthl rntclblue\">GPIO 23<\/span><\/td><\/tr><tr><td>CLK<\/td><td><span class=\"rnthl rntclblue\">GPIO 18<\/span><\/td><\/tr><tr><td>MISO<\/td><td><span class=\"rnthl rntclblue\">GPIO 19<\/span><\/td><\/tr><tr><td>GND<\/td><td><span class=\"rnthl rntcblack\">GND<\/span><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Not familiar with the BME280 sensor?<\/strong> Read the following guide<\/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<p><strong>Not familiar with using a microSD card with the ESP32?<\/strong> Check the following tutorials:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-microsd-card-arduino\/\">ESP32: Guide for MicroSD Card Module using Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-microsd-card\/\">ESP32 Web Server Hosting Files from MicroSD Card<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-temperature-to-microsd-card\/\">ESP32 Data Logging Temperature to MicroSD Card<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"html-file\">HTML File<\/h2>\n\n\n\n<p>The following HTML file will build the web server page that we can interact with to manage the data on the ESP32. <\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;\r\n    &lt;title&gt;ESP32 Datalogger&lt;\/title&gt;\r\n    &lt;style&gt;\r\n        html {\r\n            font-family: Arial, Helvetica, sans-serif;\r\n        }\r\n\r\n        body {\r\n            background-color: #f4f4f4;\r\n            margin: 0;\r\n            padding: 0;\r\n        }\r\n\r\n        .container {\r\n            max-width: 800px;\r\n            margin: 50px auto;\r\n            text-align: center;\r\n        }\r\n\r\n        h1 {\r\n            color: #333;\r\n        }\r\n\r\n        .button {\r\n            display: inline-block;\r\n            padding: 10px 20px;\r\n            margin: 10px;\r\n            font-size: 16px;\r\n            border: none;\r\n            border-radius: 5px;\r\n            cursor: pointer;\r\n            transition-duration: 0.4s;    }\r\n\r\n        .button-data {\r\n            background-color: #858585;\r\n            color: #fff;\r\n        }\r\n\r\n        .button-delete {\r\n            background-color: #780320;\r\n            color: #fff;\r\n        }\r\n\r\n        .button:hover {\r\n            background-color: #0056b3;\r\n        }\r\n    &lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n    &lt;div class=&quot;container&quot;&gt;\r\n        &lt;h1&gt;ESP32 Datalogger - Manage Data&lt;\/h1&gt;\r\n        &lt;a href=&quot;view-data&quot;&gt;&lt;button class=&quot;button button-data&quot;&gt;View Data&lt;\/button&gt;&lt;\/a&gt;\r\n        &lt;a href=&quot;download&quot;&gt;&lt;button class=&quot;button button-data&quot;&gt;Download Data&lt;\/button&gt;&lt;\/a&gt;\r\n        &lt;a href=&quot;delete&quot;&gt;&lt;button class=&quot;button button-delete&quot;&gt;Delete Data&lt;\/button&gt;&lt;\/a&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/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_Datalogger_Download_File_Web_Server\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>The web page has three buttons:<\/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=\"668\" height=\"441\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?resize=668%2C441&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger - Manage Data Web Server\" class=\"wp-image-151233\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?w=668&amp;quality=100&amp;strip=all&amp;ssl=1 668w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?resize=300%2C198&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 668px) 100vw, 668px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>View Data<\/strong>: will show the data file contents \u2014 makes a request on the <span class=\"rnthl rntliteral\">\/view-data<\/span> path;<\/li>\n\n\n\n<li><strong>Download Data<\/strong>: downloads the data file to your device \u2014 makes a request on the <span class=\"rnthl rntliteral\">\/download<\/span> path;<\/li>\n\n\n\n<li><strong>Delete Data<\/strong>: deletes the data file from the microSD card \u2014 makes a request on the <span class=\"rnthl rntliteral\">\/delete<\/span> path.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Copy the HTML File to the microSD card<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a file called index.html and copy the HTML text we&#8217;ve shown you previously. <\/li>\n\n\n\n<li>Copy that file to the microSD card.<\/li>\n\n\n\n<li>Insert the microSD card into the microSD card module that should be connected to the ESP32.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"code\">Datalogger and Web Server Code<\/h2>\n\n\n\n<p>The following code creates an ESP32 datalogger that will log BME280 sensor data and the corresponding timestamp to a file called <em>data.txt<\/em> on the microSD card. At the same time, it also hosts a web server and shows a web page (built from the HTML file you copied to the microSD card) that you can access to manage the data.<\/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  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-datalogger-download-data-file\/\n\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;Arduino.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;\n#include &quot;FS.h&quot;\n#include &quot;SD.h&quot;\n#include &quot;SPI.h&quot;\n#include &lt;Adafruit_BME280.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &quot;time.h&quot;\n#include &lt;WiFiUdp.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\/\/ NTP server to request epoch time\nconst char* ntpServer = &quot;pool.ntp.org&quot;;\n\n\/\/ Variable to save current epoch time\nunsigned long epochTime; \n\n\/\/ Variables to hold sensor readings\nfloat temp;\nfloat hum;\nfloat pres;\nString dataMessage;\n\n\/\/ File name where readings will be saved\nconst char* dataPath = &quot;\/data.txt&quot;;\n\n\/\/ Timer variables\nunsigned long lastTime = 0;\nunsigned long timerDelay = 30000;\n\n\/\/ Create AsyncWebServer object on port 80\nAsyncWebServer server(80);\n\n\/\/ BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)\nAdafruit_BME280 bme;\n\n\/\/ Init BME280\nvoid initBME(){\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\n\/\/ Init microSD card\nvoid initSDCard(){\n  if(!SD.begin()){\n    Serial.println(&quot;Card Mount Failed&quot;);\n    return;\n  }\n  uint8_t cardType = SD.cardType();\n\n  if(cardType == CARD_NONE){\n    Serial.println(&quot;No SD card attached&quot;);\n    return;\n  }\n\n  Serial.print(&quot;SD Card Type: &quot;);\n  if(cardType == CARD_MMC){\n    Serial.println(&quot;MMC&quot;);\n  } else if(cardType == CARD_SD){\n    Serial.println(&quot;SDSC&quot;);\n  } else if(cardType == CARD_SDHC){\n    Serial.println(&quot;SDHC&quot;);\n  } else {\n    Serial.println(&quot;UNKNOWN&quot;);\n  }\n  uint64_t cardSize = SD.cardSize() \/ (1024 * 1024);\n  Serial.printf(&quot;SD Card Size: %lluMB\\n&quot;, cardSize);\n}\n\n\/\/ Write to the SD card\nvoid writeFile(fs::FS &amp;fs, const char * path, const char * message) {\n  Serial.printf(&quot;Writing file: %s\\n&quot;, path);\n\n  File file = fs.open(path, FILE_WRITE);\n  if(!file) {\n    Serial.println(&quot;Failed to open file for writing&quot;);\n    return;\n  }\n  if(file.print(message)) {\n    Serial.println(&quot;File written&quot;);\n  } else {\n    Serial.println(&quot;Write failed&quot;);\n  }\n  file.close();\n}\n\n\/\/ Append data to the SD card\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message) {\n  Serial.printf(&quot;Appending to file: %s\\n&quot;, path);\n\n  File file = fs.open(path, FILE_APPEND);\n  if(!file) {\n    Serial.println(&quot;Failed to open file for appending&quot;);\n    return;\n  }\n  if(file.print(message)) {\n    Serial.println(&quot;Message appended&quot;);\n  } else {\n    Serial.println(&quot;Append failed&quot;);\n  }\n  file.close();\n}\n\n\/\/ Delete file\nvoid deleteFile(fs::FS &amp;fs, const char * path){\n  Serial.printf(&quot;Deleting file: %s\\r\\n&quot;, path);\n  if(fs.remove(path)){\n    Serial.println(&quot;- file deleted&quot;);\n  } else {\n    Serial.println(&quot;- delete failed&quot;);\n  }\n}\n\n\/\/ Function that gets current epoch time\nunsigned long getTime() {\n  time_t now;\n  struct tm timeinfo;\n  if (!getLocalTime(&amp;timeinfo)) {\n   \/\/Serial.println(&quot;Failed to obtain time&quot;);\n    return(0);\n  }\n  time(&amp;now);\n  return now;\n}\n\n\/\/ Function that initializes wi-fi\nvoid initWiFi() {\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid, password);\n  Serial.print(&quot;Connecting to WiFi ..&quot;);\n  while (WiFi.status() != WL_CONNECTED) {\n    Serial.print('.');\n    delay(1000);\n  }\n  Serial.println(WiFi.localIP());\n}\n\nvoid setup() {\n  Serial.begin(115200);\n  initWiFi();\n  initBME();\n  initSDCard();\n  configTime(0, 0, ntpServer);\n\n  \/\/ If the data.txt file doesn't exist\n  \/\/ Create a file on the SD card and write the data labels\n  File file = SD.open(&quot;\/data.txt&quot;);\n  if(!file) {\n    Serial.println(&quot;File doesn't exist&quot;);\n    Serial.println(&quot;Creating file...&quot;);\n    writeFile(SD, &quot;\/data.txt&quot;, &quot;Epoch Time, Temperature, Humidity, Pressure \\r\\n&quot;);\n  }\n  else {\n    Serial.println(&quot;File already exists&quot;);  \n  }\n  file.close();\n\n  \/\/ Handle the root URL\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\n    request-&gt;send(SD, &quot;\/index.html&quot;, &quot;text\/html&quot;);\n  });\n\n  \/\/ Handle the download button\n  server.on(&quot;\/download&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\n    request-&gt;send(SD, &quot;\/data.txt&quot;, String(), true);\n  });\n\n  \/\/ Handle the View Data button\n  server.on(&quot;\/view-data&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\n    request-&gt;send(SD, &quot;\/data.txt&quot;, &quot;text\/plain&quot;, false);\n  });\n\n  \/\/ Handle the delete button\n  server.on(&quot;\/delete&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\n    deleteFile(SD, dataPath);\n    request-&gt;send(200, &quot;text\/plain&quot;, &quot;data.txt was deleted.&quot;);\n  });\n\n  \/\/ Uncomment the following line if you need to serve more static files like CSS and javascript or favicon\n  \/\/server.serveStatic(&quot;\/&quot;, SD, &quot;\/&quot;);\n\n  server.begin();\n}\n\nvoid loop() {\n  if ((millis() - lastTime) &gt; timerDelay) {\n    \/\/Get epoch time\n    epochTime = getTime();\n    \n    \/\/Get sensor readings\n    temp = bme.readTemperature();\n    \/\/temp = 1.8*bme.readTemperature() + 32;\n    hum = bme.readHumidity();\n    pres = bme.readPressure()\/100.0F;\n\n    \/\/Concatenate all info separated by commas\n    dataMessage = String(epochTime) + &quot;,&quot; + String(temp) + &quot;,&quot; + String(hum) + &quot;,&quot; + String(pres)+ &quot;\\r\\n&quot;;\n    Serial.print(&quot;Saving data: &quot;);\n    Serial.println(dataMessage);\n\n    \/\/Append the data to file\n    appendFile(SD, &quot;\/data.txt&quot;, dataMessage.c_str());\n\n    lastTime = millis();\n  }\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_Datalogger_Download_File_Web_Server\/ESP32_Datalogger_Download_File_Web_Server.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Before uploading the code to the board, you need to insert your network credentials on the following variables.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Replace with your network credentials\nconst char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\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 <a href=\"#demonstration\" title=\"\">Demonstration <\/a>section.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Including Libraries<\/h4>\n\n\n\n<p>Start by including the required libraries. We include the libraries to connect to Wi-Fi, create the web server, handle files, communicate with the microSD card, communicate with the BME280 sensor and get the time from an NTP server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Arduino.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;\n#include \"FS.h\"\n#include \"SD.h\"\n#include \"SPI.h\"\n#include &lt;Adafruit_BME280.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include \"time.h\"\n#include &lt;WiFiUdp.h&gt;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Network Credentials<\/h4>\n\n\n\n<p>Insert your network credentials on the following variables so that the ESP32 can connect to your local network.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Replace with your network credentials\nconst char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize Variables<\/h4>\n\n\n\n<p>Then, we initialize some variables that we&#8217;ll use throughout the code.<\/p>\n\n\n\n<p>the <span class=\"rnthl rntliteral\">ntpServer<\/span> saves the URL of the NTP server we&#8217;ll use to get the time.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ntpServer = \"pool.ntp.org\";<\/code><\/pre>\n\n\n\n<p>We&#8217;ll save the timestamp in epoch time on the <span class=\"rnthl rntliteral\">epochTime<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Variable to save current epoch time\nunsigned long epochTime; <\/code><\/pre>\n\n\n\n<p>The following variables will hold BME280 sensor readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>float temp;\nfloat hum;\nfloat pres;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">dataMessage<\/span> variable will hold the concatenation of all readings separated by commas to be inserted in the data file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String dataMessage;<\/code><\/pre>\n\n\n\n<p>The data will be saved on a file called <em>data.txt <\/em>on the root of the microSD card. The <span class=\"rnthl rntliteral\">dataPath<\/span> variable saves the name and path of that file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* dataPath = \"\/data.txt\";<\/code><\/pre>\n\n\n\n<p>We&#8217;ll get new data every 30 seconds (30000milliseconds). You can change the sampling period on the <span class=\"rnthl rntliteral\">timerDelay<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Timer variables\nunsigned long lastTime = 0;\nunsigned long timerDelay = 30000;<\/code><\/pre>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">AsyncWebServer<\/span> instance on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create AsyncWebServer object on port 80\nAsyncWebServer server(80);<\/code><\/pre>\n\n\n\n<p>And create an <span class=\"rnthl rntliteral\">Adafruit_BME280<\/span> object called <span class=\"rnthl rntliteral\">bme<\/span>. It will use the ESP32 default I2C pins (GPIO 21 (SDA), and GPIO 22 (SCL))<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)\nAdafruit_BME280 bme;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize the BME280 Sensor<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initBME()<\/span> function will initialize the BME280 sensor. We&#8217;re setting the I2C address to 0x76, which is usually the address for these sensors. However, it might be different. You can check that using an <a href=\"https:\/\/randomnerdtutorials.com\/esp32-i2c-scanner-arduino\/\" title=\"\">I2C scan<\/a><a href=\"https:\/\/randomnerdtutorials.com\/esp32-i2c-scanner-arduino\/\" target=\"_blank\" rel=\"noopener\" title=\"\">n<\/a><a href=\"https:\/\/randomnerdtutorials.com\/esp32-i2c-scanner-arduino\/\" title=\"\">er<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init BME280\nvoid initBME(){\n  if (!bme.begin(0x76)) {\n    Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");\n    while (1);\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize the MicroSD Card<\/h4>\n\n\n\n<p>The following function will initialize the communication with microSD card on the ESP32 default SPI pins. It will also print some information about the microSD card type and size.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init microSD card\nvoid initSDCard(){\n  if(!SD.begin()){\n    Serial.println(\"Card Mount Failed\");\n    return;\n  }\n  uint8_t cardType = SD.cardType();\n\n  if(cardType == CARD_NONE){\n    Serial.println(\"No SD card attached\");\n    return;\n  }\n\n  Serial.print(\"SD Card Type: \");\n  if(cardType == CARD_MMC){\n    Serial.println(\"MMC\");\n  } else if(cardType == CARD_SD){\n    Serial.println(\"SDSC\");\n  } else if(cardType == CARD_SDHC){\n    Serial.println(\"SDHC\");\n  } else {\n    Serial.println(\"UNKNOWN\");\n  }\n  uint64_t cardSize = SD.cardSize() \/ (1024 * 1024);\n  Serial.printf(\"SD Card Size: %lluMB\\n\", cardSize);\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Write Data to a File<\/h4>\n\n\n\n<p>The following function allows us to write data to a file. To use this function you must pass as arguments the filesystem type, the file path and the message to be written.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Write to the SD card\nvoid writeFile(fs::FS &amp;fs, const char * path, const char * message) {\n  Serial.printf(\"Writing file: %s\\n\", path);\n\n  File file = fs.open(path, FILE_WRITE);\n  if(!file) {\n    Serial.println(\"Failed to open file for writing\");\n    return;\n  }\n  if(file.print(message)) {\n    Serial.println(\"File written\");\n  } else {\n    Serial.println(\"Write failed\");\n  }\n  file.close();\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Append Data to a File<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">writeFile()<\/span> function overwrites any existing data on a file. To append data to a file, we have the <span class=\"rnthl rntliteral\">appendFile()<\/span> function. It works as the previous function, but will append data instead of overwritting.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Append data to the SD card\nvoid appendFile(fs::FS &amp;fs, const char * path, const char * message) {\n  Serial.printf(\"Appending to file: %s\\n\", path);\n\n  File file = fs.open(path, FILE_APPEND);\n  if(!file) {\n    Serial.println(\"Failed to open file for appending\");\n    return;\n  }\n  if(file.print(message)) {\n    Serial.println(\"Message appended\");\n  } else {\n    Serial.println(\"Append failed\");\n  }\n  file.close();\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Delete a File<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">deleteFile()<\/span> function deletes a file from the specified filesystem and path.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Delete file\nvoid deleteFile(fs::FS &amp;fs, const char * path){\n  Serial.printf(\"Deleting file: %s\\r\\n\", path);\n  if(fs.remove(path)){\n    Serial.println(\"- file deleted\");\n  } else {\n    Serial.println(\"- delete failed\");\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Get Time<\/h4>\n\n\n\n<p>The following function gets and returns the current epoch time.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Function that gets current epoch time\nunsigned long getTime() {\n  time_t now;\n  struct tm timeinfo;\n  if (!getLocalTime(&amp;timeinfo)) {\n   \/\/Serial.println(\"Failed to obtain time\");\n    return(0);\n  }\n  time(&amp;now);\n  return now;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize Wi-Fi<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initWiFi()<\/span> function will connect your ESP32 to your local network using the SSID and password you inserted at the beginning of the code.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void initWiFi() {\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid, password);\n  Serial.print(\"Connecting to WiFi ..\");\n  while (WiFi.status() != WL_CONNECTED) {\n    Serial.print('.');\n    delay(1000);\n  }\n  Serial.println(WiFi.localIP());\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>, initialize the Serial Monitor, connect the ESP32 to Wi-Fi, initialize the BME280 sensor and the microSD card and configure the time server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);\ninitWiFi();\ninitBME();\ninitSDCard();\nconfigTime(0, 0, ntpServer);<\/code><\/pre>\n\n\n\n<p>Then, we create a new file on the microSD card called <em>data.txt<\/em> where we&#8217;ll save the data (if it doesn&#8217;t exist yet) and we&#8217;ll write the data headers to the file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ If the data.txt file doesn't exist\n\/\/ Create a file on the SD card and write the data labels\nFile file = SD.open(\"\/data.txt\");\nif(!file) {\n  Serial.println(\"File doesn't exist\");\n  Serial.println(\"Creating file...\");\n  writeFile(SD, \"\/data.txt\", \"Epoch Time, Temperature, Humidity, Pressure \\r\\n\");\n}\nelse {\n  Serial.println(\"File already exists\");  \n}\nfile.close();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Handle Requests<\/h4>\n\n\n\n<p>Finally, we need to handle what happens when we click on the web page buttons. As we&#8217;ve seen previously:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>View Data<\/strong>: will show the data file contents \u2014 makes a request on the \/view-data path;<\/li>\n\n\n\n<li><strong>Download Data<\/strong>: downloads the data file to your device \u2014 makes a request on the \/download path;<\/li>\n\n\n\n<li><strong>Delete Data<\/strong>: deletes the data file from the microSD card \u2014 makes a request on the \/delete path.<\/li>\n<\/ul>\n\n\n\n<p>The following line will serve the index.html file saved on the microSD card to show up the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(SD, \"\/index.html\", \"text\/html\");\n});<\/code><\/pre>\n\n\n\n<p class=\"rntbox rntclblue\">You may also like reading: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-microsd-card\/\">ESP32 Web Server Hosting Files from MicroSD Card<\/a><\/p>\n\n\n\n<p>When we click on the Download button, the ESP32 receives a request on the <strong>\/download<\/strong> URL, when that happens, we handle the request as follows.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Handle the download button\nserver.on(\"\/download\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(SD, \"\/data.txt\", String(), true);\n});<\/code><\/pre>\n\n\n\n<p>To download the file, we need to pass the following arguments to the <span class=\"rnthl rntliteral\">send()<\/span> function: filesystem, filepath, <span class=\"rnthl rntliteral\">String()<\/span>, and a boolean variable that indicates <span class=\"rnthl rntliteral\">true<\/span> = download). For more information you can check this<a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer?tab=readme-ov-file#respond-with-content-coming-from-a-file-and-extra-headers\" target=\"_blank\" rel=\"noopener\" title=\"\"> use case on the library documentation here<\/a>.<\/p>\n\n\n\n<p>When the ESP32 receives a request on the <strong>\/view-data path<\/strong>, we&#8217;ll respond with the contents of the <em>data.txt<\/em> file as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Handle the View Data button\nserver.on(\"\/view-data\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(SD, \u201c\/data.txt\u201d, \u201ctext\/plain\u201d, false);\n});<\/code><\/pre>\n\n\n\n<p>Finally, when the Delete button is clicked, we&#8217;ll call the <span class=\"rnthl rntliteral\">deleteFile()<\/span> function to detect the file from the microSD card.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/delete\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  deleteFile(SD, dataPath);\n  request-&gt;send(200, \"text\/plain\", \"data.txt was deleted.\");\n});<\/code><\/pre>\n\n\n\n<p>At the end of the <span class=\"rnthl rntliteral\">setup()<\/span>, we need to initialize the server with <span class=\"rnthl rntliteral\">server.begin()<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\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 get the time and new data from the sensor every 30 seconds and append it to the <em>data.txt<\/em> file on the microSD card.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void loop() {\n  if ((millis() - lastTime) &gt; timerDelay) {\n    \/\/Get epoch time\n    epochTime = getTime();\n    \n    \/\/Get sensor readings\n    temp = bme.readTemperature();\n    \/\/temp = 1.8*bme.readTemperature() + 32;\n    hum = bme.readHumidity();\n    pres = bme.readPressure()\/100.0F;\n\n    \/\/Concatenate all info separated by commas\n    dataMessage = String(epochTime) + \",\" + String(temp) + \",\" + String(hum) + \",\" + String(pres)+ \"\\r\\n\";\n    Serial.print(\"Saving data: \");\n    Serial.println(dataMessage);\n\n    \/\/Append the data to file\n    appendFile(SD, \"\/data.txt\", dataMessage.c_str());\n\n    lastTime = millis();\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"demonstration\">Demonstration<\/h2>\n\n\n\n<p>After inserting your network credentials in the code, you can upload it to your ESP32 board.<\/p>\n\n\n\n<p>After uploading, open the Serial Monitor at a baud rate of 115200 and press the ESP32 onboard RST button. It will connect to Wi-Fi and print its IP address.<\/p>\n\n\n\n<p>After that, it will create the file on the microSD card and it should start appending data to the file every 30 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=\"792\" height=\"799\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-microSD-card-serial-monitor.png?resize=792%2C799&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server, download file from microSD card\" class=\"wp-image-151231\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-microSD-card-serial-monitor.png?w=792&amp;quality=100&amp;strip=all&amp;ssl=1 792w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-microSD-card-serial-monitor.png?resize=297%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 297w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-microSD-card-serial-monitor.png?resize=150%2C150&amp;quality=100&amp;strip=all&amp;ssl=1 150w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-microSD-card-serial-monitor.png?resize=768%2C775&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 792px) 100vw, 792px\" \/><\/figure><\/div>\n\n\n<p>Let the code run for a while until the ESP32 gathers some data. Then, you can manage the data on the microSD card that the ESP32 is connected to by accessing the web server. Open a browser on your local network and type the ESP32 IP address.<\/p>\n\n\n\n<p>You should get 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=\"668\" height=\"441\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?resize=668%2C441&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Manage data\" class=\"wp-image-151233\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?w=668&amp;quality=100&amp;strip=all&amp;ssl=1 668w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Datalogger-Web-Server-Manage-Data.png?resize=300%2C198&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 668px) 100vw, 668px\" \/><\/figure><\/div>\n\n\n<p>You can click on the V<strong>iew Data<\/strong> button to see the raw data on the web 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=\"668\" height=\"945\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-datalogger-data-on-web-browser.png?resize=668%2C945&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Manage data web browser demonstration\" class=\"wp-image-151234\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-datalogger-data-on-web-browser.png?w=668&amp;quality=100&amp;strip=all&amp;ssl=1 668w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-datalogger-data-on-web-browser.png?resize=212%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 212w\" sizes=\"(max-width: 668px) 100vw, 668px\" \/><\/figure><\/div>\n\n\n<p>You can also download the file to your computer without the need to remove the microSD card from the adapter and insert it into your computer. You just need to click on the <strong>Download Data<\/strong> 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=\"183\" height=\"63\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/ESP32-Web-Server-Datalogger-Download-Data.png?resize=183%2C63&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Download button data\" class=\"wp-image-151235\"\/><\/figure><\/div>\n\n\n<p>Then, you can open the file on your computer and process the data as you wish.<\/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=\"515\" height=\"822\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/data-file-download-esp32-datalogger.png?resize=515%2C822&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Download button data .txt file open\" class=\"wp-image-151236\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/data-file-download-esp32-datalogger.png?w=515&amp;quality=100&amp;strip=all&amp;ssl=1 515w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/data-file-download-esp32-datalogger.png?resize=188%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 188w\" sizes=\"(max-width: 515px) 100vw, 515px\" \/><\/figure><\/div>\n\n\n<p>Finally, you can also delete the file from the microSD card by clicking the <strong>Delete Data<\/strong> button.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/Delete-data-esp32-datalogger.png?quality=100&#038;strip=all&#038;ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"142\" height=\"56\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/Delete-data-esp32-datalogger.png?resize=142%2C56&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Delete data button\" class=\"wp-image-151237\"\/><\/a><\/figure><\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/esp32-datalogger-data-was-deleted.png?quality=100&#038;strip=all&#038;ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"668\" height=\"315\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/esp32-datalogger-data-was-deleted.png?resize=668%2C315&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Datalogger Web Server Delete data button web browser\" class=\"wp-image-151238\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/esp32-datalogger-data-was-deleted.png?w=668&amp;quality=100&amp;strip=all&amp;ssl=1 668w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/03\/esp32-datalogger-data-was-deleted.png?resize=300%2C141&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 668px) 100vw, 668px\" \/><\/a><\/figure><\/div>\n\n\n<p>And that&#8217;s basically how the project works. Feel free to adapt what you learned here to your own projects.<\/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 download a file stored on a microSD card to your computer using a web server on the ESP32. This is very useful because you don&#8217;t need physical access to the card to get the files on your computer.<\/p>\n\n\n\n<p>We hope you found this tutorial useful. Here&#8217;s a list of related articles you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-temperature-to-microsd-card\/\">ESP32 Data Logging Temperature to MicroSD Card<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-microsd-card\/\">ESP32 Web Server Hosting Files from MicroSD Card<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-microsd-card-arduino\/\">ESP32: Guide for MicroSD Card Module using Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-gauges\/\">ESP32 Web Server: Display Sensor Readings in Gauges<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-firebase-gauges-charts\/\">ESP32\/ESP8266: Firebase Data Logging Web App (Gauges, Charts, and Table)<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you want to learn more about the ESP32, check out our resources:<\/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 (2nd Edition)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/smart-home-ebook\/\" title=\"\">SMART HOME with Raspberry Pi, ESP32, ESP8266<\/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<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/firebase-esp32-esp8266-ebook\/\" title=\"\">Firebase Web App with the ESP32 and ESP8266<\/a><\/li>\n\n\n\n<li><strong><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\" title=\"\">Free ESP32 Projects,Tutorials and Guides<\/a><\/strong><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this guide, we&#8217;ll build an ESP32 datalogger that also hosts a web server so that you can access and download the data remotely. The ESP32 will log data to &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Datalogger: Download Data File via Web Server (Arduino IDE)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-datalogger-download-data-file\/#more-151210\" aria-label=\"Read more about ESP32 Datalogger: Download Data File via Web Server (Arduino IDE)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":151242,"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-151210","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\/2024\/03\/ESP32-Datalogger-Download-File-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\/151210","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=151210"}],"version-history":[{"count":10,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/151210\/revisions"}],"predecessor-version":[{"id":167914,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/151210\/revisions\/167914"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/151242"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=151210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=151210"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=151210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}