Веб-сервер ESP32 с DHT11/DHT22 – Температура и влажность с использованием Arduino IDE
В этом проекте вы узнаете, как создать асинхронный веб-сервер на ESP32 с датчиком DHT11 или DHT22, который отображает температуру и влажность с использованием Arduino IDE.
Веб-сервер, который мы создадим, автоматически обновляет показания без необходимости перезагружать веб-страницу.
В этом проекте вы научитесь:
Считывать температуру и влажность с датчиков DHT;
Создавать асинхронный веб-сервер с использованием библиотеки ESPAsyncWebServer;
Обновлять показания датчиков автоматически без необходимости перезагружать веб-страницу.
Для более подробного объяснения работы с датчиками температуры и влажности DHT22 и DHT11 с ESP32 прочитайте наше полное руководство: ESP32 с датчиком температуры и влажности DHT11/DHT22 с использованием Arduino IDE
Асинхронный веб-сервер
Для создания веб-сервера мы будем использовать библиотеку ESPAsyncWebServer, которая предоставляет простой способ создания асинхронного веб-сервера. Создание асинхронного веб-сервера имеет ряд преимуществ, описанных на странице библиотеки в GitHub, таких как:
«Обработка нескольких подключений одновременно»;
«Когда вы отправляете ответ, вы сразу готовы обрабатывать другие подключения, пока сервер отправляет ответ в фоновом режиме»;
«Простой механизм обработки шаблонов»;
И многое другое.
Ознакомьтесь с документацией библиотеки на странице GitHub.
Необходимые компоненты
Для выполнения этого руководства вам понадобятся следующие компоненты:
Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!
Схема подключения
Перед тем как приступить к веб-серверу, необходимо подключить датчик DHT11 или DHT22 к ESP32, как показано на следующей схеме.
В данном случае мы подключаем вывод данных к GPIO 27, но вы можете подключить его к любому другому цифровому выводу. Эту схему подключения можно использовать как для датчика DHT11, так и для DHT22.
(Эта схема использует модуль ESP32 DEVKIT V1 с 36 GPIO – если вы используете другую модель, проверьте распиновку вашей платы.)
Примечание: если вы используете модуль с датчиком DHT, он обычно поставляется только с тремя выводами. Выводы должны быть промаркированы, чтобы вы знали, как их подключать. Кроме того, многие такие модули уже имеют встроенный подтягивающий резистор, поэтому вам не нужно добавлять его в схему.
Установка библиотек
Для этого проекта необходимо установить несколько библиотек:
Библиотеки DHT и Adafruit Unified Sensor Driver для чтения данных с датчика DHT.
Библиотеки ESPAsyncWebServer и Async TCP от ESP32Async для создания асинхронного веб-сервера.
Вы можете установить эти библиотеки через менеджер библиотек 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">°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">%</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);
Создание веб-страницы
Переходим к веб-странице сервера.
Как видно на рисунке выше, веб-страница содержит один заголовок и два абзаца. Один абзац отображает температуру, а другой – влажность. Также есть две иконки для оформления страницы.
Давайте рассмотрим, как создается эта веб-страница.
Весь 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">°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">%</sup>
</p>
Теги <i> отображают иконки fontawesome.
Как отображать иконки
Для выбора иконок перейдите на сайт Font Awesome Icons.
Найдите нужную иконку. Например, «thermometer».
Нажмите на нужную иконку. Затем просто скопируйте предоставленный HTML-текст.
<i class="fas fa-thermometer-half">
Для выбора цвета просто передайте параметр 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">°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">%</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. Ваш веб-сервер должен отобразить последние показания датчика.
Обратите внимание, что показания температуры и влажности обновляются автоматически без необходимости перезагружать веб-страницу.
Устранение неполадок
Если ваш датчик DHT не может получить показания, прочитайте наше руководство по устранению неполадок DHT, чтобы решить проблему.
Заключение
В этом руководстве мы показали, как создать асинхронный веб-сервер на ESP32 для отображения показаний датчика DHT11 или DHT22 и как автоматически обновлять данные.
Если вам понравился этот проект, вам также могут быть интересны:
Это руководство является предварительным обзором курса «Изучите ESP32 с Arduino IDE». Если вам понравился этот проект, обязательно ознакомьтесь со страницей курса ESP32, где мы рассматриваем эту и многие другие темы, связанные с ESP32.
Источник: ESP32 DHT11/DHT22 Web Server – Temperature and Humidity using Arduino IDE