ESP8266 Web Data Logger: create a web page to view temperature and humidity data


Welcome to the adventure of creating an ESP8266 Web Data Logger with the DHT22 sensor, enriched by the presence of SPIFFS (SPI Flash File System). This comprehensive guide will take you through building an advanced environmental monitoring system, making the process of acquiring and managing data an exciting experience.

The DHT22 sensor, known for its precise temperature and humidity readings, will be our ally in creating a reliable and accurate device. However, the real innovation lies in the use of SPIFFS, which allows the data logger web page to be archived directly on the flash memory of the ESP8266. This approach not only simplifies file management, but opens up new opportunities for customizing the web interface and data visualization.

Through this experience, you will not only learn how to create an advanced monitoring system but you will gain valuable skills in using SPIFFS to organize and manage files on your ESP8266. Get ready to explore the potential of this technological combination and turn environmental monitoring into an exciting project. Without further ado, let’s dive into the fascinating world of digital DIY, with a touch of innovation thanks to SPIFFS!

As usual we will use the excellent PlatformIO IDE for writing the firmware.

What is a file system

A file system is a mechanism for managing and organizing data on a storage device, such as a hard drive, flash memory, or even a removable device. It provides a logical abstraction of the data structure and allows saving, searching, modifying and deleting files and directories within the storage device. Here are some key features and functionality of a file system:

  1. Hierarchical organization: files and directories are organized in a hierarchical structure, allowing for easy data organization and navigation. This hierarchical structure is often represented as a tree, with a main (root) directory and subdirectories and files extending from it.
  2. Metadata: each file or directory in the file system is associated with metadata, such as the file name, creation date, last modification date, access permissions, and other relevant information. Metadata is essential for file management and control.
  3. Space management: a file system manages the space available on the storage device. This includes allocating space for new files, managing free space, and the ability to release space when a file is deleted.
  4. Access and security: file systems include mechanisms to control access to files and directories. This can be accomplished through read, write, and execute permissions assigned to specific users or groups of users.
  5. File types: a file system supports different file types, such as text documents, images, executable programs, and other formats. Each file type can be handled appropriately by the operating system and applications.
  6. Redundancy and error recovery: some file systems include mechanisms for error handling and data redundancy to protect against information loss due to hardware failure or other problems.
  7. Types of file systems: there are several types of file systems, each designed for specific purposes. For example, FAT (File Allocation Table) is commonly used on SD cards, while NTFS (New Technology File System) is often used on Windows operating systems. File systems like ext4 are popular in Linux environments.
  8. Storage facilities: file systems can use different storage structures, such as file allocation tables (FATs), inodes, or more complex algorithms such as B-tree or MFT (Master File Table).

Overall, a file system plays a vital role in ensuring the effective management and access of data on storage devices, thus contributing to the proper functioning of computer systems.

The utility of the SPIFFS file system: a technical insight

SPIFFS (SPI Flash File System) presents itself as an essential element in the panorama of data storage technologies for ESP8266-based devices. It is a file system designed specifically for use on SPI flash memory, providing remarkable efficiency and flexibility.

Distinctive features of SPIFFS include:

  1. Built-in Flash Memory: the system takes advantage of the flash memory built into the ESP8266, offering additional storage space without the need for additional hardware.
  2. Direct access to files: the SPIFFS file management approach allows direct and easy access to archived data. This feature greatly simplifies reading and writing operations.
  3. Suitable for small files: SPIFFS is optimized to operate in environments where file sizes are relatively small. It is ideal for managing resources such as web pages, images, and configuration data.
  4. Flexibility in the organization: allows you to organize files in a hierarchical way, allowing an orderly and easily manageable structure.
  5. Common use in IoT projects: SPIFFS is widely employed in Internet of Things (IoT) projects and embedded applications, offering an efficient alternative for storing non-volatile data.
  6. Interface with the standard file system: although designed to operate on flash memory, SPIFFS offers a standard file system interface, simplifying implementation in applications.

In summary, SPIFFS represents an invaluable resource for enriching the storage capabilities of ESP8266 platforms, helping to make these devices more versatile and functional in the contexts in which they are used. Its adoption in IoT and embedded projects is testimony to its effectiveness in optimizing storage space and simplifying data management.

Our project is a data logger that measures temperature and humidity every 5 seconds. The measured values ​​are shown via a web page that is served by the webserver running on the ESP8266. This web page also contains two buttons that are used to turn each relay on/off independently. The relays, compatibly with the maximum values ​​allowed for the loads, can be used to activate heaters (to heat a room) or fans (to cool a part of the room or a specific object) or to activate a dehumidifier or a humidifier. Just respect the maximum power tolerated by the relay contacts.

We will use this file system to store the html file that will display the measured data on the web page and the relay control buttons. Generally we can use this file system to store files like html, css and javascript.

The interface will look like the following image:

Example screen
Example screen

As you can see, a line appears with the current time and the temperature and humidity values. On the row below there are the two buttons. A button appears red if the corresponding relay is deactivated, it appears green if the corresponding relay is activated.

What components do we need for our data logger?

The list of components is not particularly long:

The double relay module

  1. Power supply:
    • It accepts a wide range of supply voltages, usually between 5V and 12V.
    • The power connector is designed to be easily connected to an external power source, such as a battery or power supply.
  2. Relay:
    • Two relays on board, each with its own electrical contacts: common (COM), normal open (NO) and normal closed (NC).
    • The relay contacts are designed to handle power loads. However, the exact specifications depend on the specific model of the relay module.
  3. Control Inputs:
    • Two control inputs (IN1 and IN2) that can be connected to digital pins of an Arduino-type development board.
    • Activating one of these inputs with a high (or low, as appropriate) logic signal will activate the corresponding relay.
  4. LED indicators:
    • Built-in LED indicators for each relay that indicate activation status (often with colors such as red for activated and off for deactivated).
  5. Arduino compatibility:
    • Designed to be easily integrated with development platforms such as Arduino, making relay control a simple and accessible operation.
  6. Pilotable Loads:
    • Capable of driving a variety of electrical loads such as light bulbs, motors, solenoid valves, and other devices requiring on/off control.
    • The exact load specifications depend on the relay model, but they can often handle loads with alternating voltages up to 250V and currents up to 10A.

These relay modules are widely used in home automation, electronic automation and remote control projects, providing a safe and controlled interface for power devices.

An example of a double opto-isolated relay module used by the ESP8266 Web Data Logger
An example of a double opto-isolated relay module used by the ESP8266 Web Data Logger

The DHT22 sensor

Here is a detailed technical overview of the main features of the DHT22 sensor:

  1. Accuracy of Measurements: the DHT22, also known as AM2302, is a digital temperature and humidity sensor that offers remarkable precision in measurements. The temperature accuracy is ±0.5°C, while for humidity it is ±2%.
  2. Wide Measurement Range: the sensor is able to measure the temperature in a range from -40°C to 80°C, therefore covering a wide range of environmental conditions. As for humidity, the range is between 0% and 100%.
  3. Fast answer: thanks to its ability to respond quickly to environmental changes, the DHT22 is ideal for monitoring conditions in real time.
  4. Digital Signals: the sensor transmits data directly in digital form, simplifying interfacing with microcontrollers such as the ESP8266. This avoids the need for external analog-to-digital converters.
  5. Reliability and Durability: designed to offer long life, the DHT22 features good reliability over time, making it suitable for long-term monitoring applications.
  6. Easy to use: with its single-wire communication interface, the DHT22 simplifies integration into electronic designs. Communication occurs via a two-wire communication protocol (data and ground).
  7. Integrated Calibration: the sensor includes internal calibration, reducing the need for external corrections and ensuring greater measurement accuracy.
  8. Wide Applicability: due to its features and ease of integration, the DHT22 is used in a variety of applications, including personal weather stations, air conditioning systems, and environmental monitoring projects.

Project implementation

The wiring diagram of the Esp8266 Web Data Logger

Before creating the actual circuit let’s take a look at the pinout of the board:

ESP8266 NodeMCU pinout
ESP8266 NodeMCU pinout

The DHT22 sensor pinout:

DHT22 pinout
DHT22 pinout

Let’s now see the electrical diagram of the project, created as usual with Fritzing:

Electrical diagram of the ESP8266 Web Data Logger
Electrical diagram of the ESP8266 Web Data Logger

