ESP32/ESP8266 Веб-сервер термостата – управление выходом на основе температуры
В этом проекте вы создадите веб-сервер термостата на ESP32/ESP8266 с полем ввода для установки порогового значения температуры. Это позволяет автоматически управлять выходом на основе текущих показаний температуры. Выход будет включён, если температура превышает пороговое значение, и выключен, если она ниже порога – это можно использовать для создания простого проекта термостата.
В качестве примера мы будем считывать температуру с помощью датчика DS18B20. Вы можете использовать любой другой датчик температуры, например DHT11/DHT22, BME280 или LM35.
Для лучшего понимания работы этого проекта рекомендуем ознакомиться со следующими руководствами:
Ввод данных через HTML-форму на веб-сервере ESP32/ESP8266 (Arduino IDE)
ESP32 с DS18B20 (один датчик, несколько датчиков, веб-сервер)
ESP8266 NodeMCU с DS18B20 (один датчик, несколько датчиков, веб-сервер)
Обзор проекта
На следующем изображении показан общий обзор проекта, который мы создадим.
ESP32/ESP8266 размещает веб-сервер, который отображает последние показания температуры с датчика DS18B20.
Есть поле ввода для установки порогового значения температуры. Когда температура превышает порог, выход автоматически включается. Вы можете инвертировать эту логику в зависимости от применения вашего проекта.
Когда температура опускается ниже порога, выход выключается.
Систему можно активировать или деактивировать через веб-сервер. Если вы решите деактивировать систему, выход сохранит своё состояние, независимо от значения температуры.
На следующем изображении показано, как выглядит страница веб-сервера.
Необходимые условия
Перед началом работы над этим проектом убедитесь, что выполнены все перечисленные ниже условия.
1. Дополнение ESP32 или ESP8266 для Arduino IDE
Этот проект совместим как с платами ESP32, так и с ESP8266. Мы будем программировать эти платы с помощью Arduino IDE, поэтому убедитесь, что у вас установлены необходимые дополнения:
2. Библиотеки асинхронного веб-сервера
Для создания веб-сервера вам необходимо установить следующие библиотеки:
ESP32: установите библиотеки ESPAsyncWebServer и AsyncTCP (от ESP32Async).
ESP8266: установите библиотеки ESPAsyncWebServer и ESPAsyncTCP (от ESP32Async).
Вы можете установить эти библиотеки через менеджер библиотек Arduino IDE. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеки по названию.
3. Необходимые компоненты
Для выполнения этого руководства вам понадобятся следующие компоненты:
ESP32 (см. лучшие платы ESP32) или ESP8266 (см. лучшие платы ESP8266)
Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!
Схема подключения
Перед началом работы подключите датчик температуры DS18B20 к вашей плате.
ESP32 с DS18B20 и светодиодом
Если вы используете ESP32, подключите DS18B20 как показано на следующей схеме, с выводом данных, подключённым к GPIO 4.
ESP8266 с DS18B20 и светодиодом
Если вы используете ESP8266, подключите DS18B20 как показано на следующей схеме, с выводом данных, подключённым к GPIO 4 (D2).
Код – Веб-сервер термостата с вводом порогового значения
Скопируйте следующий код в вашу Arduino IDE, но пока не загружайте его. Вам нужно внести некоторые изменения, чтобы он заработал. Необходимо вставить ваши сетевые учётные данные и пороговое значение температуры по умолчанию.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-thermostat-web-server/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Default Threshold Temperature Value
String inputMessage = "25.0";
String lastTemperature;
String enableArmChecked = "checked";
String inputMessage2 = "true";
// HTML web page to handle 2 input fields (threshold_input, enable_arm_input)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html><head>
<title>Temperature Threshold Output Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head><body>
<h2>DS18B20 Temperature</h2>
<h3>%TEMPERATURE% °C</h3>
<h2>ESP Arm Trigger</h2>
<form action="/get">
Temperature Threshold <input type="number" step="0.1" name="threshold_input" value="%THRESHOLD%" required><br>
Arm Trigger <input type="checkbox" name="enable_arm_input" value="true" %ENABLE_ARM_INPUT%><br><br>
<input type="submit" value="Submit">
</form>
</body></html>)rawliteral";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
AsyncWebServer server(80);
// Replaces placeholder with DS18B20 values
String processor(const String& var){
//Serial.println(var);
if(var == "TEMPERATURE"){
return lastTemperature;
}
else if(var == "THRESHOLD"){
return inputMessage;
}
else if(var == "ENABLE_ARM_INPUT"){
return enableArmChecked;
}
return String();
}
// Flag variable to keep track if triggers was activated or not
bool triggerActive = false;
const char* PARAM_INPUT_1 = "threshold_input";
const char* PARAM_INPUT_2 = "enable_arm_input";
// Interval between sensor readings. Learn more about ESP32 timers: https://RandomNerdTutorials.com/esp32-pir-motion-sensor-interrupts-timers/
unsigned long previousMillis = 0;
const long interval = 5000;
// GPIO where the output is connected to
const int output = 2;
// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("ESP IP Address: http://");
Serial.println(WiFi.localIP());
pinMode(output, OUTPUT);
digitalWrite(output, LOW);
// Start the DS18B20 sensor
sensors.begin();
// Send web page to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html, processor);
});
// Receive an HTTP GET request at <ESP_IP>/get?threshold_input=<inputMessage>&enable_arm_input=<inputMessage2>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
// GET threshold_input value on <ESP_IP>/get?threshold_input=<inputMessage>
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
// GET enable_arm_input value on <ESP_IP>/get?enable_arm_input=<inputMessage2>
if (request->hasParam(PARAM_INPUT_2)) {
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
enableArmChecked = "checked";
}
else {
inputMessage2 = "false";
enableArmChecked = "";
}
}
Serial.println(inputMessage);
Serial.println(inputMessage2);
request->send(200, "text/html", "HTTP GET request sent to your ESP.<br><a href=\"/\">Return to Home Page</a>");
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
sensors.requestTemperatures();
// Temperature in Celsius degrees
float temperature = sensors.getTempCByIndex(0);
Serial.print(temperature);
Serial.println(" *C");
// Temperature in Fahrenheit degrees
/*float temperature = sensors.getTempFByIndex(0);
Serial.print(temperature);
Serial.println(" *F");*/
lastTemperature = String(temperature);
// Check if temperature is above threshold and if it needs to trigger output
if(temperature > inputMessage.toFloat() && inputMessage2 == "true" && !triggerActive){
String message = String("Temperature above threshold. Current temperature: ") +
String(temperature) + String("C");
Serial.println(message);
triggerActive = true;
digitalWrite(output, HIGH);
}
// Check if temperature is below threshold and if it needs to trigger output
else if((temperature < inputMessage.toFloat()) && inputMessage2 == "true" && triggerActive) {
String message = String("Temperature below threshold. Current temperature: ") +
String(temperature) + String(" C");
Serial.println(message);
triggerActive = false;
digitalWrite(output, LOW);
}
}
}
Как работает код
Продолжайте чтение, чтобы узнать, как работает код, или перейдите к разделу «Демонстрация».
Библиотеки
Начнём с подключения необходимых библиотек. Библиотеки WiFi (или ESP8266WiFi), AsyncTCP (или ESPAsyncTCP) и ESPAsyncWebServer необходимы для создания веб-сервера.
Библиотеки OneWire и DallasTemperature необходимы для работы с DS18B20.
Код автоматически подключает нужные библиотеки в зависимости от выбранной платы (ESP32 или ESP8266).
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#else
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
Сетевые учётные данные
Вставьте ваши сетевые учётные данные в следующие строки:
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Пороговое значение температуры по умолчанию
В переменную inputMessage вставьте пороговое значение температуры по умолчанию. Мы установили его на 25.0, но вы можете задать любое другое значение.
String inputMessage = "25.0";
Вспомогательные переменные
Переменная lastTemperature будет хранить последнее значение температуры для сравнения с пороговым значением.
String lastTemperature;
Переменная enableArmChecked будет показывать, установлен ли флажок для автоматического управления выходом или нет.
String enableArmChecked = "checked";
Если флажок установлен, значение, сохранённое в inputMessage2, должно быть установлено в true.
String inputMessage2 = "true";
HTML-текст
Далее у нас есть базовый HTML-текст для создания страницы с двумя полями ввода: полем ввода порогового значения температуры и флажком для включения или отключения автоматического управления выходом.
Веб-страница также отображает последние показания температуры с датчика DS18B20.
Следующие строки отображают температуру:
<h2>DS18B20 Temperature</h2>
<h3>%TEMPERATURE% °C</h3>
%TEMPERATURE% – это заполнитель, который будет заменён фактическим значением температуры, когда ESP32/ESP8266 обслуживает страницу.
Далее у нас есть форма с двумя полями ввода и кнопкой «Submit». Когда пользователь вводит данные и нажимает кнопку «Submit», эти значения отправляются на ESP для обновления переменных.
<form action="/get">
Temperature Threshold <input type="number" step="0.1" name="threshold_input" value="%THRESHOLD%" required><br>
Arm Trigger <input type="checkbox" name="enable_arm_input" value="true" %ENABLE_ARM_INPUT%><br><br>
<input type="submit" value="Submit">
</form>
Первое поле ввода имеет тип number, а второе поле ввода – это checkbox. Чтобы узнать больше о полях ввода, рекомендуем ознакомиться со следующими ресурсами на сайте w3schools:
Атрибут action формы указывает, куда отправлять данные, введённые в форму, после нажатия кнопки submit. В данном случае выполняется HTTP GET-запрос по адресу:
/get?threshold_input=value&enable_arm_input=value
Значение value соответствует тексту, который вы вводите в каждое поле ввода. Чтобы узнать больше об обработке полей ввода на ESP32/ESP8266, прочитайте: Ввод данных через HTML-форму на веб-сервере ESP32/ESP8266 с использованием Arduino IDE.
processor()
Функция processor() заменяет все заполнители в HTML-тексте фактическими значениями.
%TEMPERATURE% – lastTemperature
%THRESHOLD% – inputMessage
String processor(const String& var){
//Serial.println(var);
if(var == "TEMPERATURE"){
return lastTemperature;
}
else if(var == "THRESHOLD"){
return inputMessage;
}
else if(var == "ENABLE_ARM_INPUT"){
return enableArmChecked;
}
return String();
}
Параметры полей ввода
Следующие переменные будут использоваться для проверки, получили ли мы HTTP GET-запрос от этих полей ввода, и для сохранения значений в переменные.
const char* PARAM_INPUT_1 = "threshold_input";
const char* PARAM_INPUT_2 = "enable_arm_input";
Интервал между считываниями
Каждые 5000 миллисекунд (5 секунд) мы получаем новое значение температуры с датчика DS18B20 и сравниваем его с пороговым значением. Для отслеживания времени мы используем таймеры.
Измените переменную interval, если хотите изменить время между считываниями датчика.
unsigned long previousMillis = 0;
const long interval = 5000;
Выход GPIO
В этом примере мы будем управлять GPIO 2. Этот вывод подключён к встроенному светодиоду ESP32 и ESP8266, что позволяет легко проверить, работает ли проект как ожидается. Вы можете управлять любым другим выходом, и для многих приложений вы захотите управлять модулем реле.
// GPIO where the output is connected to
const int output = 2;
Инициализация датчика температуры DS18B20
Инициализация датчика температуры DS18B20.
// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
Чтобы узнать больше о подключении датчика температуры DS18B20 к плате ESP, прочитайте:
setup()
В функции setup() подключаемся к Wi-Fi в режиме станции и выводим IP-адрес ESP:
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("ESP IP Address: http://");
Serial.println(WiFi.localIP());
Устанавливаем GPIO 2 как выход и устанавливаем его в LOW при первом запуске ESP.
pinMode(output, OUTPUT);
digitalWrite(output, LOW);
Инициализируем датчик температуры DS18B20:
sensors.begin();
Обработка веб-сервера
Затем определяем, что происходит, когда ESP32 или ESP8266 получает HTTP-запросы. Когда мы получаем запрос по корневому URL /, отправляем HTML-текст с обработчиком (чтобы заполнители были заменены последними значениями).
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
Когда форма отправлена, ESP получает запрос по следующему URL:
<ESP_IP>/get?threshold_input=<inputMessage>&enable_arm_input=<inputMessage2>
Поэтому мы проверяем, содержит ли запрос входные параметры, и сохраняем эти параметры в переменные:
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
// GET threshold_input value on <ESP_IP>/get?threshold_input=<inputMessage>
if (request->hasParam(PARAM_INPUT_1)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
// GET enable_arm_input value on <ESP_IP>/get?enable_arm_input=<inputMessage2>
if (request->hasParam(PARAM_INPUT_2)) {
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
enableArmChecked = "checked";
}
else {
inputMessage2 = "false";
enableArmChecked = "";
}
}
Это часть кода, где переменные будут заменены значениями, отправленными через форму. Переменная inputMessage сохраняет пороговое значение температуры, а inputMessage2 сохраняет, установлен ли флажок или нет (нужно ли управлять GPIO или нет).
После отправки значений через форму отображается новая страница с сообщением об успешной отправке запроса на вашу плату и ссылкой для возврата на главную страницу.
request->send(200, "text/html", "HTTP GET request sent to your ESP.<br><a href=\"/\">Return to Home Page</a>");
});
Наконец, запускаем сервер:
server.begin();
loop()
В функции loop() мы используем таймеры для получения новых показаний температуры каждые 5 секунд.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
sensors.requestTemperatures();
// Temperature in Celsius degrees
float temperature = sensors.getTempCByIndex(0);
Serial.print(temperature);
Serial.println(" *C");
// Temperature in Fahrenheit degrees
/*float temperature = sensors.getTempFByIndex(0);
Serial.print(temperature);
Serial.println(" *F");*/
lastTemperature = String(temperature);
После получения нового значения температуры мы проверяем, выше оно или ниже порога, и включаем или выключаем выход соответственно.
В этом примере мы устанавливаем состояние выхода в HIGH, если выполнены все следующие условия:
Текущая температура выше порогового значения;
Автоматическое управление выходом включено (флажок на веб-странице установлен);
Выход ещё не был активирован.
// Check if temperature is above threshold and if it needs to trigger output
if(temperature > inputMessage.toFloat() && inputMessage2 == "true" && !triggerActive){
String message = String("Temperature above threshold. Current temperature: ")
+ String(temperature) + String("C");
Serial.println(message);
triggerActive = true;
digitalWrite(output, HIGH);
}
Затем, если температура опускается ниже порога, устанавливаем выход в LOW.
else if((temperature < inputMessage.toFloat()) && inputMessage2 == "true" && triggerActive) {
String message = String("Temperature below threshold. Current temperature: ")
+ String(temperature) + String(" C");
Serial.println(message);
triggerActive = false;
digitalWrite(output, LOW);
}
В зависимости от вашего применения вы можете захотеть изменить выход на LOW, когда температура выше порога, и на HIGH, когда температура ниже порога.
Демонстрация – термостат ESP
Загрузите код на вашу плату ESP (с DS18B20, подключённым к вашей плате ESP32 или ESP8266).
Откройте монитор последовательного порта на скорости 115200 бод и нажмите кнопку RST/EN на плате. ESP выведет свой IP-адрес и начнёт отображать новые значения температуры каждые 5 секунд.
Откройте браузер и введите IP-адрес ESP. Должна загрузиться аналогичная веб-страница со значениями по умолчанию (определёнными в вашем коде):
Если триггер активирован (флажок установлен) и температура превышает пороговое значение, светодиод должен загореться (выход установлен в HIGH).
После этого, если температура опускается ниже порогового значения, выход выключится.
Вы можете использовать поля ввода на веб-странице для изменения порогового значения или для активации и деактивации управления выходом. Чтобы любые изменения вступили в силу, просто нажмите кнопку «Submit».
Одновременно вы должны увидеть новые значения полей ввода в мониторе последовательного порта.
Заключение
В этом проекте вы узнали, как создать веб-сервер с пороговым значением для автоматического управления выходом в зависимости от текущих показаний температуры (веб-сервер термостата). В качестве примера мы управляли светодиодом. Для реальных приложений вы, вероятно, захотите управлять модулем реле. Вы можете прочитать следующие руководства, чтобы узнать, как управлять реле с помощью ESP:
Модуль реле ESP32 – управление устройствами переменного тока (веб-сервер)
Модуль реле ESP8266 NodeMCU – управление устройствами переменного тока (веб-сервер)
Мы использовали простой HTML-текст, чтобы упростить понимание проекта. Мы рекомендуем добавить CSS для стилизации вашей веб-страницы, чтобы она выглядела лучше. Вы также можете добавить уведомления по электронной почте к этому проекту.
Если вы хотите узнать больше о ESP32 и ESP8266, ознакомьтесь с нашими проектами и ресурсами:
Спасибо за чтение.
Источник: ESP32/ESP8266 Thermostat Web Server – Control Output Based on Temperature