logo elektroda
logo elektroda
X
logo elektroda

Wemos D1 "Arduino" and DHT11 - a simple weather station with graphs on the website

p.kaczmarek2  17 4692 Cool? (+13)
📢 Listen (AI):

TL;DR

  • Wemos D1/ESP8266 weather station reads a DHT11 sensor, fetches NTP time, stores measurements, and serves temperature and humidity graphs over a local WiFi web page.
  • The design uses ESP8266WebServer for HTTP, NTPClient for network time, chart.js for plotting, and a circular buffer to keep recent samples in RAM.
  • The history buffer holds 24 * 6 samples, enough for a full day at 10-minute intervals.
  • The graph is built from label, temperature, and humidity arrays, iterating from the oldest sample and skipping empty entries where time equals 0.
  • RAM limits and code organization remain open issues, and the author suggests Ticker, WiFiManager, and checking NTP before the first measurement.
Generated by the language model.
Temperature and humidity chart displayed on the ESP8266 Weather Station webpage
My previous topic about creating charts on Arduino R4 received a greater response than I expected, so now, for a change, I am making a similar project but in a more complete form and on the ESP8266 platform in PlatformIO. Typically, the whole thing will be assembled from ready-made libraries, and I will describe the process in the form of a tutorial. First, I will run the DHT11 temperature and humidity sensor here, then I will show you how you can download the current time from the Internet. Then I will demonstrate how to collect measurements in memory in a circular buffer, and finally display them on a simple web page with a graph, all available within our WiFi.

First steps
Some time ago I published a topic about ArduinoOTA and ESP8266 in PlatformIO , I recommend you read it. Here I am assuming that we have OTA running and I will use what I developed earlier. As a reminder - here is my "hello world":
Code: C / C++
Log in, to see the code

In this topic, we try to assemble everything from ready-made elements, so we will also use a ready-made library for the DHT11 sensor. We just need to install it.
Open the Libraries tab:
PlatformIO interface with the libraries tab open in Visual Studio Code
We look for DHT. The DHT library also requires the Adafruit Unified Sensor Library to be added, so we add both libraries. First Unified Sensor:
Screenshot showing a search for DHT libraries in PlatformIO.
We add to the project:
PlatformIO interface with selected Adafruit Unified Sensor library
We will need to indicate to which project we are adding this library:
Project dependency addition dialog in PlatformIO
Added:
Adafruit Unified Sensor library installation in PlatformIO
This is how we add both libraries. A keen eye will notice that they have also been added to our platformio.ini:
PlatformIO library configuration with added Adafruit libraries
It is also worth noting that there is also an example of its use on the library`s website:
Screenshot of an example using the DHT_Unified_Sensor library in PlatformIO.
Let`s integrate it into our code. I perform the measurement when the diode status is switched:
Code: C / C++
Log in, to see the code

At the moment, I only send results via UART. Then we will make a website. Let`s first check if DHT works at all:
Terminal display showing WiFi connection data and temperature and humidity readings.

We add a website
Putting our measurements out into the world is really very simple. A class is used to create a server ESP8266WebServer . First, we include its header:
Code: C / C++
Log in, to see the code

Then we create its instance, the argument is the port on which the server will be created (HTTP is usually port 80):
Code: C / C++
Log in, to see the code

Then we create functions that handle the page data:
Code: C / C++
Log in, to see the code

In the main loop we serve clients:
Code: C / C++
Log in, to see the code

And finally, the function that handles the main page:
Code: C / C++
Log in, to see the code

Result:
Webpage displaying the text Hello Elektroda! in a browser.
It`s really very simple. Now we will display the temperature.


We display the temperature
To display the temperature on the page we need to do two things:
- these measurements need to be saved, for simplicity we will put them in global variables
- they must then be placed in the text of the page, I will use my favorite method to format the string, i.e. a C-style buffer and the sprintf function.
Global variables themselves:
Code: C / C++
Log in, to see the code

Writing to global variables:
Code: C / C++
Log in, to see the code

Modified page creation function:
Code: C / C++
Log in, to see the code

In this way, the temperature and humidity read from DHT will appear on our screen.

Network time (NTP)
NTP stands for Network Time Protocol, a way to obtain time from the network. We also have a ready-made one here, we will use the class NTPClient . We add the appropriate library as before:
Screenshot displaying the library search interface for NTPClient in PlatformIO.
PlatformIO interface with NTPClient library open in the code editor.
Of course, this library also has a ready-made example of use:
Code: C / C++
Log in, to see the code