The DHT22 sensor is connected to GPIO D4 while the IN1 and IN2 inputs of the relay module are connected to GPIOs D0 and D1 respectively.

As you can see the scheme is not particularly complex. Rather you have to pay attention to the power supplies since they are separated between the sensor power section and the relay module power section. As you can see, the power for the sensor is taken from the NodeMCU’s 3.3V output (pin 3V3). It is necessary to power it with 3.3V so that its output is also 3.3V as the digital pins of the NodeMCU do not accept voltages higher than 3.3V.

Instead, the power supply to the relay module is taken from the Vin pin of the NodeMCU which supplies the 5V necessary to drive the relay coils.

PAY ATTENTION: in the ESP8266 NodeMCU the maximum voltage tolerated by the digital inputs is equal to 3.3V. Any higher voltage would damage it irreparably!!

Currently the double relay module is not present in Fritzing but, if you want to use it in your projects, you can download it from here.

Let’s analyze the connection of the relay module in more detail.

The two Vin and GND pins of the ESP8266 NodeMCU are used to power the dual relay module, connecting the Vin pin (NodeMCU side) to the VCC pin (dual relay module side) and the two GND pins (ground).

Two other connections are used to control the relay module and they are the orange wire and the brown wire which connect the two digital outputs D0 and D1 of the NodeMCU to the IN1 and IN2 inputs of the relay module.

You will notice that on the relay module there is a jumper (drawn in blue on the left connector) that connects the JD-VCC and VCC terminals. This jumper is used to power the relay module through the VCC and GND terminals on the right connector. Without this jumper, we would be forced to power the module with an external power supply.

Each relay can drive a load independently of the other.

The sketch

Let’s create the PlatformIO project

We have already seen the procedure for creating a PlatformIO project in the article How to create a project for NodeMCU ESP8266 with PlatformIO.

Of the libraries indicated, install the DHT sensor library for ESPx by Bernd Giesecke and the WiFiManager by tzapu.

Now edit the platformio.ini file so it looks like this:

platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
board_build.flash_mode = dout
board_flash_size = 4MB
build_flags = 
lib_deps = 
	[email protected]
	beegee-tokyo/DHT sensor library for ESPx@^1.19

As you can see there is a part:

board_build.flash_mode = dout
board_flash_size = 4MB
build_flags = 

which is used to manage the SPIFFS file system while in the lib_deps section we are adding the [email protected], ArduinoJson, ESP8266mDNS libraries with the alternative method that PlatformIO offers: adding them directly to the platformio.ini file instead of going through the search window.

Obviously you can download the project from the following link:

Replace the main.cpp file of the project you created with the one present in the zip file. In addition to this operation, create a folder named data in the project folder where you will copy the index.html file present in the zip file. This file represents the web interface that will be exposed by the ESP8266. We will see later how to load it onto your file system (the usual compilation and transfer operation transfers only the compiled sketch but not the index.html file which requires a separate operation).

Now let’s see how the sketch works.

Initially the necessary libraries are included:

#include <Arduino.h>
#include "DHTesp.h"
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <FS.h>
#include <WiFiManager.h>

Below is the definition of the GPIO on which to connect the DHT22 sensor:

// Set the DHT22 sensor pin
DHTesp dht;
#define  DHT22_PIN D4 

The variables that regulate the reading timing of the DHT22 are then defined:

unsigned long measureDelay = 2000;                //    NOT LESS THAN 2000!!!!!   
unsigned long lastTimeRan;

and then the GPIOs on which to connect the relay module are defined:

const int relay1Pin = D0;  // GPIO where relay 1 is connected
const int relay2Pin = D1;  // GPIO where relay 2 is connected

The variables that contain the states of the two relays are then defined:

bool relay1State = false;
bool relay2State = false;

and the temperature and humidity variables both as a float and as a string, in addition to the webserver that listens on port 80:

String temperature_s = "";
String humidity_s = "";

float temperature, humidity;

ESP8266WebServer server(80);

The handleGetData function follows which creates a json document containing the current temperature and humidity values ​​(previously transformed into strings):

void handleGetData() {
  //Example of data logger data in JSON format
  temperature_s = String(temperature);
  humidity_s = String(humidity);
  String jsonData = "{ \"temperature\": " + temperature_s + ", \"humidity\": " + humidity_s + " }";
  // Send the response to the client
  server.send(200, "application/json", jsonData);

Then follows the readFile function which reads the index.html file from the SPIFFS file system, puts the contents in the content variable and returns it to the function that called it:

String readFile(String path) {
  // Read the file from SPIFFS storage
  File file =, "r");
  if (!file) {
    Serial.println("Error opening the file " + path);
    return "";

  // Read the contents of the file
  String content = file.readString();

  return content;

The handleRoot function reads the html part contained in the index.html file and returns the code 200 to the client.

void handleRoot() {
  // Read HTML content from SPIFFS
  String html = readFile("/index.html");

  // Send the response to the client
  server.send(200, "text/html", html);

The handleRelay1 and handleRelay2 functions each manage the state of the reference relay depending on the value contained in the relay1State and relay2State variables respectively:

void handleRelay1() {
  // Change the state of relay 1
  relay1State = !relay1State;
  digitalWrite(relay1Pin, relay1State ? LOW : HIGH);

  Serial.print("\nrelay1State ");

  // Sends the current state of relay 1 to the client
  server.send(200, "text/plain", relay1State ? "ON" : "OFF");

void handleRelay2() {
  // Change the state of relay 2
  relay2State = !relay2State;
  digitalWrite(relay2Pin, relay2State ? LOW : HIGH);

  Serial.print("\nrelay2State ");

  // Sends the current state of relay 2 to the client
  server.send(200, "text/plain", relay2State ? "ON" : "OFF");

Immediately afterwards we encounter the setup function. Initially the serial port is activated:


then the DHT22 sensor is connected:

dht.setup(DHT22_PIN, DHTesp::DHT22); // Connect DHT sensor to GPIO D4 

Then follows the management part of the wifi connection:

WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
// it is a good practice to make sure your code sets wifi mode how you want it.

//WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wm;

// reset settings - wipe stored credentials for testing
// these are stored by the esp library
// wm.resetSettings();

// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result

bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

if(!res) {
    Serial.println("Failed to connect");
else {
    //if you get here you have connected to the WiFi    
    Serial.println("Connected...yeey :)");
    Serial.print("My IP is: ");

followed by the part that initializes the SPIFFS file system:

// Initialize SPIFFS
if (!SPIFFS.begin()) {
Serial.println("Error initializing SPIFFS");
} else {
Serial.println("SPIFFS initialized");

Immediately afterwards we find the part that routes the requests from the client (typically the browser on our PC or on our mobile phone) to the relevant functions (i.e. connect / to handleRoot, /getData to handleGetData, /relay1 to handleRelay1 and /relay2 to handleRelay2) and starting the server:

// Set up request handlers
server.on("/", HTTP_GET, handleRoot);
server.on("/getData", HTTP_GET, handleGetData);
server.on("/relay1", HTTP_GET, handleRelay1);
server.on("/relay2", HTTP_GET, handleRelay2);

// Start the server

The relay1Pin and relay2Pin pins are then initialized as OUTPUT, the outputs are put in the HIGH state and a message is printed:

  // Initialize the relay pins as output
  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);

  digitalWrite(relay1Pin, HIGH);
  digitalWrite(relay2Pin, HIGH);

  Serial.println("Server running");

Then follows the loop function:

void loop() {

  if (millis() > lastTimeRan + measureDelay)  {
    temperature = dht.getTemperature(); // reads temperature and puts the value in the global temperature variable
    humidity = dht.getHumidity(); // reads humidity and puts the value in the global humidity variable
    lastTimeRan = millis();

The client handler is always kept running and the temperature and humidity are acquired from the DHT22 every measureDelay seconds.

Let’s also take a look at the index.html file:

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Data Logger</title>
        table {
            font-family: Arial, sans-serif;
            border-collapse: collapse;
            width: 100%;

        th, td {
            border: 1px solid #dddddd;
            text-align: left;
            padding: 8px;

        th {
            background-color: #f2f2f2;

        #relay1, #relay2 {
            display: inline-block;
            padding: 10px;
            margin: 5px;
            cursor: pointer;
            border-radius: 5px;

        #relay1.on, #relay2.on {
            background-color: green;
            color: white;
        }, {
            background-color: red;
            color: white;
    <h1> ESP8266 Data Logger Web Interface</h1>
            <th>Temperature (°C)</th>
            <th>Humidity (%)</th>
        <tbody id="dataBody"></tbody>

    <div id="relay1" class="off" onclick="toggleRelay(1)">Relay 1</div>
    <div id="relay2" class="off" onclick="toggleRelay(2)">Relay 2</div>

        function getData() {
                .then(response => response.json())
                .then(data => {
                    // Create a new row
                    const newRow = document.createElement("tr");

                    // Add time, temperature, and humidity as cells
                    newRow.innerHTML = `
                        <td>${new Date().toLocaleTimeString()}</td>

                    // Clear previous data and append the new row to the table body
                    document.getElementById('dataBody').innerHTML = '';

        function toggleRelay(relayNumber) {
                .then(response => response.text())
                .then(state => {
                    const relayElement = document.getElementById(`relay${relayNumber}`);

                    // Toggle the class based on the relay state
                    relayElement.className = state === 'ON' ? 'on' : 'off';

        setInterval(getData, 5000);  // Update data every 5 seconds
        getData();  // Load initial data

The page style is initially defined, with an initial table that will display a row containing the current time, temperature and humidity (which are updated every 5 seconds). Then the style of the two control buttons of the two relays is defined. The body contains the getData and toggleRelay functions which respectively acquire the detected temperature and humidity values ​​from the sketch and manage the state of the buttons that control the relays.

How to transfer the index.html file to the SPIFFS file system

The operation is quite simple. It is necessary to open a new terminal on PlatformIO with the button indicated in the figure:

Button to open a new terminal
Button to open a new terminal

write the following command:

pio run -t uploadfs

and press the ENTER key. If all goes well, the index.html file will have been transferred to the SPIFFS file system.

NOTE: it is important that the index.html file is located, as already mentioned at the beginning, in a folder with the data name at the same level as the src folder.

Loading the sketch, however, follows the normal way.

How to connect the board to the Internet

After uploading the sketch to the card, open the Serial Monitor to see the messages coming from the device. First the board goes into Access Point mode and will provide us with an IP address that we will use shortly. This operation is used to connect the board to the Internet without having to enter the WiFi network parameters (SSID and password) in the code.

The board provides us with its IP address
The board provides us with its IP address

In this case the IP address is

At this point the ESP8266 is in Access Point mode (with AutoConnectAP SSID) and we need to connect our computer to the AutoConnectAP network. If we go to the networks menu of our computer, we should also see the AutoConnectAP network in the list of wireless networks.

List of available WiFi networks
List of available WiFi networks

Connect your computer to the AutoConnectAP network. Then go to your browser and enter the IP previously provided by the ESP8266 (which in this example is

You will see a screen like this:

The browser screen for choosing the network
The browser screen for choosing the network

Click the ConfigureWiFi button. It will show you the available networks:

List of available networks
List of available networks

Choose your network’s SSID:

Choose your network
Choose your network

Enter your network password and click the save button:

Enter your password
Enter your password

The board's response
The board’s response

The ESP8266 module keeps the access parameters stored even if you turn it off, it will remember them when restarting and will automatically reconnect without having to repeat this procedure. Only if you reset it by uncommenting this line

// wm.resetSettings();

will lose the connection parameters.

Please note: the device can only memorize one network. If you later connect it to another network, it will forget the settings of the previous network.

Let’s test the project with a browser

Once the ESP8266 has been connected to the WiFi network it will provide us with its IP address via the PlatformIO Serial Monitor, as visible in the following figure:

The board's IP
The board’s IP

In this case the board’s IP is (in your case it will most likely be different).

Copy the IP that the board indicated to you on the browser bar (on your PC or on your mobile phone) and press ENTER. The web page will appear, as you can see in the following video:


If you want to be informed about the release of new articles, subscribe to the newsletter. Before subscribing to the newsletter read the page Privacy Policy (UE)

If you want to unsubscribe from the newsletter, click on the link that you will find in the newsletter email.

Enter your name
Enter your email
0 0 votes
Article Rating
Inline Feedbacks
View all comments
Would love your thoughts, please comment.x
Scroll to Top