MicroPython: веб-сервер ESP32/ESP8266 с DHT11/DHT22 (метеостанция)

В этом руководстве вы узнаете, как создать простой веб-сервер на ESP32 или ESP8266 для отображения показаний датчика температуры и влажности DHT11 или DHT22.

MicroPython ESP32 ESP8266 с DHT11 DHT22 веб-сервер температуры и влажности

Для ознакомления с датчиками температуры и влажности DHT11/DHT22 в MicroPython прочитайте следующее руководство:

Предварительные требования — прошивка MicroPython

Для выполнения этого руководства вам необходима прошивка MicroPython, установленная на плату ESP32 или ESP8266. Также вам нужна IDE для написания и загрузки кода на плату. Мы предлагаем использовать Thonny IDE или uPyCraft IDE:

Необходимые компоненты

Для выполнения этого руководства вам нужно подключить датчик DHT11 или DHT22 к ESP32 или ESP8266. Необходимо использовать подтягивающий резистор 10 кОм.

Вот список необходимых компонентов:

Схема подключения: ESP32 с DHT11/DHT22

Подключите датчик DHT22 или DHT11 к плате разработки ESP32, как показано на следующей схеме.

Схема подключения MicroPython ESP32 с DHT11 DHT22 веб-сервер температуры и влажности

В данном примере мы подключаем вывод данных DHT к GPIO 14. Однако вы можете использовать любой другой подходящий цифровой вывод.

Узнайте, как использовать GPIO ESP32, из нашего руководства: Справочник по распиновке ESP32: какие выводы GPIO следует использовать?

Схема подключения: ESP8266 с DHT11/DHT22

Следуйте приведённой ниже схеме, если вы используете плату ESP8266.

Схема подключения MicroPython ESP8266 с DHT11 DHT22 веб-сервер температуры и влажности

Код

Для этого проекта вам понадобятся файл boot.py и файл main.py. Файл boot.py содержит код, который выполняется только один раз при загрузке. Это включает импорт библиотек, учётные данные сети, инициализацию выводов, подключение к сети и другие настройки.

Файл main.py содержит код, который запускает веб-сервер для обслуживания файлов и выполнения задач на основе запросов, полученных от клиента.

boot.py

Создайте новый файл в вашей MicroPython IDE. Скопируйте следующий код в файл boot.py.

# Полная информация о проекте на 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))

Просмотреть исходный код

Мы подробно рассматривали код веб-сервера в предыдущем руководстве. Поэтому здесь мы рассмотрим только соответствующие части для данного примера.

Импортируем необходимые библиотеки для создания веб-сервера.

try:
  import usocket as socket
except:
  import socket

import network

Импортируем класс Pin из модуля machine и модуль dht для чтения данных с датчика DHT.

from machine import Pin
import dht

Также необходимо инициализировать датчик, создав экземпляр dht на GPIO 14 следующим образом:

sensor = dht.DHT22(Pin(14))

Если вы используете датчик DHT11, раскомментируйте следующую строку и закомментируйте предыдущую:

sensor = dht.DHT11(Pin(14))

Не забудьте указать свои сетевые учётные данные в следующих строках:

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

main.py

Скопируйте следующий код в файл main.py.

# Полная информация о проекте на 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()

Просмотреть исходный код

Чтение датчика DHT

Сначала создаём функцию read_sensor(), которая считывает температуру и влажность. Вы можете использовать эту функцию в любых других проектах, где нужно получать показания с датчиков DHT.

def read_sensor():

Функция начинается с создания двух глобальных переменных, чтобы мы могли использовать их во всех частях скрипта (они инициализируются значением 0).

global temp, hum
temp = hum = 0

Переменная temp содержит температуру, считанную с датчика, а hum — влажность.

Далее используем конструкции try и except. Внутри блока try мы пытаемся получить значения температуры и влажности.

Примечание

Конструкция try и except позволяет продолжить выполнение программы при возникновении исключения. Например, при возникновении ошибки выполнение кода в блоке try останавливается и передаётся в блок except. В нашем примере обработка исключений особенно полезна для предотвращения сбоя веб-сервера, когда мы не можем прочитать данные с датчика.

Мы выполняем измерение датчика с помощью метода measure() объекта sensor.

try:
 sensor.measure()

Затем считываем температуру с помощью sensor.temperature() и влажность с помощью sensor.humidity(). Сохраняем эти показания в переменных temp и hum.

temp = sensor.temperature()
hum = sensor.humidity()

Корректные показания температуры и влажности должны иметь тип float (если вы используете датчик DHT22) или тип int (если используете DHT11). Поэтому мы проверяем, корректны ли показания, используя функцию isinstance() перед продолжением.

if (isinstance(temp, float) and isinstance(hum, float)) or (isinstance(temp, int) and isinstance(hum,int)):

Примечание

Функция isinstance() принимает в качестве аргументов переменную и тип данных: isinstance(variable, data type). Она возвращает True, если переменная соответствует указанному типу данных, и False — если нет.

Если показания корректны, подготавливаем сообщение для вывода в Shell, которое содержит значения температуры и влажности:

msg = (b'{0:3.1f},{1:3.1f}'.format(temp, hum))

Если вы хотите отображать температуру в градусах Фаренгейта, раскомментируйте следующую строку:

#temp = temp * (9/5) + 32.0

Округляем показание влажности до двух десятичных знаков. Это значение будет отображено на странице веб-сервера.

hum = round(hum, 2)

Наконец, возвращаем сообщение с температурой и влажностью:

return(msg)

Если показания датчика некорректны (не типа float), возвращаем сообщение Invalid sensor readings..

else:
    return('Invalid sensor readings.')

Если не удаётся прочитать данные с датчика (например, если он отключился), возвращаем сообщение об ошибке.

except OSError as e:
  return('Failed to read sensor.')

Веб-страница

Функция web_page() возвращает HTML-страницу. Разберём каждую строку HTML и посмотрим, что она делает.

Следующий тег <meta> делает вашу веб-страницу адаптивной для любого браузера.

<meta name="viewport" content="width=device-width, initial-scale=1">

Тег <link> необходим для загрузки иконок, используемых на веб-странице, с сайта fontawesome.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
Стили

Между тегами <style></style> мы добавляем CSS для стилизации веб-страницы.

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

По сути, мы настраиваем HTML-страницу для отображения текста шрифтом Arial, блочным элементом без отступов и с выравниванием по центру.

html {
  font-family: Arial;
  display: inline-block;
  margin: 0px auto;
  text-align: center;
 }

Мы задаём размер шрифта для заголовка (h2), абзаца (p) и единиц измерения (.units) показаний.

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;
}

Все предыдущие теги должны располагаться между тегами <head> и </head>. Эти теги используются для включения контента, который не отображается напрямую пользователю, например теги <meta>, <link> и стили.

Тело HTML

Внутри тегов <body></body> мы добавляем содержимое веб-страницы.

Теги <h2></h2> добавляют заголовок на веб-страницу. В данном случае текст «ESP DHT server», но вы можете добавить любой другой текст.

<h2>ESP DHT Server</h2>

Далее идут два абзаца. Один для отображения температуры, другой — для влажности. Абзацы ограничены тегами <p> и </p>. Абзац для температуры выглядит следующим образом:

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

Теги <i> отображают иконки fontawesome.

Как отображать иконки

Чтобы выбрать иконки, перейдите на сайт Font Awesome Icons.

Страница иконок Font Awesome для ESP32/ESP8266 веб-сервера

Найдите нужную иконку. Например, «thermometer».

Иконка термометра Font Awesome для ESP32/ESP8266 веб-сервера

Нажмите на нужную иконку. Затем просто скопируйте предоставленный HTML-текст.

<i class="fas fa-thermometer-half">
HTML-код иконки Font Awesome для ESP32/ESP8266 веб-сервера

Чтобы выбрать цвет, нужно передать параметр style с цветом в шестнадцатеричном формате, как показано ниже:

<i class="fas fa-tint" style="color:#00add6;"></i>

Продолжаем разбор HTML-текста…

Следующая строка выводит слово «Temperature» на веб-страницу.

<span class="dht-labels">Temperature</span>

После этого мы добавляем фактическое значение температуры на HTML-страницу, конкатенируя переменную temp, как показано ниже.

<span>"""+str(temp)+"""</span>

Наконец, добавляем символ градуса.

<sup class="units">&deg;C</sup>

Теги <sup></sup> делают текст надстрочным.

Мы используем тот же подход для абзаца влажности.

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

Создание веб-сервера

После этого выполняем стандартные процедуры для создания сокетного сервера.

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

Для подробного объяснения этой процедуры обратитесь к этому руководству.

В цикле while мы вызываем функцию read_sensor() для вывода показаний датчика и обновления глобальных переменных temp и hum.

sensor_readings = read_sensor()
print(sensor_readings)

Таким образом, функция web_page() генерирует HTML-текст с последними показаниями датчика.

response = web_page()

Загрузка кода

После копирования кода и внесения необходимых изменений вы можете загрузить код на ESP32 или ESP8266.

Сначала загрузите файл boot.py, а затем файл main.py на ваш ESP32 или ESP8266.

Если вы используете Thonny IDE, просто перейдите в Device > Upload current script as boot script или Upload current script as main script. После загрузки кода нажмите кнопку RESET на плате ESP.

IP-адрес ESP32 или ESP8266 должен отобразиться в оболочке MicroPython Shell.

IP-адрес веб-сервера ESP32/ESP8266 DHT11 DHT22 в MicroPython

Если вы используете uPyCraft IDE и у вас возникли проблемы с загрузкой кода, перейдите к следующему руководству и прокрутите вниз до раздела «Running Your First Script»: Начало работы с MicroPython на ESP32 и ESP8266.

Возникли проблемы с загрузкой кода в Thonny IDE? Перейдите к этому руководству и прокрутите до раздела Troubleshooting Tips for Thonny IDE.

Демонстрация

Откройте браузер и введите IP-адрес ESP. Вы должны увидеть веб-страницу с последними показаниями датчика:

Демонстрация веб-сервера ESP32/ESP8266 DHT11 DHT22 MicroPython

Одновременно вы можете видеть в оболочке MicroPython Shell, что происходит в фоновом режиме.

Фоновый вывод веб-сервера ESP32/ESP8266 DHT11 DHT22 MicroPython

Для получения последних показаний датчика просто обновите веб-страницу.

Если ваш датчик DHT не может получить показания, прочитайте наше руководство по устранению неполадок DHT, которое поможет вам решить проблему.

Заключение

Для создания веб-сервера, отображающего показания температуры и влажности, достаточно конкатенировать переменные, содержащие показания датчика, с HTML-текстом. Убедитесь, что вы получаете показания датчика перед отображением веб-страницы, чтобы всегда иметь актуальные значения температуры и влажности.

Вам также могут быть полезны следующие руководства: