MicroPython: ESP32/ESP8266 with DHT11/DHT22 Web Server (Weather Station)

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.

MicroPython ESP32 ESP8266 with DHT11 DHT22 Web Server Temperature and humidity

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:

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:

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.

MicroPython ESP32 with DHT11 DHT22 Web Server Temperature and humidity Circuit Schematic wiring

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.

MicroPython ESP8266 with DHT11 DHT22 Web Server Temperature and humidity Circuit Schematic wiring

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))

View raw code

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">&deg;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()

View raw code

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.

ESP32/ESP8266 Web Server HTML page icons

Search the icon you’re looking for. For example, “thermometer”.

ESP32/ESP8266 Web Server HTML page icons

Click the desired icon. Then, you just need to copy the HTML text provided.

<i class="fas fa-thermometer-half">
ESP32/ESP8266 Web Server HTML page icons

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.

ESP32/ESP8266 DHT11 DHT22 Web Server IP Address MicroPython

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:

ESP32/ESP8266 DHT11 DHT22 Web Server MicroPython Demonstration

At the same time, you can see on the MicroPython Shell, what’s going on in the background.

ESP32/ESP8266 DHT11 DHT22 Web Server MicroPython Demonstration

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:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

13 thoughts on “MicroPython: ESP32/ESP8266 with DHT11/DHT22 Web Server (Weather Station)”

  1. 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

    Reply
  2. If i put the humidity above the temperature the formatting is rather strange, anybody else having the same issue?

    Thanks.

    Reply
    • Hi.
      Use & # 37; (without spaces) instead of the % sign.
      Search for its html entity : freeformatter.com/html-entities.html
      Regards,
      Sara

      Reply
  3. 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

    Reply
  4. 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?

    Reply
  5. 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
    “`

    Reply
  6. 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!

    Reply
  7. “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

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.