We still need to somehow obtain time in the form of an inscription. There is a ready-made function for this - getFormattedTime . After integration with our website we get:
Code: C / C++
Log in, to see the code

Result:
Screenshot displaying weather data on a webpage
If we are ambitious, we can format the time ourselves as we wish. We have a function for this strftime , which works similarly to spritnf . We also format the time using special tags. Below is a slightly developed example:
Code: C / C++
Log in, to see the code



Better organization
However, our goal is to collect measurements. Before we implement it, I suggest that you better organize your measurement records. Let`s introduce a structure to represent the measurement. But what should such a structure contain? We will include:
- temperature (float)
- humidity (also float)
- time (here it is best to use the time_t type, which is in fact also a number, I do not recommend writing time as a string, it would be inefficient and inconvenient in further processing)
Code: C / C++
Log in, to see the code

You can then create an instance of this structure globally:
Code: C / C++
Log in, to see the code

and our program will work as before, the only important thing is to save the time at the moment of measurement and not create it at the moment of reading the page.

Measurement history - circular buffer - part 1
Now you need to figure out how to store the measurements. You`d like to use a regular table, but what about deleting old measurements? RAM is not infinite.
There is a simple way to do this - we will use it here circular buffer .
A circular buffer is an array-based data structure that allows you to store a certain number of data elements, and when the buffer is full, new data begins to overwrite the oldest. As the name suggests - the index of the element to which we write comes full circle here. Appropriate manipulation of the array index (along with the appropriate method of reading and writing) makes it become a circular buffer.
Let`s first decide on the size of the board - if we want to have a day of measurements, where measurements are taken every 10 minutes, then 24 * 6 will be enough.
Code: C / C++
Log in, to see the code

We also need the index of the last measurement (this index will "circle" around the array, it will loop):
Code: C / C++
Log in, to see the code

A function that adds a measurement (and loops through the index using the modulo operator):
Code: C / C++
Log in, to see the code

And the table clearing function:
Code: C / C++
Log in, to see the code

Of course, this also needs to be included in downloading measurements from DHT:
Code: C / C++
Log in, to see the code

and their display (no chart at the moment):
Code: C / C++
Log in, to see the code



Finally a chart
Now it`s time to create the chart. We will generate it similarly to the related topic: Drawing charts in HTML on Arduino R4 - statistics, measurements on a mini website .
I decided to use a library for this chart.js
On the W3schools website you can find examples of its use:
https://www.w3schools.com/js/js_graphics_chartjs.asp
Quoting the example from the website above - it all comes down to plugging our collected values into arrays:
Sample source code for a line chart in Chart.js
The above code will generate a chart like this:
Line chart with three lines in different colors.
Arrays are characterized by the fact that all elements except the first one are preceded by a comma, which we also have to handle, probably using some if.
We create three arrays:
- labels (markings, dates/times here)
- temperature values
- humidity values
Sample solution:
Code: C / C++
Log in, to see the code

The above solution ignores the circular buffer for now!
Additionally, I break the loop early to skip "empty" measurements.
Then we need to place the tables generated in this way in the rest of the code generating the chart, which we can take from examples of using this library:
Code: C / C++
Log in, to see the code

Here is the result, for the effect I heated the sensor with a powerful flashlight:
Graph showing temperature and humidity changes.
Webpage displaying a temperature and humidity chart.

Fixes and improvements
The code in the previous paragraph is not correct. We must respect the circular buffer and ignore empty measurements. The measurement is empty if the time is equal to 0, and we start the buffer iteration from the oldest entry, which we identify as:

(lastSample+1)%SAMPLES_COUNT

the % operator is modulo, the remainder when divided by an integer. This way we start with the oldest entry and end with lastSample.
Code: C / C++
Log in, to see the code

You can still show the result here, but in terms of appearance nothing will change (well, unless we wait longer, then without the correction to the circular buffer the graph could break):
Temperature and humidity chart on a webpage


Measurement frequency, cleaning
Now it is worth separating these 1000 ms of the temperature sampling period into a separate variable. Let`s say:
Code: C / C++
Log in, to see the code

In fact, this entire piece of code should be rewritten, maybe it could be possible to use, for example, a class Ticker , but maybe in the next part.

View full code:
Code: C / C++
Log in, to see the code

A screenshot of collecting samples over an extended period of time in a slightly modified version of the program:
Temperature and humidity graph over time



Summary
A very simple and pleasant design. Just right to start with ESP. After selecting the appropriate number of samples (we are limited by RAM memory) and sampling frequency, you can have nice graphs showing the temperature and humidity for the last few days.
Of course, this is just a small example and there is much room for improvement, e.g.:
- aesthetics (dividing the chart into two units, etc.)
- code (mentioned Ticker would come in handy)
- configurability (you could connect WiFiManager to avoid the need to hard-code our SSID and password)
- it would also be useful to check before the first measurement whether the time from NTP has already been downloaded, if not, skip the measurement
Perhaps I will deal with this in the next part, but for now I invite you to comment. Have you implemented this type or a similar project at ESP? Or maybe someone will be tempted to develop the seed I provided here and turn it into a full-fledged system?

About Author
p.kaczmarek2
p.kaczmarek2 wrote 14451 posts with rating 12433 , helped 650 times. Been with us since 2014 year.

Comments

bialy 11 Apr 2024 08:31

DHT11 used to be an indicator rather than a temperature sensor (it showed whether it was rising or falling ;D). How does it work better now? [Read more]

p.kaczmarek2 11 Apr 2024 08:43

Basically I can only agree. There are also slightly better sensors, for example BME280: https://obrazki.elektroda.pl/9254153600_1712817719_thumb.jpg A beginner can treat this as an exercise and convert... [Read more]

SylwekK 11 Apr 2024 09:37

and the SHT3x series measures temp&humi and is praised :) [Read more]

p.kaczmarek2 11 Apr 2024 12:27

I recognize SHT3X, I sometimes see it in Tuya products: https://www.elektroda.com/rtvforum/find.php?q=SHT3X+Tuya Interestingly, there are temperature sensors with an "alarm" function, which allows you... [Read more]

Nargo 11 Apr 2024 18:53

My budget replacement for BMP280 is BME280+AHT20 in one module. The disadvantages that I noticed after almost 6 months on 4 systems are the difference of one 1 C between AHT and BME and the need for changes... [Read more]

mariomario 11 Apr 2024 21:06

I have , because I was very interested in this project on ESP8266 -> Were there any tests carried out, how many measurements will enter the RAM memory of this ESP8266, let`s say the last 30 full... [Read more]

p.kaczmarek2 11 Apr 2024 21:26

@mariomario, you have information on the Internet how much RAM is on ESP8266, but the question is, are you sure you want to use RAM? So that it disappears after a power loss? Maybe it`s better to use some... [Read more]

bialy 11 Apr 2024 21:26

In the option as above, approximately 46kb of RAM will be used for 30 days. You can optimize the capacity because 16 bytes per measurement is a lot of data. [Read more]

p.kaczmarek2 11 Apr 2024 21:39

Every 15 minutes a reading gives us 4 readings per hour, or 96 per day. 30 days is 30*96 = 2880 readings Indeed, if this time_t is 64-bit, with a measurement structure of size 16 bytes we get 46kB, as... [Read more]

mariomario 11 Apr 2024 21:46

I don`t know about other forum members, but in my opinion a project like this would be nice, which after entering the web interface shows the current (current) temperature and humidity reading along with... [Read more]

p.kaczmarek2 12 Apr 2024 08:54

This means that you would also need to connect WiFiManager so that you do not have to enter network data. I can`t estimate it in my memory, but you should consider whether you want to send all 2880... [Read more]

mariomario 12 Apr 2024 11:56

This would definitely improve the experience of using such a thermometer :) (the only question is how much memory will be left for the reading data?) Even without the optimizations discussed above,... [Read more]

p.kaczmarek2 12 Apr 2024 12:14

I haven m not at the top in this language, but I rather thought that the measurements were sent to the browser as text, not as binary data. So, for example, this JavaScript array: let sampleArray =... [Read more]

ElektrodaBot 12 Apr 2024 12:15

Hello! I will be happy to show you how you can download a binary file from the Internet using the GET method and parse it in JavaScript to extract 16-bit integer and 32-bit integer values from it. I... [Read more]

p.kaczmarek2 12 Apr 2024 12:25

@mariomario, I think you s no point in packing bit by bit. I`m exaggerating a bit out of habit, because I have created/am making systems where I fight for every bit, etc. If you were to offer a binary... [Read more]

ElektrodaBot 12 Apr 2024 12:30

Welcome back! Parsing the complex data format from a object requires a special approach because we will be working at the bit level. In your case, we are dealing with a total of 40 bits of data, where... [Read more]

p.kaczmarek2 12 Apr 2024 12:41

I see that in JS it basically looks like what I would do in C. [Read more]

FAQ

TL;DR: This FAQ shows Wemos D1 mini beginners how to build a Wi‑Fi weather station that stores 144 samples per day, while one expert calls DHT11 a “trend indicator.” It solves the full path from OTA and NTP time to circular-buffer history and Chart.js graphs on a local ESP8266 web page. [#21040300]

Why it matters: The thread turns a simple ESP8266 demo into a practical blueprint for local monitoring, memory planning, and future upgrades.

Sensor / option What the thread says Best fit in this project
DHT11 Easy starter part, but treated more as a trend indicator than a precise sensor Learning, quick prototype
BME280 Explicitly called a slightly better sensor Better all-round upgrade
SHT3x Praised for temperature and humidity measurement Quality temp+RH alternative
BME280 + AHT20 module Reported as a budget replacement with two temperature sensors Low-cost experiment, redundancy
DHT22 / AM2301 Mentioned as an alternative; one user reported buzzing in an outdoor sensor Drop-in alternative with caveats

Key insight: The most valuable design choice is not the graph. It is storing readings in the right structure and reading the circular buffer in the correct order, from the oldest valid sample to the newest.

Quick Facts

  • The tutorial sizes history as 24*6, which equals 144 samples for one day at 10-minute intervals. [#21040300]
  • For 30 days at 15-minute intervals, the thread calculates 2880 readings total. [#21042000]
  • A Measurement with float temperature, float humidity, and time_t timestamp is estimated at 12 bytes or 16 bytes if time_t is 64-bit. [#21041976]
  • With a 16-byte record size, 2880 readings use about 46 kB RAM; cutting records to 8 bytes drops that to about 23 kB. [#21042000]
  • One flash-endurance example uses a W25Q80 with a minimum 100,000 erase cycles per 4 KB sector; the thread argues that wear-managed logging can stretch practical lifetime dramatically. [#21042317]

How do I build a simple weather station on a Wemos D1 mini (ESP8266) with a DHT11 sensor, OTA updates, and a web page in PlatformIO?

Build it by combining ready-made ESP8266 libraries in PlatformIO. 1. Connect Wi‑Fi, start ArduinoOTA, and initialize DHT_Unified on pin D3. 2. Read temperature and humidity, then store them in a Measurement record with a timestamp. 3. Start ESP8266WebServer on port 80 and serve an HTML page with current values or a graph. The thread’s working example also blinks D13 and samples every 1000 ms. [#21040300]

What is a circular buffer, and how does it help store temperature and humidity history on an ESP8266 with limited RAM?

A circular buffer stores a fixed number of readings and overwrites the oldest when full. "Circular buffer" is an array-based data structure that stores a limited number of elements, reuses the same memory, and wraps the write index with modulo arithmetic. On this ESP8266 project, lastSample++ followed by % SAMPLES_COUNT keeps RAM usage predictable. That matters because history can grow quickly: even 30 days at 15-minute intervals reaches 2880 readings. [#21040300]

How can I use ESP8266WebServer to show current DHT11 temperature, humidity, and NTP time on a local web page?

Use ESP8266WebServer server(80);, register server.on("/", HTTP_GET, handleRoot);, and call server.handleClient() in loop(). In handleRoot(), format HTML with sprintf, inserting temperature, humidity, and either timeClient.getFormattedTime() or a strftime string. The thread shows a page that returns HTTP 200 and displays all three values on one local page after Wi‑Fi connects. [#21040300]

What is NTPClient, and how do I use it on ESP8266 to fetch and format the current time from the Internet?

NTPClient is the library the project uses to fetch network time over UDP. "NTPClient" is a time-synchronization library that queries an NTP server, returns current network time, and exposes both epoch values and formatted strings for easy display. In the thread, it is created with WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org");, then started with timeClient.begin() and refreshed by timeClient.update(). [#21040300]

Why is the DHT11 often considered more of a trend indicator than an accurate temperature sensor, and what practical limits should I expect?

The thread treats DHT11 as good for direction, not precision. One participant says it used to be “an indicator rather than a temperature sensor,” meaning it shows whether values rise or fall more reliably than it gives exact readings. In practice, expect it to work for a beginner graphing project, but expect limited trust in absolute accuracy and be ready to upgrade if measurement quality matters. [#21040993]

Which sensor is better for an ESP8266 weather station: DHT11, BME280, SHT3x, AHT20, or DHT22?

BME280 or SHT3x are presented as better choices than DHT11 for a serious ESP8266 weather station. The author explicitly names BME280 as “slightly better,” and another participant says the SHT3x series is praised. A combined BME280+AHT20 module is described as a budget option, though one user saw a 1.0–1.1°C difference between its two temperature readings. DHT22 is mentioned, but one outdoor sensor reportedly started buzzing. [#21041790]

How do I draw temperature and humidity graphs on an ESP8266 web interface using Chart.js and data collected from a DHT sensor?

Generate three JavaScript arrays on the ESP8266: labels, temperatures, and humidities, then inject them into a Chart.js line chart. The thread builds HTML with a <canvas> element and loads Chart.js from cdn.jsdelivr.net. Each label is a formatted timestamp, while the two datasets plot temperature and humidity with different colors. This gives a graph directly in the browser, using only local ESP8266 data and one HTTP page. [#21040300]

Why does a Chart.js graph break when I read samples from a circular buffer in the wrong order, and how do I iterate from the oldest entry correctly?

The graph breaks because the buffer is not a simple linear history after wraparound. Once the array fills, the newest sample may sit in the middle, so reading from index 0 upward mixes old and new data. The thread fixes this by starting from the oldest entry, calculated as (lastSample+1)%SAMPLES_COUNT, then iterating SAMPLES_COUNT times and skipping records where timestamp == 0. That preserves chronological order. [#21040300]

How many ESP8266 RAM bytes would 30 days of temperature and humidity measurements every 15 minutes use, and how can I estimate it from the Measurement struct size?

For 30 days at 15-minute intervals, you need 2880 readings, and the thread estimates about 46 kB if each Measurement is 16 bytes. The math is simple: 30*96 = 2880, because 15-minute logging gives 4 readings per hour and 96 per day. Then multiply record count by struct size. If the struct shrinks to 8 bytes, the same history drops to about 23 kB. [#21042000]

What are the best ways to reduce measurement storage size on ESP8266: using smaller structs, packed values, lower timestamp resolution, or binary API transfers?

The best reductions come from shrinking the struct first, then lowering timestamp precision, then avoiding text transfer. The thread suggests packing temperature and humidity into about 3 bytes instead of two floats, and replacing full time_t with seconds, tens of seconds, or minutes counted from 2024-01-01. It also notes that a binary API can copy raw readings directly to the HTTP response, avoiding sprintf and ASCII expansion on the ESP8266. [#21041976]

How should I store long-term weather history on ESP8266 so data survives power loss: RAM, built-in flash, external EEPROM, or another flash chip?

Use flash or external nonvolatile memory if you need data after power loss. The thread warns that RAM disappears on outage, then suggests built-in flash, external EEPROM, or even another flash chip. It also argues that flash wear is manageable: with a W25Q80 rated for at least 100,000 erase cycles per 4 KB sector, careful wear-saving writes can make hourly or periodic logging practical for very long periods. [#21042317]

Why might sending 2880 readings from ESP8266 to the browser as JavaScript text arrays be larger than expected, and what alternatives are more efficient?

Text arrays are larger than many people expect because every number and timestamp becomes ASCII characters plus commas. The thread notes that a value like 19.5 already takes about 4 text bytes, while a Unix timestamp like 1712916348 takes 10 characters before separators. For 2880 readings, that overhead adds up quickly. More efficient options are a binary measurement API, summary views such as hourly averages, or separate detailed charts per day. [#21042575]

How can I create a binary measurement API on ESP8266 and parse the response in JavaScript with ArrayBuffer and DataView?

Return raw bytes from the ESP8266 instead of JavaScript text, then parse them in the browser with fetch(), arrayBuffer(), and DataView. The thread explicitly proposes copying readings from memory straight into the GET response to avoid sprintf work. It also shows JavaScript that reads binary data with getInt16, getInt32, and even bit-packed fields. That approach cuts formatting overhead and gives tighter control over bandwidth. [#21042590]

What role would WiFiManager play in an ESP8266 weather station, and how would it improve setup compared with hard-coded SSID and password?

WiFiManager would remove the need to hard-code MY_SSID and MY_PASS in the firmware. The thread recommends it for a more complete product, especially if other users will install the weather station. That improves first-time setup and makes the device easier to move between networks without rebuilding and reflashing the code. It also aligns with the thread’s goal of turning a tutorial into a fuller system. [#21042317]

How should I redesign the sampling loop on ESP8266 so measurement timing is cleaner than using delay(1) and a counter, for example with Ticker?

Replace the delay(1) plus incrementing counter with a timer-driven design. The author already separates timing into g_sampleIntervalMS = 1000 and says the loop should be rewritten more cleanly, suggesting Ticker as the next step. A cleaner design keeps ArduinoOTA.handle(), server.handleClient(), and timeClient.update() responsive while sampling on schedule. That avoids fragile timing based on loop speed and manual counter increments. [#21040300]
Generated by the language model.
%}