Перейти к содержимому

Термостат на ESP32 с удаленным управлением. Часть 9. Термостат и управление нагрузкой

Добрый день, уважаемые читатели!

Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF с возможностью дистанционного контроля и управления через WiFi. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.

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


Управление GPIO в ESP-IDF

На самом деле в управлении нагрузкой на ESP32 и ESP-IDF нет вообще ничего сложно и страшного. Не сложнее, чем в Arduino.

  • gpio_set_direction(pin, GPIO_MODE_OUTPUT) – настроить вывод pin в режим вывода
  • gpio_set_level(pin, level) – записать в этот самый вывод pin уровень level

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

Ну дальше, как всегда, появляются дополнительные идеи и хотелки:

  • Нужно включить нагрузку только на определенное время? Ничего сложного – только добавьте воды таймер.
  • Нужно включать и выключать нагрузку в импульсном режиме – добавьте ещё один таймер.
  • Хочется фиксировать время включения и выключения нагрузки, а также длительность работы – опять же предельно просто – несколько переменных и готово
  • Публиковать всё это на mqtt-брокере в виде JSON? И с этим можно легко справится.

Но всё это повторяется из проекта в проект, и чтобы не переносить один и тот же код копипастой, я написал для этого специальную библиотечку. Как всегда, она довольно тесно интегрирована с другими моими библиотеками для ESP-IDF, и идеально подходит для проекта термостата.

Встречайте – reLoadCtrl: GitHub – kotyara12/reLoadCtrl


 

reLoadCtrl – класс для управления нагрузкой

Данная библиотека, как и многие другие, реализована в виде класса, что позволяет легко использовать одновременно несколько отдельных экземпляров класса в рамках одного проекта. Кроме того, ООП позволяет изящно организовать разные реализации либо для работы со встроенными GPIO микроконтроллера, либо для различных расширителей GPIO.

Основные возможности:

  • Два варианта реализации: rLoadGpioController для встроенных GPIO и rLoadIoExpController для расширителей IO.
  • Управление нагрузкой по низкому или высокому уровню на выходе – может работать с любыми схемами и готовыми модулями
  • Инициализация для встроенных GPIO – вам не нужно заботится об этом
  • Хранение текущего состояния нагрузки
  • Фиксация времени включения и выключения нагрузки, вычисление длительности работы за последний цикл, за сутки, неделю, месяц, учетный период (например с 25 числа месяца по 25 числа следующего месяца), год.
  • Включение нагрузки на определенное время по таймеру. Всю работу с таймерами библиотека берет на себя
  • Управление нагрузкой в циклическом режиме в пределах общей длительности. Например: включаем насос на 30 секунд, затем 60 секунд ждем, затем цикл повторяется. И так 30 минут.
  • Формирование JSON для публикации на MQTT с данными о состоянии нагрузки и всеми счетчиками и интервалами.

Создание экземпляра класса

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

rLoadController(uint8_t pin, bool level_on, bool use_pullup, bool use_timer, const char* nvs_space, uint32_t* cycle_duration, uint32_t* cycle_interval, timeintv_t cycle_type, cb_load_change_t cb_gpio_before, cb_load_change_t cb_gpio_after, cb_load_change_t cb_state_changed, cb_load_publish_t cb_mqtt_publish)

где:

  • uint8_t pin – номер вывода GPIO ESP32 или вывода IO EXT
  • bool level_on – уровень, которым включается нагрузка. 1 – если установить высокий уровень для включения нагрузки, 0 – если нужно установить низкий уровень для включения нагрузки
  • bool use_pullup – смысла в этом аргументе вообще нет. Возможно позже уберу, если не лень будет
  • bool use_timer – использовать постоянный таймер для управления нагрузкой. Если true, то таймер будет создан при инициализации GPIO и он останется “жить” до удаления экземпляра класса. Если false, то таймер будет создан при вызове loadSetTimer() и после использования удален. Это чуть-чуть экономит кучу, если таймер используется нечасто.
  • const char* nvs_space – ключ для сохранения данных счетчиков в NVS разделе. Должен быть не длиннее 10 символов!
  • uint32_t* cycle_duration – указатель на переменную, в которой хранится длительность включения нагрузки в циклическом режиме. Это именно указатель на переменную, а не само значение – сделано это для того, чтобы можно было менять параметр во время работы программы
  • uint32_t* cycle_interval – указатель на переменную, в которой хранится длительность паузы между включениями нагрузки в циклическом режиме.
  • timeintv_t cycle_type – размерность цикла: миллисекунды, секунды, минуты, часы
  • cb_load_change_t cb_gpio_before – функция обратного вызова, которая будет вызвана перед любым изменением состояния выхода. Например можно отключить прерывания по входам перед коммутацией мощной нагрузки
  • cb_load_change_t cb_gpio_after – функция обратного вызова, которая будет вызвана после любого изменения состояния выхода.
  • cb_load_change_t cb_state_changed – функция обратного вызова, которая будет вызвана сразу после успешного изменения состояния вывода. Например можно отправить уведомление в telegram: “котёл включен
  • cb_load_publish_t cb_mqtt_publish – – функция обратного вызова для публикации пакета JSON на mqtt брокере. В принципе, можно было бы обойтись и без этого, но тогда бы пришлось инклудить в библиотеку mqtt-клиент, а это не очень удобно.

Затем, в любом удобном месте программы, нужно инициализировать вывод с помощью функцию loadInit(init_value), в котором как раз и будет выполнена настройка вывода и таймеров.

Простое управление нагрузкой

Для переключения состояния реле используйте функцию loadSetState:

loadSetState(bool new_state, bool forced, bool publish)

где:

  • bool new_state – новое состояние нагрузки
  • bool forced – записать состояние в выход, даже если текущее состояние нагрузки соответствует новому. В противном случае изменение произойдет, только если текущее состояние отличается от нового.
  • bool publish – выполнить публикацию состояния на MQTT после изменения состояния

При этом если при создании экземпляра класса заданы cycle_duration и cycle_interval, и они больше 0, то нагрузка будет включаться в периодическом режиме.

Включение нагрузки на определенный период

Если вам нужно включить нагрузку только на определенное время, используйте функцию loadSetTimer():

bool loadSetTimer(uint32_t duration_ms);

Эта функция принимает единственный параметр – длительность интервала включения нагрузки.

Для принудительного отключения нагрузки до истечения заданного времени таймера используйте описанную выше функцию loadSetState(false, …) – работа таймера будет прервана досрочно и нагрузка выключена.


 

Термостат

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

Предупреждение! В прошлой статье я рассказывал, как изменить код прошивки для того, чтобы можно было подключить другие сенсоры. В этой версии я вернул все сенсоры в прошивке на предыдущий вариант – DHT22 + BME280.

1. Добавляем параметры

В первую очередь нам нужно добавить рабочие параметры: температуру и гистерезис. Параметр – это переменная, которую можно изменять “извне” во время работы прошивки, например через mqtt-сервер. Сделать это можно двумя способами:

  • в простом случае мы просто задаем две температуры – температуру включения и температуру выключения, например 22°С и 24°С. Это достаточно просто, но редко применяется на практике – в “заводских” термостатах этот метод не используется, и далее вы поймете почему
  • более популярный вариант – целевая температура плюс гистерезис. Например 23°С и 1°С. Это гораздо удобнее для восприятия простыми пользователями, так как для установки новой температуры в помещении нужно изменить только один единственный параметртемпературу, а гистерезис остается постоянным и вовсе обычно “спрятан” внутри системных настроек.

Итак, с параметрами определились. Их можно определить как в sensors.h, так и в sensors.cpp – в данном случае без разницы. Я предпочитаю первый вариант – отделяю “мух от котлет” (хотя может быть идеологически это и не совсем правильно).

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

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

За эти возможности в моей прошивке отвечают библиотеки reNvs и reParams. Подробней про них я еще расскажу отдельно, а пока просто воспользуемся их возможностями “как есть”.

Найдите в файле sensors.cpp функцию void sensorsInitParameters(). В ней мы вначале создадим группу параметров – pgThermostat, а “внутри” неё уже будем добавлять нужные параметры. Создание группы – не обязательное действие, но создает некий порядок в топиках. Для создания группы вначале потребуется добавить константы:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-3

Регистрируем группу параметров:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-4

 

Ок, теперь можно добавить сами параметры:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-5

При этом не забываем и про константы:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-6

Хотя, вы можете вполне обойтись и без всех этих #define. Но я так не люблю – потом замучаешься лазить по коду, чтобы что-то исправить. А так все настройки в одном месте, и их очень легко найти и исправить.

Всё готово! Если теперь скомпилировать и запустить проект, то мы увидим два новых типика в секции location/device/confirm. Но отправлять новые данные нужно в location/device/config! Об этом я писал в статье “Топики”…

2. Добавляем контроллер управления реле / котлом

Теперь можно добавить сам объект-контроллер. Для начала не забываем добавить библиотеку в список:

#include “reLoadCtrl.h”

Затем объявляем собственно сам экземпляр. В библиотеке объявлены три класса – один виртуальный и два обычных. Поскольку мы используем реле, подключенное ко встроенному GPIO, мы должны использовать класс rLoadGpioController.

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-7

Я думаю, здесь все всем будет понятно, пока поподробнее остановимся на последних функциях обратного вызова. Выглядят они пока следующим образом:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-8

где:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-9

Следующим этапом нужно настроить GPIO выход и сам объект, делается это достаточно просто:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-10

  • первая строка (не обязательная) передает в объект указатель на день месяца, с которого начинается очередной учетный период, например 25 число.
  • второй командой считываем ранее сохраненную статистику из NVS памяти
  • ну и последняя команда настраивает выход GPIO и все внутренние структуры и отключает нагрузку (false)

Далее, чтобы статистика включений и выключений нагрузки сохранялась в NVS памяти, нужно добавить соответствующую строчку в конце функции sensorsStoreData():

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-11

Все вместе это обеспечит сохранение статистики включений и выключений нагрузки в течение всего срока службы устройства. Если вам не нужно сохранение статистики, вы можете исключить эти команды. Но согласитесь – мелочь, а удобно.

Но сами по себе интервалы этот класс считать не умеет. Вместо этого встроенный в прошивку шедулер рассылает через систему событий временные метки – начало (00,000 секунд) каждой минуты, часа, дня, и т д. Нам требуется только подписаться на нужно событие и ждать. Добавляем строку в обработчик события sensorsTimeEventHandler():

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-12

И последнее приготовление… Прежде чем мы сможем что-либо публиковать на MQTT-брокере, мы должны сгенерировать топик. Топики в данной прошивке не есть что-то постоянное, они создаются в момент подключения к брокеру, хранятся в оперативной памяти и удаляются в момент отключения от него. Сделано это так потому, что MQTT-брокера может быть два (основной и резервный) и они могут иметь совершенно разные настройки. В соответствии с этим топики создаются по разным правилам.

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-13

Почти все готово. Осталось не забыть добавить в главный цикл публикацию состояния и статистики. Впрочем – это не строго обязательно. Во время переключения нагрузки объект сам опубликует свое состояние. Но неплохо периодически “обновлять” данные на брокере вместе с обновление данных с сенсоров.

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-14

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

 

3. Управление бойлером в зависимости от температуры

Теперь вы можете написать код для управления нагрузкой. Для включения котла достаточно написать команду:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-15

Можно прикрутить “входящий” топик, с помощью которого вы будете дистанционно включать и выключать реле, а соответственно и котёл. Но этом слишком не интересно и не практично – а вдруг вы заболели и не можете управлять температурой в доме??? Автоматика должна работать незаметно и автономно, что бы не случилось.

У нас же есть датчики температуры и мы добавили параметры для управления температурой! Напишем несложную функцию для автоматического управления.

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-16

Не забываем добавить вызов sensorsBoilerControl(); в основной цикл задачи после получения данных с сенсоров!

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

Цель достигнута. Но не будем почивать на лаврах, ведь нет предела совершенству…

 

4. Добавляем расписание

Большинство современных электронных термостатов умеют поддерживать заданную температуру в пределах заданного времени суток. Например днем, когда все члены семьи на работе, можно котел и не включать, а включить только к возвращению жителей домой. Давайте добавим простейшее расписание – в пределах времени суток. Например котел может работать с 15:00 часов одного дня до 08:00 часов следующего, в остальное время работа термостата должна быть заблокирована.

Добавляем параметры.

15000800 означает с 15-00 одного дня по 08-00 другого дня

Не забываем зарегистрировать параметр

Теперь можно добавить проверку времени работы. Это не просто, а очень просто!

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-19

Вот, собственно, и все суточное расписание. А теперь подумайте, как сделать недельное расписание, например будни + выходные.

5. Принудительная блокировка или включение котла

А теперь давайте подумаем, как отключить термостат на лето. Ведь при любом незначительном понижении температуры в комнате наш термостат включит котел. Конечно, можно выключить сам котёл, но все-таки лучше предусмотреть летний режим на самом термостате. Второй вопрос – иногда нужно, чтобы котёл работал по заданному выше суточному расписанию вне зависимости от температуры в комнате. То есть нужно предусмотреть принудительное включение или блокировку работы термостата.

Объявляем новый тип данных и новую переменную – параметр “режим работы“:

Я добавил гораздо больше вариантов чем перечислено в тексте выше

Регистрируем параметр:

Перечислимые типы в Си - это физически тип uint8_t

Ранее написанную функцию проверки температуры в помещении я вынес в отдельную новую функцию, ниже будет понятно зачем:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-22

И затем полностью переделываем старую функцию управления:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-23

Вот теперь всё хорошо. В любой момент можно включить ручной режим со смартфона и вернуть в автоматический. Можно прошивать, упаковывать в коробку и устанавливать на место.

6. Добавляем уведомления о включении и выключении нагрузки

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

Чтобы уведомления можно было отключить в любой момент (если они надоели), добавим ещё одну переменную:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-24

Помните функцию boilerStateChange(), которую мы создали выше? Вот теперь пришла пора её написать:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-25

где:

Добрый день, уважаемые читатели! Продолжаю описание самодельного устройства на базе ESP32 и ESP-IDF. Если вы наткнулись на эту статью случайно, то приведу список предыдущих статей.-26

Как видите – ничего сложного… По аналогии вы теперь можете сделать управление вентилятором в ванной комнате или погребе или тепловентилятором в гараже.

 

В качестве домашнего задания

Теперь, при должной фантазии, вы сможете сделать сколь угодно сложный и удобный термостат своими руками. Например с недельным расписанием. А кроме того я вам предлагаю подумать вот над чем….

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

Какие будут ваши идеи насчёт преодоления этого недостатка?


Ссылки

Как всегда, новая прошивка доступна в репозитории: GitHub – kotyara12/telemeter_dzen: Термостат + охранно-пожарная сигнализация

 

Все статьи цикла “Термостат и ОПС”:

Прошивка K12 для ESP32 и ESP-IDF:

Дополнительные статьи, которые применимы к любым устройствам, запрограммированным с помощью тех же самых библиотек.

 

🔶 Полный архив статей вы найдете здесь

 


Пожалуйста, оцените статью:
[ 0 из 5, всего 0 оценок ]

9 комментариев для “Термостат на ESP32 с удаленным управлением. Часть 9. Термостат и управление нагрузкой”

  1. Александр

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

    1. Здравствуйте. Конечно есть, как и на всяком микроконтроллере. Подключайте нужную библиотеку и работайте. Но в данном устройстве он не предусмотрен. Этот термостат больше для удаленной дачи, чем для дома. Хотя, если есть желание – две шины IIC на разъемы выведены, подключить к любой из них техническая возможность есть.

  2. “Избавиться” от проветривания можно установкой датчиков температуры во всех комнатах и по температуре от всех датчиков “принимать решение ” включать котёл или нет.

  3. а как можно новичку который не знает С++ получить готовый код, может купить или бесплатно, спасибо

    1. Всё есть на гитхабе, в открытом доступе. Но подправить под ваши данные все равно потребуется – как минимум имя сети, имя сервера, пароли и т.д.

  4. у меня самодельный электрокотел неделю как работает, управление через Aqara датчик температуры и реле, если реле включится а батарея в датчике температуры сдохнет то котел будет греть до кипятка, хотелось бы чтото без управления через воздух, тоесть автономная работа с управлением через энкодер или телефон, есть варианты на ютюбе но они не работают почемуто

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

  6. Можете тыкать в правильное направление, может ссылка есть на такой проект, очень мне поможете, спасибо

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *