ESP32/ESP8266 Веб-сервер термостата – управление выходом на основе температуры

В этом проекте вы создадите веб-сервер термостата на ESP32/ESP8266 с полем ввода для установки порогового значения температуры. Это позволяет автоматически управлять выходом на основе текущих показаний температуры. Выход будет включён, если температура превышает пороговое значение, и выключен, если она ниже порога – это можно использовать для создания простого проекта термостата.

ESP32 или ESP8266 управление выходом на основе порогового значения температуры -- веб-сервер термостата

В качестве примера мы будем считывать температуру с помощью датчика DS18B20. Вы можете использовать любой другой датчик температуры, например DHT11/DHT22, BME280 или LM35.

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

Обзор проекта

На следующем изображении показан общий обзор проекта, который мы создадим.

Обзор проекта веб-сервера ESP32 ESP8266 с пороговым значением температуры
  • ESP32/ESP8266 размещает веб-сервер, который отображает последние показания температуры с датчика DS18B20.

  • Есть поле ввода для установки порогового значения температуры. Когда температура превышает порог, выход автоматически включается. Вы можете инвертировать эту логику в зависимости от применения вашего проекта.

  • Когда температура опускается ниже порога, выход выключается.

  • Систему можно активировать или деактивировать через веб-сервер. Если вы решите деактивировать систему, выход сохранит своё состояние, независимо от значения температуры.

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

Веб-сервер ESP32/ESP8266 с пороговым значением температуры для автоматического управления выходом

Необходимые условия

Перед началом работы над этим проектом убедитесь, что выполнены все перечисленные ниже условия.

1. Дополнение ESP32 или ESP8266 для Arduino IDE

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

2. Библиотеки асинхронного веб-сервера

Для создания веб-сервера вам необходимо установить следующие библиотеки:

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

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

Платы разработки ESP32 и ESP8266

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

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

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

Перед началом работы подключите датчик температуры DS18B20 к вашей плате.

ESP32 с DS18B20 и светодиодом

Если вы используете ESP32, подключите DS18B20 как показано на следующей схеме, с выводом данных, подключённым к GPIO 4.

Схема подключения датчика температуры DS18B20 к плате ESP32

ESP8266 с DS18B20 и светодиодом

Если вы используете ESP8266, подключите DS18B20 как показано на следующей схеме, с выводом данных, подключённым к GPIO 4 (D2).

Схема подключения датчика температуры DS18B20 к плате ESP8266 (NodeMCU)

Код – Веб-сервер термостата с вводом порогового значения

Скопируйте следующий код в вашу 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% &deg;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% &deg;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 секунд.

Монитор последовательного порта ESP32 ESP8266 с пороговым значением температуры в Arduino IDE

Откройте браузер и введите IP-адрес ESP. Должна загрузиться аналогичная веб-страница со значениями по умолчанию (определёнными в вашем коде):

Веб-сервер ESP32 ESP8266 с пороговым значением температуры в Arduino IDE

Если триггер активирован (флажок установлен) и температура превышает пороговое значение, светодиод должен загореться (выход установлен в HIGH).

ESP32 ESP8266 управление выходом при температуре выше порогового значения

После этого, если температура опускается ниже порогового значения, выход выключится.

ESP32 ESP8266 управление выходом при температуре ниже порогового значения

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

Одновременно вы должны увидеть новые значения полей ввода в мониторе последовательного порта.

ESP32 ESP8266 изменение порогового значения температуры на веб-сервере

Заключение

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

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

Если вы хотите узнать больше о ESP32 и ESP8266, ознакомьтесь с нашими проектами и ресурсами:

Спасибо за чтение.


Источник: ESP32/ESP8266 Thermostat Web Server – Control Output Based on Temperature