Веб-сервер ESP32 с DHT11/DHT22 – Температура и влажность с использованием Arduino IDE

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

Веб-сервер ESP32 с DHT11/DHT22 -- Температура и влажность с использованием Arduino IDE

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

В этом проекте вы научитесь:

  • Считывать температуру и влажность с датчиков DHT;

  • Создавать асинхронный веб-сервер с использованием библиотеки ESPAsyncWebServer;

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

Для более подробного объяснения работы с датчиками температуры и влажности DHT22 и DHT11 с ESP32 прочитайте наше полное руководство: ESP32 с датчиком температуры и влажности DHT11/DHT22 с использованием Arduino IDE

Асинхронный веб-сервер

Для создания веб-сервера мы будем использовать библиотеку ESPAsyncWebServer, которая предоставляет простой способ создания асинхронного веб-сервера. Создание асинхронного веб-сервера имеет ряд преимуществ, описанных на странице библиотеки в GitHub, таких как:

  • «Обработка нескольких подключений одновременно»;

  • «Когда вы отправляете ответ, вы сразу готовы обрабатывать другие подключения, пока сервер отправляет ответ в фоновом режиме»;

  • «Простой механизм обработки шаблонов»;

  • И многое другое.

Ознакомьтесь с документацией библиотеки на странице GitHub.

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

Компоненты для проекта ESP32 DHT11 DHT22 веб-сервера

Для выполнения этого руководства вам понадобятся следующие компоненты:

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Схема подключения

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

В данном случае мы подключаем вывод данных к GPIO 27, но вы можете подключить его к любому другому цифровому выводу. Эту схему подключения можно использовать как для датчика DHT11, так и для DHT22.

Схема подключения ESP32 с датчиком DHT11 DHT22 для Arduino IDE

(Эта схема использует модуль ESP32 DEVKIT V1 с 36 GPIO – если вы используете другую модель, проверьте распиновку вашей платы.)

Примечание: если вы используете модуль с датчиком DHT, он обычно поставляется только с тремя выводами. Выводы должны быть промаркированы, чтобы вы знали, как их подключать. Кроме того, многие такие модули уже имеют встроенный подтягивающий резистор, поэтому вам не нужно добавлять его в схему.

Установка библиотек

Для этого проекта необходимо установить несколько библиотек:

Вы можете установить эти библиотеки через менеджер библиотек Arduino IDE. Перейдите в Sketch > Include Library > Manage Libraries и найдите нужные библиотеки по названию.

Код

Мы будем программировать ESP32 с помощью Arduino IDE, поэтому убедитесь, что у вас установлено дополнение ESP32 перед началом работы:

Откройте Arduino IDE и скопируйте следующий код.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com
*********/

// Import required libraries
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <Adafruit_Sensor.h>
#include <DHT.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

#define DHTPIN 27     // Digital pin connected to the DHT sensor

// Uncomment the type of sensor in use:
//#define DHTTYPE    DHT11     // DHT 11
#define DHTTYPE    DHT22     // DHT 22 (AM2302)
//#define DHTTYPE    DHT21     // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String readDHTTemperature() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return "--";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

String readDHTHumidity() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("Failed to read from DHT sensor!");
    return "--";
  }
  else {
    Serial.println(h);
    return String(h);
  }
}

const char index_html[] PROGMEM = R"rawliteral(
<!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>ESP32 DHT Server</h2>
  <p>
    <i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
    <span class="dht-labels">Temperature</span>
    <span id="temperature">%TEMPERATURE%</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 id="humidity">%HUMIDITY%</span>
    <sup class="units">&percnt;</sup>
  </p>
</body>
<script>
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").textContent = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("humidity").textContent = this.responseText;
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 10000 ) ;
</script>
</html>)rawliteral";

// Replaces placeholder with DHT values
String processor(const String& var){
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return readDHTTemperature();
  }
  else if(var == "HUMIDITY"){
    return readDHTHumidity();
  }
  return String();
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  dht.begin();

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });
  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readDHTTemperature().c_str());
  });
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readDHTHumidity().c_str());
  });

  // Start server
  server.begin();
}

void loop(){

}

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

