In this tutorial, you’re going to learn how to create a simple web server with the ESP32 or ESP8266 to display readings from the DHT11 or DHT22 temperature and humidity sensors.
For an introduction to the DHT11/DHT22 temperature and humidity sensors with MicroPython, read the following guide:
Prerequisites – Flashing MicroPython
To follow this tutorial you need MicroPython firmware installed in your ESP32 or ESP8266 boards. You also need an IDE to write and upload the code to your board. We suggest using Thonny IDE or uPyCraft IDE:
- Thonny IDE:
- uPyCraft IDE:
- Install uPyCraft IDE (Windows, Mac OS X, Linux)
- Flash/Upload MicroPython Firmware to ESP32 and ESP8266
Parts Required
To follow this tutorial you need to wire the DHT11 or DHT22 sensor to the ESP32 or ESP8266. You need to use a 10k Ohm pull-up resistor.
Here’s a list of parts you need to build the circuit:
- ESP32 or ESP8266 (read ESP32 vs ESP8266)
- DHT11 or DHT22 temperature and humidity sensor
- 10k Ohm resistor
- Breadboard
- Jumper wires
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic: ESP32 with DHT11/DHT22
Wire the DHT22 or DHT11 sensor to the ESP32 development board as shown in the following schematic diagram.
In this example, we’re connecting the DHT data pin to GPIO 14. However, you can use any other suitable digital pin.
Learn how to use the ESP32 GPIOs with our guide: ESP32 Pinout Reference: Which GPIO pins should you use?
Schematic: ESP8266 with DHT11/DHT22
Follow the next tutorial if you’re using an ESP8266 board.
Code
For this project you’ll need a boot.py file and amain.py file. The boot.py file contains the code that only needs to run once on boot. This includes importing libraries, network credentials, instantiating pins, connecting to your network, and other configurations.
The main.py file contains the code that runs the web server to serve files and perform tasks based on the requests received by the client.
boot.py
Create a new file in your MicroPython IDE. Copy the following code to your boot.py file.
# Complete project details at https://RandomNerdTutorials.com
try:
import usocket as socket
except:
import socket
import network
from machine import Pin
import dht
import esp
esp.osdebug(None)
import gc
gc.collect()
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)
while station.isconnected() == False:
pass
print('Connection successful')
print(station.ifconfig())
sensor = dht.DHT22(Pin(14))
#sensor = dht.DHT11(Pin(14))
We’ve covered in great detail the web server code in a previous tutorial. So, we’ll just take a look at the relevant parts for this example.
Import the necessary libraries to create a web server.
try:
import usocket as socket
except:
import socket
import network
Import the Pin class from the machine module and the dht module to read from the DHT sensor.
from machine import Pin
import dht
You also need to initialize the sensor by creating a dht instance on GPIO 14 as follows:
sensor = dht.DHT22(Pin(14))
If you’re using a DHT11 sensor, uncomment the next line, and comment the previous one:
sensor = dht.DHT11(Pin(14))
Don’t forget to add your network credentials on the following lines:
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
main.py
Copy the following code to your main.py file.
# Complete project details at https://RandomNerdTutorials.com
def read_sensor():
global temp, hum
temp = hum = 0
try:
sensor.measure()
temp = sensor.temperature()
hum = sensor.humidity()
if (isinstance(temp, float) and isinstance(hum, float)) or (isinstance(temp, int) and isinstance(hum, int)):
msg = (b'{0:3.1f},{1:3.1f}'.format(temp, hum))
# uncomment for Fahrenheit
#temp = temp * (9/5) + 32.0
hum = round(hum, 2)
return(msg)
else:
return('Invalid sensor readings.')
except OSError as e:
return('Failed to read sensor.')
def web_page():
html = """<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 1.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
</style>
</head>
<body>
<h2>ESP DHT Server</h2>
<p>
<i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
<span class="dht-labels">Temperature</span>
<span>"""+str(temp)+"""</span>
<sup class="units">°C</sup>
</p>
<p>
<i class="fas fa-tint" style="color:#00add6;"></i>
<span class="dht-labels">Humidity</span>
<span>"""+str(hum)+"""</span>
<sup class="units">%</sup>
</p>
</body>
</html>"""
return html
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
conn, addr = s.accept()
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
print('Content = %s' % str(request))
sensor_readings = read_sensor()
print(sensor_readings)
response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
conn.close()
Reading the DHT sensor
First, create a function called read_sensor() that reads temperature and humidity. You can use that function in any other projects in which you need to take sensor readings from DHT sensors.
def read_sensor():
The function starts by creating two global variables, so we can use them in all parts of the script (they are initilazed as 0).
global temp, hum
temp = hum = 0
The temp variable holds the temperature read from the sensor and the hum holds the humidity read from the sensor.
Next, use try and except statements. Inside the try statement we try to get the temperature and humidity values.
Note: try and except allows us to continue the execution of the program when an exception happens. For example, when an error occurs, a try block code execution is stopped and transferred to the except block. In our example, the exception is especially useful to prevent the web server from crashing when we are not able to read from the sensor.
We measure the sensor by using the measure() method on the sensor object.
try:
sensor.measure()
Then, read the temperature with sensor.temperature() and the humidity with sensor.humidity(). Save those readings on the temp and hum variables.
temp = sensor.temperature()
hum = sensor.humidity()
Valid temperature and humidity readings should be of type float (if you’re using a DHT22 sensor) or type int (if you’re using a DHT11) sensor. So, we check if we have valid readings by using the isinstance() function before proceeding.
if (isinstance(temp, float) and isinstance(hum, float)) or (isinstance(temp, int) and isinstance(hum,int)):
Note: the isinstance() function accepts as arguments the variable and the data type: isinstance(variable, data type). It returns True if the variable corresponds to the inserted data type and False if it doesn’t.
If the readings are valid, prepare a message to be printed on the Shell that includes the temperature and humidity readings:
msg = (b'{0:3.1f},{1:3.1f}'.format(temp, hum))
If you want to display temperature in Fahrenheit, uncomment the following line:
#temp = temp * (9/5) + 32.0
Round the humidity reading to two decimal points. This will be printed later on the web server page.
hum = round(hum, 2)
Finally, return the message with the temperature and humidity:
return(msg)
In case we don’t get valid sensor readings (not float type), we return Invalid sensor readings. message.
else:
return('Invalid sensor readings.')
In case we’re not able to read from the sensor (for example, in case it disconnects), return an error message.
except OSError as e:
return('Failed to read sensor.')
Web Page
The web_page() function returns the HTML page. We’ll go through each line of the HTML and see what it does.
The following <meta> tag makes your web page responsive in any browser.
<meta name="viewport" content="width=device-width, initial-scale=1">
The <link> tag is needed to load the icons used in the webpage from the fontawesome website.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
Styles
Between the <style></style> tags, we add some CSS to style the web page.
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 1.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
</style>
Basically, we’re setting the HTML page to display the text with Arial font in block without margin, and aligned at the center.
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
We set the font size for the heading (h2), paragraph (p) and the units(.units) of the readings.
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 1.2rem; }
The labels for the readings are styled as shown below:
dht-labels {
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
All of the previous tags should go between the <head> and </head> tags. These tags are used to include content that is not directly visible to the user, like the <meta> , the <link> tags, and the styles.
HTML Body
Inside the <body></body> tags is where we add the web page content.
The <h2></h2> tags add a heading to the web page. In this case, the “ESP DHT server” text, but you can add any other text.
<h2>ESP DHT Server</h2>
Then, there are two paragraphs. One to display the temperature and the other to display the humidity. The paragraphs are delimited by the <p> and </p> tags. The paragraph for the temperature is the following:
<p>
<i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
<span class="dht-labels">Temperature</span>
<span>"""+str(temp)+"""</span>
<sup class="units">°C</sup>
</p>
And the paragraph for the humidity is on the following snipet:
<p>
<i class="fas fa-tint" style="color:#00add6;"></i>
<span class="dht-labels">Humidity</span>
<span>"""+str(hum)+"""</span>
<sup class="units">%</sup>
</p>
The <i> tags display the fontawesome icons.
How to display icons
To chose the icons, go to the Font Awesome Icons website.
Search the icon you’re looking for. For example, “thermometer”.
Click the desired icon. Then, you just need to copy the HTML text provided.
<i class="fas fa-thermometer-half">
To chose the color, you just need to pass the style parameter with the color in hexadecimal, as follows:
<i class="fas fa-tint" style="color:#00add6;"></i>
Proceeding with the HTML text…
The next line writes the word “Temperature” into the web page.
<span class="dht-labels">Temperature</span>
After that, we add the actual temperature value to the HTML page by concatenating the temp variable as shown below.
<span>"""+str(temp)+"""</span>
Finally, we add the degree symbol.
<sup class="units">°C</sup>
The <sup></sup> tags make the text superscript.
We use the same approach for the humidity paragraph.
<p>
<i class="fas fa-tint" style="color:#00add6;"></i>
<span class="dht-labels">Humidity</span>
<span>"""+str(hum)+"""</span>
<sup class="units">%</sup>
</p>
Creating the web server
After that, make the usual procedures to create a socket server.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
conn, addr = s.accept()
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
print('Content = %s' % str(request))
sensor_readings = read_sensor()
print(sensor_readings)
response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
conn.close()
For an in-depth explanation of this procedure, refer to this tutorial.
In the while loop, when we call the read_sensor() function to print the sensor readings and to update the global variables temp and hum.
sensor_readings = read_sensor()
print(sensor_readings)
So, the web_page() function generates HTML text with the latest sensor readings.
response = web_page()
Uploading the Code
After copying the code and making any necessary changes, you can upload the code to your ESP32 or ESP8266.
First, upload the boot.py, and then the main.py file to your ESP32 or ESP8266.
If you’re using Thonny IDE, you just need to go to Device > Upload current script as boot script or Upload current script as main script. After uploading the code, press the ESP on-board RESET button.
The ESP32 or ESP8266 IP address should be printed on the MicroPython Shell.
If you’re using uPyCraft IDE and you’re having trouble uploading the code, go to the following tutorial and scroll down to the “Running Your First Script” section: Getting Started with MicroPython on ESP32 and ESP8266.
Trouble uploading code with Thonny IDE? Go to this tutorial and scroll all the way down to the Troubleshooting Tips for Thonny IDE section.
Demonstration
Open your browser and type the ESP IP address. You should access the web page with the latest sensor readings:
At the same time, you can see on the MicroPython Shell, what’s going on in the background.
To get the latest sensor readings, you just need to refresh the web page.
Troubleshooting
If your DHT sensor fails to get the readings, read our DHT Troubleshooting Guide to help you fix the issue.
Wrapping Up
To create a web server to display the temperature and humidity readings, you just need to concatenate the variables that hold the sensor readings with the HTML text. Make sure you get the sensor readings before displaying the web page, so that you always have the updated temperature and humidity.
We hope you’ve found this tutorial useful. If you want to learn more about MicroPython with the ESP32 and ESP8266, take a look at our eBook:
You may also like the following tutorials:
- ESP32/ESP8266 GPIOs Explained with MicroPython
- ESP32/ESP8266 Analog Readings with MicroPython
- ESP32/ESP8266 PWM with MicroPython – Dim LED
- ESP32/ESP8266 MicroPython Web Server – Control Outputs
Thanks for reading.
Could you compare the pros and cons of IDEs — micropython versus Arduino
Thanks for the suggestion Curt! We definitely plant to write a post about that subject.
Hello, how many meters of reach has the ESP32 or the ESP8266 with free access or a wall in the middle? THANK YOU
Well, I was doing a turorial by someone else and he was using a dht22. I only had a dht11 and kept getting invalid reading. After searching, came here to find dht22 data is float and dht11 is int. Changed the type and now my setup is working. Thank you for letting us know the difference between the two.
Joe
Hi Joe.
Thanks for sharing that.
Regards,
Sara 😀
If i put the humidity above the temperature the formatting is rather strange, anybody else having the same issue?
Thanks.
Hi.
Use & # 37; (without spaces) instead of the % sign.
Search for its html entity : freeformatter.com/html-entities.html
Regards,
Sara
Hi Sara,
I’m steadily working through the examples at your website, and they are proving very useful.
Thanks for all the help!
I’m wondering, are you planning on an article on using ESP8266/ESP32 & micropython with the popular Blynk app (which is widely used in Arduino-world).
there are some Blynk / micropython libraries at github and while I have managed to get some connectivity, it is very unstable and unreliable.
Would be great to see a project here.
Thanks & keep up the good work
Philip
This might be ultra basic, but..
I have everything working, and now I want to power the chip just with a power supply and have it run the code without being connected to my computer. How do I power it and tell it to boot up and run?
Hi.
If you reset the board it will boot up automatically. You can restart the board by pressing the RST button.
Regards,
Sara
thank you for step-by-step tutorial which runs on my esp8266 with esp8266-20210203-unstable-v1.14.bin firmware. However, main.py exists frequently with the following message. I have searched via google, but it seems that it is hard to solve this. If so, can we use micropython in a stable product? Thanks
Got a connection from (‘192.168.2.4’, 44522)
Content = b’GET / HTTP/1.1\r\nHost: 192.168.2.12\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\r\n\r\n’
b’27.0,84.0′
Traceback (most recent call last):
File “main.py”, line 78, in
OSError: [Errno 104] ECONNRESET
“`
Hi Sara, thanks for the tutorial! I followed it using a NodMCU with ESP8266 and DHT22, and works as described. However, after a while I cannot access the webserver because I suppose that NodeMCU goes in sleep mode. How can I keep NodeMCU awake without the need of pressing RST button to wake it up? Thanks a lot!
“In this program, the temperature and humidity values on the display remain static and do not update dynamically. To fix this issue, I recommend using the AJAX technique. This technique allows the browser to continuously retrieve new temperature and humidity data from the server without refreshing the entire page