Вставьте учетные данные вашей сети в следующие переменные, и код заработает сразу.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Как работает код

В следующих параграфах мы объясним, как работает код. Продолжайте читать, если хотите узнать больше, или перейдите к разделу «Демонстрация», чтобы увидеть конечный результат.

Импорт библиотек

Сначала импортируем необходимые библиотеки. WiFi, ESPAsyncWebServer и ESPAsyncTCP нужны для создания веб-сервера. Adafruit_Sensor и DHT нужны для чтения данных с датчиков DHT11 или DHT22.

#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <ESPAsyncTCP.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

Настройка сетевых учетных данных

Вставьте учетные данные вашей сети в следующие переменные, чтобы ESP32 мог подключиться к вашей локальной сети.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Определение переменных

Определите GPIO, к которому подключен вывод данных DHT. В данном случае он подключен к GPIO 27.

#define DHTPIN 27  // Digital pin connected to the DHT sensor

Затем выберите тип используемого датчика DHT. В нашем примере мы используем DHT22. Если вы используете другой тип, просто раскомментируйте нужный датчик и закомментируйте все остальные.

#define DHTTYPE DHT22   // DHT 22 (AM2302)

Создайте экземпляр объекта DHT с типом и выводом, определенными ранее.

DHT dht(DHTPIN, DHTTYPE);

Создайте объект AsyncWebServer на порту 80.

AsyncWebServer server(80);

Функции чтения температуры и влажности

Мы создали две функции: одну для чтения температуры (readDHTTemperature()) и другую для чтения влажности (readDHTHumidity()).

String readDHTTemperature() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return "--";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

Получение показаний датчика выполняется просто с помощью методов readTemperature() и readHumidity() объекта dht.

float t = dht.readTemperature();
float h = dht.readHumidity();

Также есть условие, которое возвращает два тире (–), если датчик не смог получить показания.

if (isnan(t)) {
  Serial.println("Failed to read from DHT sensor!");
  return "--";
}

Показания возвращаются в виде строки (string). Для преобразования float в строку используйте функцию String().

return String(t);

По умолчанию мы считываем температуру в градусах Цельсия. Чтобы получить температуру в градусах Фаренгейта, закомментируйте строку с температурой в Цельсии и раскомментируйте строку с температурой в Фаренгейте, чтобы получилось следующее:

//float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float t = dht.readTemperature(true);

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

Переходим к веб-странице сервера.

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

Как видно на рисунке выше, веб-страница содержит один заголовок и два абзаца. Один абзац отображает температуру, а другой – влажность. Также есть две иконки для оформления страницы.

Давайте рассмотрим, как создается эта веб-страница.

Весь HTML-текст со стилями хранится в переменной index_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> добавляют заголовок на веб-страницу. В данном случае это текст «ESP32 DHT server», но вы можете добавить любой другой текст.

<h2>ESP32 DHT Server</h2>

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

<p>
  <i class="fas fa-thermometer-half" style="color:#059e8a;"</i>
  <span class="dht-labels">Temperature</span>
  <span id="temperature">%TEMPERATURE%</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 id="humidity">%HUMIDITY%</span>
  <sup class="units">&percnt;</sup>
</p>

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

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

Для выбора иконок перейдите на сайт Font Awesome Icons.

Сайт Font Awesome Icons

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

Поиск иконки термометра на сайте Font Awesome Icons

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

<i class="fas fa-thermometer-half">
HTML-код иконки термометра на сайте Font Awesome Icons

Для выбора цвета просто передайте параметр style с цветом в шестнадцатеричном формате, как показано ниже:

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

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

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

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

Текст TEMPERATURE между знаками % является заполнителем для значения температуры.

<span id="temperature">%TEMPERATURE%</span>

Это означает, что текст %TEMPERATURE% является своего рода переменной, которая будет заменена фактическим значением температуры с датчика DHT. Заполнители в HTML-тексте должны быть заключены между знаками %.

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

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

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

Мы используем тот же подход для абзаца влажности, но с другой иконкой и заполнителем %HUMIDITY%.

<p>
  <i class="fas fa-tint" style="color:#00add6;"></i>
  <span class="dht-labels">Humidity</span>
  <span id="humidity">%HUMIDITY%</span>
  <sup class="units">&percnt;</sup>
</p>

Автоматическое обновление

Наконец, в нашей веб-странице есть JavaScript-код, который автоматически обновляет температуру и влажность каждые 10 секунд.

Скрипты в HTML-тексте должны находиться между тегами <script></script>.

<script>
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").textContent = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("humidity").textContent = this.responseText;
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 10000 ) ;
</script>

Для обновления температуры в фоновом режиме используется функция setInterval(), которая выполняется каждые 10 секунд.

По сути, она отправляет запрос на URL /temperature для получения последнего значения температуры.

xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 10000 ) ;

Когда значение получено, обновляется HTML-элемент с идентификатором temperature.

if (this.readyState == 4 && this.status == 200) {
  document.getElementById("temperature").textContent = this.responseText;
}

Подводя итог, предыдущий раздел отвечает за асинхронное обновление температуры. Тот же процесс повторяется для показаний влажности.

Важно: поскольку датчик DHT довольно медленно получает показания, если вы планируете подключать несколько клиентов к ESP32 одновременно, рекомендуем увеличить интервал запросов или отключить автоматическое обновление.

Процессор (Processor)

Теперь нам нужно создать функцию processor(), которая будет заменять заполнители в нашем HTML-тексте фактическими значениями температуры и влажности.

String processor(const String& var){
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return readDHTTemperature();
  }
  else if(var == "HUMIDITY"){
    return readDHTHumidity();
  }
  return String();
}

При запросе веб-страницы мы проверяем, содержит ли HTML какие-либо заполнители. Если найден заполнитель %TEMPERATURE%, мы возвращаем температуру, вызывая ранее созданную функцию readDHTTemperature().

if(var == "TEMPERATURE"){
  return readDHTTemperature();
}

Если заполнитель – %HUMIDITY%, мы возвращаем значение влажности.

else if(var == "HUMIDITY"){
  return readDHTHumidity();
}

setup()

В функции setup() инициализируем Serial Monitor для отладки.

Serial.begin(115200);

Инициализируем датчик DHT.

dht.begin();

Подключаемся к локальной сети и выводим IP-адрес ESP32.

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

Наконец, добавляем следующие строки кода для обработки запросов веб-сервера.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTTemperature().c_str());
});
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTHumidity().c_str());
});

При запросе корневого URL мы отправляем HTML-текст, хранящийся в переменной index_html. Также необходимо передать функцию processor, которая заменит все заполнители правильными значениями.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

Нам нужно добавить два дополнительных обработчика для обновления показаний температуры и влажности. При получении запроса на URL /temperature мы просто отправляем обновленное значение температуры. Это обычный текст, и он должен быть отправлен как char, поэтому мы используем метод c_str().

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTTemperature().c_str());
});

Тот же процесс повторяется для влажности.

server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTHumidity().c_str());
});

Наконец, мы можем запустить сервер.

server.begin();

Поскольку это асинхронный веб-сервер, нам не нужно писать ничего в функции loop().

void loop(){

}

Вот, собственно, и все о том, как работает код.

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

Теперь загрузите код на ESP32. Убедитесь, что выбрана правильная плата и COM-порт.

После загрузки откройте Serial Monitor со скоростью 115200 бод. Нажмите кнопку сброса ESP32. IP-адрес ESP32 должен появиться в мониторе порта.

Загрузка кода и отображение IP-адреса ESP32 в Serial Monitor

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

Откройте браузер и введите IP-адрес ESP32. Ваш веб-сервер должен отобразить последние показания датчика.

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

Демонстрация работы асинхронного веб-сервера ESP32 с DHT11/DHT22

Устранение неполадок

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

Заключение

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

Если вам понравился этот проект, вам также могут быть интересны:

Это руководство является предварительным обзором курса «Изучите ESP32 с Arduino IDE». Если вам понравился этот проект, обязательно ознакомьтесь со страницей курса ESP32, где мы рассматриваем эту и многие другие темы, связанные с ESP32.


Источник: ESP32 DHT11/DHT22 Web Server – Temperature and Humidity using Arduino IDE