Умная теплица на Arduino: датчики, полив, вентилятор и код
Умная теплица на Arduino: измерения с DHT22, датчиком влажности почвы и LDR, управление вентилятором, нагревателем, насосом и светодиодами через реле и MOSFET, гистерезис, LCD и код для ESP32.
Умная теплица на Arduino — это система автоматизации, в которой Arduino или ESP32 измеряет температуру и влажность воздуха, влажность почвы и освещённость в теплице и сама включает вентилятор, нагреватель, водяной насос и фитосветодиоды. Это одна из самых популярных тем для научных выставок, школьных конкурсов и уроков технологии, потому что результат хорошо виден: подсохла почва — запускается насос, стало жарко внутри — крутится вентилятор, наступил вечер — загораются светодиоды. А ещё между макетом теплицы из контейнера для хранения и настоящей мини-теплицей на балконе почти нет разницы ни в схеме, ни в коде.
В этом руководстве мы начинаем с того, что на самом деле нужно растениям, а затем шаг за шагом собираем макет теплицы: список деталей и примерная стоимость, подключение датчиков (DHT22, ёмкостный датчик влажности почвы, LDR) и исполнительных устройств (вентилятор, нагреватель, насос и светодиоды через реле и MOSFET), план питания и таблица пинов. Дальше разбираем логику порогов и гистерезиса, которая не даёт реле постоянно щёлкать, полный код Arduino без delay() и дисплей LCD 16x2. В последних разделах — как следить за теплицей с телефона через ESP32, версия на MicroPython для Raspberry Pi Pico, советы для школьных презентаций и научных выставок и частые ошибки.





Как работает умная теплица?
Каждая умная теплица работает по одному и тому же циклу из четырёх шагов: измерить, решить, выполнить, показать. Датчики измеряют среду: сколько градусов внутри, насколько влажный воздух, не подсохла ли почва, хватает ли света? Arduino сравнивает эти значения с правилами, которые задаёшь ты: «если температура выше 30 °C, включи вентилятор», «если влажность почвы упала ниже 35%, полей». Исполнительные устройства выполняют решение: реле включают и выключают вентилятор, нагреватель и насос, а MOSFET задаёт яркость светодиодной ленты. Наконец, LCD и твой телефон показывают текущее состояние теплицы. Климатические компьютеры в настоящих промышленных теплицах работают ровно по такому же циклу, просто у них больше датчиков и мощнее моторы.
| Что измеряем? | Датчик | Исполнительное устройство | Правило (пример) |
|---|---|---|---|
| Температура воздуха | DHT22 | Вентилятор и нагреватель | Вентилятор включается при 30 °C, нагреватель — при 16 °C |
| Относительная влажность | DHT22 | Вентилятор | Вентилятор включается при 85% и выключается при 75% |
| Влажность почвы | Ёмкостный датчик влажности почвы | Погружной мини-насос | Ниже 35% поливает импульсами по 3 секунды |
| Свет | LDR | Светодиодная лента (MOSFET) | Когда темнеет, светодиоды горят не больше 4 часов |
| Вода в баке | Поплавковый датчик уровня | Блокировка насоса | Если бак пуст, насос не включается вообще |
Что нужно растениям? Целевая температура, влажность и свет
Прежде чем писать правила, нужно понять, к чему стремиться. Большинству растений нужна разная температура днём и ночью: небольшое ночное похолодание помогает растению отдыхать. Если влажность долго держится выше 85%, на листьях конденсируется вода и быстро распространяются грибковые болезни вроде плесени и ложной мучнистой росы; поэтому вентилятор в теплице работает не только для охлаждения, но и для удаления лишней влаги. Что касается света, большей части овощной рассады нужно 14–16 часов в сутки, но не меньше важен и тёмный период длиной хотя бы 6–8 часов; если оставлять светодиоды включёнными на всю ночь, растению будет больше вреда, чем пользы. Значения ниже — хороший ориентир для начала; подстрой их под то, что выращиваешь.
| Растение | День | Ночь | Относительная влажность | Примечание |
|---|---|---|---|---|
| Томат | 21–27 °C | 15–18 °C | 60–75% | Выше 32 °C цветки могут опадать |
| Перец | 21–29 °C | 16–20 °C | 60–75% | Чувствительнее к холоду, чем томаты |
| Салат и шпинат | 15–20 °C | 10–15 °C | 60–70% | В жару и при длинном дне рано уходят в стрелку |
| Клубника | 18–24 °C | 10–13 °C | 60–75% | При высокой влажности растёт риск серой гнили |
| Рассада и проращивание | 20–25 °C | 18–20 °C | 70–85% | Температура почвы важнее температуры воздуха |
Строим макет теплицы
Самый простой и дешёвый корпус — прозрачный пластиковый контейнер для хранения с крышкой: контейнера на 40–60 литров хватит на два-три горшка, он не пропускает воду, а отверстия в нём легко сделать канцелярским ножом или нагретым жалом. Для более реалистичного вида собери каркас из деревянных реек и обшей его прозрачным оргстеклом (акрилом) или листами PETG толщиной 2–3 мм; теплица с двускатной крышей на презентации смотрится гораздо лучше. Размер около 50×35×40 см — хорошая отправная точка. Какой бы корпус ни был выбран, крышка или одна из боковых стенок должна открываться: внутрь придётся залезать рукой, чтобы полить растения, переставить датчик и поправить провода.
- Электроника снаружи: внутри теплицы влажно, а по утрам на стенках конденсируется вода. Размести Arduino, модуль реле и блок питания в отдельной коробке снаружи теплицы; внутри остаются только датчики, вентилятор, нагреватель, шланг насоса и светодиоды.
- Вентиляция: тёплый воздух поднимается вверх, поэтому установи вентилятор в верхней части одной из коротких стенок так, чтобы он выдувал воздух наружу. В нижней части противоположной стенки прорежь приточное отверстие размером с вентилятор и закрой его москитной сеткой; тогда свежий воздух входит снизу, а тёплый и влажный уходит сверху.
- Нагреватель: закрепи нагреватель у дна на алюминиевой пластине или керамической плитке, не ближе 5 см к пластиковым стенкам и растениям. Поднимаясь, тёплый воздух расходится по всей теплице.
- Светодиодное освещение: приклей светодиодную ленту к внутренней стороне крышки, на 15–30 см выше растений. Если закрепить ленту на тонком алюминиевом профиле, она лучше отводит тепло, а клей со временем не отходит.
- Датчики: размести DHT22 на высоте растений там, куда не попадает прямое солнце или свет светодиодов. Датчик влажности почвы закопай в корневую зону, подальше от края горшка. А вот LDR не должен видеть светодиоды: вынеси его наружу, на крышу теплицы, лицом к небу.
- Бак для воды: для насоса и поплавка хватит ёмкости на 1–2 литра. Поставь под горшки поддоны: лишняя вода соберётся в поддоне, а не разольётся лужей по дну контейнера.
Список деталей и стоимость
| Деталь | Кол-во | Задача | Уровень цены |
|---|---|---|---|
| Arduino Uno R3 или ESP32 DevKit | 1 | Мозг теплицы | Средний |
| Модуль температуры и влажности DHT22 (AM2302) | 1 | Температура и влажность воздуха | Низкий |
| Ёмкостный датчик влажности почвы (v1.2) | 1 (по одному на горшок) | Влажность почвы | Низкий |
| LDR (GL5528) и резистор 10 kΩ | 1 + 1 | Внешняя освещённость | Очень низкий |
| Поплавковый датчик уровня воды | 1 | Блокирует насос, когда бак пуст | Низкий |
| 4-канальный модуль реле 5 V (с оптопарами и JD-VCC) | 1 | Вентилятор, нагреватель и насос | Низкий |
| N-канальный MOSFET с логическим уровнем управления (IRLZ44N), резисторы 220 Ω и 100 kΩ | 1 комплект | Яркость светодиодной ленты (ШИМ) | Очень низкий |
| Погружной мини-насос 3–6 V и силиконовый шланг 1 м | 1 | Полив | Низкий |
| Компьютерный вентилятор 12 V, 80 мм | 1–2 | Вентиляция и удаление влаги | Низкий |
| PTC-нагреватель 12 V (10–30 W) и термопредохранитель | 1 + 1 | Обогрев | Низкий |
| Светодиодная лента 12 V (фитолента или холодный белый), около 1 м | 1 | Досветка | Низкий |
| Дисплей I2C LCD 16x2 | 1 | Экран состояния | Низкий – средний |
| Блок питания 12 V 5 A и понижающий преобразователь LM2596 | 1 + 1 | Питание | Средний |
| Диод 1N5819 или 1N4007, конденсатор 470 µF, винтовые клеммы, перемычки | – | Защита и соединения | Очень низкий |
| Прозрачный контейнер с крышкой или листы оргстекла, горшки, поддоны | – | Корпус теплицы | Средний |
Цены у продавцов сильно различаются, поэтому вместо точных цифр — примерный бюджет. Без учёта платы базовая версия (DHT22, датчик влажности почвы, LDR, 2-канальное реле, насос и вентилятор на 5 V) обычно обходится примерно в 25–40 $. Полная версия с нагревателем, светодиодной лентой, LCD, блоком питания 12 V, LM2596, поплавковым датчиком и корпусом обычно выходит около 70–120 $; основная часть бюджета уходит на плату, блок питания, дисплей и корпус. Если есть стартовый набор Arduino, плата, макетная плата, провода, LDR, резисторы, а часто и LCD уже в наличии. При скромном бюджете оставь нагреватель и светодиоды на второй этап: полив и вентиляция сами по себе уже дают осмысленный рабочий проект.
Датчики: DHT22, датчик влажности почвы и LDR
Для теплицы советуем DHT22 (AM2302). Он немного дороже DHT11, зато измеряет температуру от −40 до 80 °C с точностью ±0,5 °C и влажность во всём диапазоне 0–100% с точностью около ±2%; DHT11 точен лишь до ±2 °C в пределах от 0 до 50 °C и не видит влажность выше 90%. Утром влажность в теплице может подняться выше 90%, а зимой температура может опуститься ниже нуля, так что DHT22 подходит гораздо лучше. Опрашивать датчик можно не чаще одного раза в 2 секунды, поэтому код читает его каждые 2,5 секунды. Если поставить датчик туда, где на него прямо светит солнце или светодиоды, его корпус нагреется и он будет показывать на несколько градусов больше, чем температура воздуха; этого не случится, если затенить датчик белым колпачком с отверстиями. Подключение и библиотеки описаны в статье о датчиках температуры и влажности DHT11 и DHT22.

Для влажности почвы выбирай ёмкостный датчик. У дешёвых резистивных датчиков (YL-69, FC-28) два металлических щупа напрямую контактируют с почвой, и при постоянном питании их за несколько недель разъедает электролиз; для системы, которая будет работать месяцами, они не годятся. Электрод ёмкостного датчика покрыт защитным слоем, так что металл с почвой не соприкасается. Выход у него аналоговый и уменьшается по мере увлажнения почвы: на Uno датчик в воздухе даёт примерно 500–600, а опущенный в воду — около 250–300. Эти числа отличаются от датчика к датчику, поэтому перед загрузкой основного кода посмотри в мониторе порта значения своего датчика в воздухе и в воде и впиши их в константы SOIL_DRY_RAW и SOIL_WET_RAW. Код переводит эти два значения в 0–100% с помощью map(). Подробнее — в статье о датчике влажности почвы.
Для освещённости хватит фоторезистора LDR (GL5528) из набора. Подключи LDR делителем напряжения с резистором 10 kΩ: 5V → LDR → A1 → 10 kΩ → GND. Чем больше света, тем меньше сопротивление LDR и тем выше значение на A1; код тоже переводит его в 0–100% по двум числам, измеренным в темноте и на солнце. LDR — не точный люксметр, но на вопрос «стемнело ли, пасмурно или солнечно?» он отвечает надёжно. Для более точных измерений освещённости можно перейти на I2C-датчик освещённости, например BH1750. Расчёт делителя удобно сделать в утилите делителя напряжения, а подробности есть в статье о фоторезисторе LDR.
Исполнительные устройства: реле или MOSFET?
Пин Arduino может отдать всего около 20 mA, а вентилятор, нагреватель, насос и светодиодная лента потребляют сотни миллиампер или даже несколько ампер. Поэтому каждая нагрузка подключается либо через реле, либо через MOSFET. Реле — это механический выключатель: оно включает и выключает нагрузку в полностью отдельной цепи, переменного или постоянного тока, но умеет только «вкл/выкл», щёлкает при каждом переключении, а ресурс его контактов ограничен. MOSFET — бесшумный электронный ключ; он работает только с нагрузками постоянного тока, но переключается сотни раз в секунду и поэтому может задавать яркость и скорость с помощью ШИМ (PWM). В этом проекте для нагревателя, вентилятора и насоса используется 4-канальный модуль реле, а для яркости светодиодной ленты — MOSFET.
| Нагрузка | Типичное значение | Чем управлять | Почему? |
|---|---|---|---|
| PTC-нагреватель 12 V | 10–30 W, примерно 1–2,5 A | Реле (IN2) | Большой ток, нужно только вкл/выкл |
| Вентилятор 12 V, 80 мм | 0,1–0,2 A | Реле (IN1) или MOSFET | Реле проще; MOSFET позволяет ещё и регулировать скорость ШИМ |
| Погружной мини-насос 3–6 V | 0,1–0,25 A, при запуске больше | Реле (IN3) или MOSFET с диодом | Работает короткими импульсами; помехи от мотора нужно подавлять |
| Светодиодная лента 12 V (5050, 60 светодиодов/м) | Около 1,2 A на метр | MOSFET с логическим уровнем управления | Регулировка яркости ШИМ и бесшумная работа |

В зависимости от высоты шланга погружной мини-насос подаёт примерно 1–1,5 литра в минуту, так что 3-секундный импульс даёт горшку 50–75 мл воды. Поэтому насос не работает непрерывно, «пока почва не намокнет», а включается короткими импульсами: полить 3 секунды, подождать 60 секунд, пока вода впитается, и снова измерить. Если бы датчик опрашивался снова раньше, чем до него дойдёт вода, почва всё ещё выглядела бы сухой, и насос работал бы, пока горшок не переполнится. Насос, работающий всухую, перегревается и выходит из строя за считаные минуты; поплавковый датчик в баке блокирует насос, когда вода заканчивается. При каждой остановке мотора его обмотка выдаёт выброс обратного напряжения; диод, включённый в обратном направлении параллельно клеммам насоса (1N5819 или 1N4007, полоской к плюсу), защищает от этих помех и контакты реле, и Arduino. Выбор насоса и способы управления им описаны в статье о мини-насосе для воды.
Для обогрева используй PTC-нагреватель на 12 V. Сопротивление керамических PTC-элементов (с положительным температурным коэффициентом) растёт по мере нагрева, поэтому температура их поверхности сама ограничивается на заданном уровне; это гораздо безопаснее открытой раскалённой спирали. Для пластикового корпуса выбирай низкотемпературную модель, у которой поверхность нагревается не выше примерно 80 °C. Нагреватель на 24 W при 12 V по формуле P = V × I потребляет 2 A; в первые секунды, пока он холодный, ток несколько выше. Для освещения подойдёт красно-синяя фитолента или светодиодная лента холодного белого света; для фотосинтеза растения используют в основном красный (около 660 нм) и синий (около 450 нм) свет. Управляй лентой через MOSFET с логическим уровнем управления, например IRLZ44N: затвор подключается к D9 через резистор 220 Ω и к GND через резистор 100 kΩ; плюс ленты идёт на 12 V, минус — на сток MOSFET, а исток — на GND. Обычные модули MOSFET вроде IRF520 при напряжении на затворе 5 V открываются не полностью и греются при нагрузке в несколько ампер; подробности — в статье об использовании MOSFET с Arduino. Ток и мощность можно посчитать в калькуляторе закона Ома.
План питания: шины 12 V и 5 V
Самые частые проблемы умных теплиц («Arduino перезагружается, когда работает насос», «на LCD появляются странные символы») связаны с питанием. Самое чистое решение — взять две шины от одного блока питания 12 V 5 A. Шина 12 V напрямую питает нагреватель, вентиляторы и светодиодную ленту, а также идёт на гнездо питания (DC) Arduino Uno. Те же 12 V подаются на понижающий преобразователь LM2596, настроенный на 5 V; от этой шины 5 V работают катушки реле, насос и LCD. Если снять перемычку JD-VCC на модуле реле, ток катушек полностью берётся от LM2596, а со стороны Arduino зажигаются только светодиоды оптопар. Датчики (DHT22, датчик влажности почвы и делитель с LDR) питай от собственного пина 5V Arduino: analogRead() измеряет относительно собственных 5 V платы, поэтому питание аналоговых датчиков от того же напряжения делает показания стабильными. Всегда соединяй вместе GND блока питания, LM2596 и Arduino.
| Нагрузка | Шина | Примерный ток |
|---|---|---|
| PTC-нагреватель (24 W) | 12 V | 2 A (в первый момент, пока он холодный, больше) |
| Светодиодная лента (1 м) | 12 V | 1–1,2 A |
| Два вентилятора 80 мм | 12 V | 0,2–0,4 A |
| Arduino Uno и датчики (через гнездо DC) | 12 V | Около 0,06–0,08 A |
| Вход LM2596 (когда сторона 5 V потребляет 1 A) | 12 V | Около 0,5 A |
| Модуль реле (включены три реле) | 5 V | 0,2–0,25 A |
| Погружной мини-насос | 5 V | 0,1–0,25 A (при запуске больше) |
| I2C LCD (подсветка включена) | 5 V | 20–30 mA |
Даже если всё работает одновременно, общий ток составляет около 4–4,5 A, так что блок питания 12 V 5 A (60 W) даёт достаточный запас. На практике нагреватель и вентилятор по температуре никогда не работают одновременно, потому что между их порогами большой промежуток. Прежде чем подключать LM2596 к схеме, измерь его выход мультиметром и выставь 5,0 V подстроечным резистором на плате: прямо из коробки он может пропускать почти все 12 V. Во время загрузки кода Arduino можно питать от USB, а в работе — от гнезда DC; для Uno одновременное подключение обоих не страшно. Подробности — в руководстве по питанию Arduino.
Подключение и таблица пинов
В таблице ниже — пины, которые используют три программы из этого руководства. На Uno MOSFET, управляющий светодиодной лентой, подключён к D9 с поддержкой ШИМ; на Uno ШИМ есть только на D3, D5, D6, D9, D10 и D11. Поплавковому датчику и реле ШИМ не нужен. LCD использует только шину I2C (A4 и A5), поэтому занимает очень мало пинов; на Uno остаются свободными A2, A3 и несколько цифровых пинов — для второго датчика почвы или кнопки. На ESP32 аналоговые датчики подключены к пинам ADC1 (GPIO 34 и 35), которые продолжают работать при включённом Wi-Fi. Расположение пинов своей платы можно посмотреть в утилите распиновки.
| Подключение | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DHT22, вывод DATA | D2 | GPIO 4 | GP15 |
| Поплавковый датчик (второй провод на GND) | D3 | GPIO 13 | GP14 |
| Реле IN3: насос | D4 | GPIO 25 | GP18 |
| Реле IN1: вентилятор | D7 | GPIO 26 | GP16 |
| Реле IN2: нагреватель | D8 | GPIO 27 | GP17 |
| Затвор MOSFET (через 220 Ω): светодиоды | D9 | GPIO 18 | GP19 |
| Датчик влажности почвы, вывод AOUT | A0 | GPIO 34 | GP26 |
| Средняя точка делителя с LDR | A1 | GPIO 35 (делитель от 3V3) | GP27 (делитель от 3V3) |
| I2C LCD: выводы SDA / SCL | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Питание датчиков | 5V | 3V3 | 3V3 |
Пороги и гистерезис: почему реле постоянно щёлкают?
Представь, что вентилятор управляется одним порогом: «включить, если температура выше 30 °C, иначе выключить». Пока температура колеблется между 29,9 и 30,1 °C (а из-за шумов датчика так бывает часто), реле переключается при каждом измерении. Изнашиваются и контакты реле, и мотор вентилятора, а в случае нагревателя при каждом включении лишнюю нагрузку получают ещё и проводка, и блок питания. Решение — гистерезис: для включения и выключения используются два разных порога. Вентилятор включается при 30 °C, но выключается, только когда температура опустится до 27 °C. В мёртвой зоне шириной 3 градуса между ними система сохраняет последнее состояние. По тому же принципу работают комнатные термостаты котлов и холодильники.
| Управление | Включается | Выключается | Мёртвая зона |
|---|---|---|---|
| Вентилятор (температура) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Вентилятор (влажность) | ≥ 85% | ≤ 75% | 10% |
| Нагреватель | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Полив | Почва ≤ 35% | Почва ≥ 50% | 15% |
| Светодиодное освещение | Свет ≤ 25% | Свет ≥ 35% или через 4 часа | 10% |
Ширина мёртвой зоны — это компромисс: если она слишком узкая, реле всё равно переключается часто, а если слишком широкая, температура уходит далеко от цели. Для начала хорошо подходят около 2–3 °C для температуры, 8–10% для влажности воздуха и 10–15% для влажности почвы. Кроме того, между 19 °C, где выключается нагреватель, и 30 °C, где включается вентилятор, оставлен большой промежуток, чтобы нагреватель и вентилятор по температуре никогда не работали друг против друга. В коде гистерезис живёт в маленькой функции hysteresis(), чтобы не писать его заново для каждого управления: если порог включения больше порога выключения, это значит «включать при росте» (вентилятор), если меньше — «включать при падении» (нагреватель, полив, светодиоды). Если хочется ещё и сгладить выбросы в данных датчиков, загляни в статью о фильтрации данных с датчиков.
Структура кода: измерить, решить, выполнить, показать
Самое важное правило кода теплицы: ни одна задача не ждёт другую. Если после насоса ждать целую минуту с delay(60000), всё это время вентилятор не будет работать, а дисплей не будет обновляться. Поэтому код состоит из маленьких функций, которые отсчитывают время через millis() и отвечают каждая за одно дело; loop() вызывает их по очереди тысячи раз в секунду, и каждая функция что-то делает, только когда пришло её время. Полив — это маленький конечный автомат с тремя состояниями: ожидание (IDLE), полив (PUMPING) и впитывание воды (SOAKING). Все настройки собраны константами в начале кода, так что ради изменения порога не придётся копаться во всём коде. Почему выбран такой подход, объясняют статьи о millis() и delay() и о конечных автоматах на Arduino. Кроме того, в коде есть такие меры безопасности:
- Безопасный запуск: значение «выключено» записывается в пины реле ещё до вызова pinMode(), поэтому реле не щёлкают даже на мгновение, пока Arduino загружается.
- Отказ датчика: если DHT22 не удаётся прочитать три раза подряд, код переходит в безопасное состояние: выключает нагреватель, включает вентилятор и показывает на LCD «Sensor hatasi» (ошибка датчика). Система, которая не знает температуру, ни в коем случае не должна оставлять нагреватель включённым.
- Защита бака: когда поплавковый датчик сообщает, что бак пуст, насос вообще не запускается, а если уже работает — сразу останавливается.
- Защита от перелива: если за один цикл полива после шести импульсов влажность почвы так и не дошла до 50%, возможно, датчик вылез из почвы или соскочил шланг; код прекращает полив и показывает «Sulama hatasi» (ошибка полива). Предупреждение снимается само, как только почва снова становится влажной.
- Запись данных: каждые 10 секунд код выводит показания в монитор порта строкой через запятую (CSV); эти строки можно скопировать и построить по ним графики в Excel.
Пошаговая сборка и проверка
- Подготовь корпус: прорежь отверстия для вентилятора, притока воздуха, шланга и проводов, а коробку с электроникой поставь снаружи теплицы.
- Собери шины питания: подключи блок питания 12 V к винтовой клемме, выставь на LM2596 5,0 V по мультиметру и соедини все GND.
- Подключи I2C LCD и выведи на него какой-нибудь текст. Если дисплей ничего не показывает, попробуй скетч-сканер и подстроечный резистор контраста из статьи об I2C LCD.
- Добавляй датчики по одному и проверяй их в мониторе порта: запиши показания DHT22, сырое значение датчика почвы в воздухе и в воде и значение LDR в темноте и на солнце.
- Сначала проверь реле без нагрузки: следи за их светодиодами и прислушивайся к щелчку. Потом подключи вентилятор, насос (при полном баке) и в последнюю очередь нагреватель.
- Подключи светодиодную ленту через MOSFET и меняй значение analogWrite(), чтобы увидеть, как меняется яркость.
- Загрузи основной код, впиши свои калибровочные значения в константы и несколько часов погоняй систему под присмотром: нет ли ошибочного полива, реле, которое всё время щёлкает, или детали, которая слишком греется?
Полный код для Arduino Uno
Чтобы скомпилировать код, установи через «Менеджер библиотек» (Library Manager) библиотеку DHT sensor library от Adafruit (вместе с её зависимостью Adafruit Unified Sensor) и библиотеку LiquidCrystal I2C автора Frank de Brabander; Wire входит в состав Arduino IDE. Адрес LCD обычно 0x27, на некоторых модулях — 0x3F. Перед загрузкой впиши измеренные калибровочные значения в константы SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW и LDR_BRIGHT_RAW. Если твой поплавковый датчик в воде размыкается, а не замыкается, задай TANK_OK значение HIGH; если модуль реле с активным HIGH, поменяй местами значения RELAY_ON и RELAY_OFF.
1#include <Wire.h>
2#include <LiquidCrystal_I2C.h>
3#include <DHT.h>
4
5// Pinler / Pins
6const byte DHT_PIN = 2, FLOAT_PIN = 3, PUMP_RELAY = 4, FAN_RELAY = 7, HEATER_RELAY = 8;
7const byte LED_PIN = 9; // MOSFET gate (PWM)
8const byte SOIL_PIN = A0, LDR_PIN = A1;
9
10// Kalibrasyon: kendi ölçümlerini yaz / Calibration: write your own readings
11const int SOIL_DRY_RAW = 520, SOIL_WET_RAW = 260; // havada ve suda / in air and in water
12const int LDR_DARK_RAW = 60, LDR_BRIGHT_RAW = 900; // karanlık ve güneş / dark and sunlight
13
14// Eşikler (açma, kapama) / Thresholds (on, off)
15const float FAN_ON_C = 30, FAN_OFF_C = 27; // sıcaklıkta fan / fan on heat
16const float HUM_ON = 85, HUM_OFF = 75; // nemde fan / fan on humidity
17const float HEAT_ON_C = 16, HEAT_OFF_C = 19; // ısıtıcı / heater
18const float SOIL_DRY = 35, SOIL_OK = 50; // sulama başla, dur / start, stop watering
19const float DARK_ON = 25, DARK_OFF = 35; // ışık % / light %
20
21// Zamanlar / Timing
22const unsigned long READ_MS = 2500; // DHT22: en az 2 sn / at least 2 s
23const unsigned long PUMP_MS = 3000; // tek sulama darbesi / one watering pulse
24const unsigned long SOAK_MS = 60000; // suyun yayılması / let the water soak in
25const byte MAX_PULSES = 6; // fazlası arıza / more means a fault
26const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // günü 4 saat uzat / extend the day by 4 h
27const byte LED_LEVEL = 200; // LED parlaklığı 0-255 / LED brightness 0-255
28const byte RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
29const byte TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
30
31DHT dht(DHT_PIN, DHT22);
32LiquidCrystal_I2C lcd(0x27, 16, 2);
33
34float tempC = NAN, hum = NAN, soil = 0, light = 0;
35byte dhtErrors = 0, pulses = 0, page = 0;
36bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
37bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
38unsigned long lastRead = 0, lastLcd = 0, lastLog = 0, pumpAt = 0, darkSince = 0;
39
40enum PumpState { IDLE, PUMPING, SOAKING };
41PumpState pumpState = IDLE;
42
43// onAt > offAt: yükselince aç (fan) / turn on when rising (fan)
44// onAt < offAt: düşünce aç (ısıtıcı) / turn on when falling (heater)
45bool hysteresis(bool state, float value, float onAt, float offAt) {
46 if (onAt > offAt) {
47 if (value >= onAt) return true;
48 if (value <= offAt) return false;
49 } else {
50 if (value <= onAt) return true;
51 if (value >= offAt) return false;
52 }
53 return state; // ölü bölgede durumu koru / keep state in the dead band
54}
55
56void setRelay(byte pin, bool on) {
57 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
58}
59
60float readPercent(byte pin, int raw0, int raw100) {
61 long sum = 0;
62 for (byte i = 0; i < 10; i++) sum += analogRead(pin); // 10 ölçüm ortalaması / average of 10
63 long pct = map(sum / 10, raw0, raw100, 0, 100);
64 return constrain(pct, 0, 100);
65}
66
67void setup() {
68 Serial.begin(9600);
69 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT); // açılışta kapalı / off at boot
70 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
71 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
72 pinMode(LED_PIN, OUTPUT);
73 pinMode(FLOAT_PIN, INPUT_PULLUP);
74 dht.begin();
75 lcd.init();
76 lcd.backlight();
77 lcd.print(F("BlueGrays Sera"));
78 Serial.println(F("sn,sicaklik,nem,toprak,isik,fan,isitici,pompa,led")); // CSV başlığı / CSV header
79 delay(2000); // DHT22 açılış süresi / DHT22 start-up time
80 readSensors();
81 lastRead = millis();
82}
83
84void loop() {
85 unsigned long now = millis();
86 if (now - lastRead >= READ_MS) {
87 lastRead = now;
88 readSensors();
89 }
90 controlClimate();
91 controlLight(now);
92 controlPump(now);
93 updateLcd(now);
94 logCsv(now);
95}
96
97// 1) Ölç / Measure
98void readSensors() {
99 float t = dht.readTemperature();
100 float h = dht.readHumidity();
101 if (isnan(t) || isnan(h)) {
102 if (dhtErrors < 255) dhtErrors++; // okuma hatası / read error
103 } else {
104 tempC = t;
105 hum = h;
106 dhtErrors = 0;
107 }
108 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
109 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
110 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
111}
112
113// 2) Fan ve ısıtıcı / Fan and heater
114void controlClimate() {
115 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
116 heaterOn = false;
117 fanByTemp = true;
118 } else {
119 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
120 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
121 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
122 }
123 fanOn = fanByTemp || fanByHum;
124 setRelay(FAN_RELAY, fanOn);
125 setRelay(HEATER_RELAY, heaterOn);
126}
127
128// 3) LED aydınlatma / LED grow light
129void controlLight(unsigned long now) {
130 bool wasDark = isDark;
131 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
132 if (isDark && !wasDark) darkSince = now; // akşam oldu / dusk detected
133 ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);
134 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
135}
136
137// 4) Sulama: darbe + bekleme / Watering: pulse + soak
138void controlPump(unsigned long now) {
139 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
140 if (!needWater) {
141 pulses = 0;
142 pumpFault = false; // toprak ıslandı / soil is wet again
143 }
144 switch (pumpState) {
145 case IDLE:
146 if (needWater && tankOk && !pumpFault) {
147 if (pulses >= MAX_PULSES) {
148 pumpFault = true; // nem yükselmiyor / moisture is not rising
149 } else {
150 pumpState = PUMPING;
151 pumpAt = now;
152 pulses++;
153 }
154 }
155 break;
156 case PUMPING:
157 if (now - pumpAt >= PUMP_MS || !tankOk) {
158 pumpState = SOAKING;
159 pumpAt = now;
160 }
161 break;
162 case SOAKING:
163 if (now - pumpAt >= SOAK_MS) pumpState = IDLE;
164 break;
165 }
166 setRelay(PUMP_RELAY, pumpState == PUMPING);
167}
168
169// 5) LCD: üç sayfa / three pages
170void updateLcd(unsigned long now) {
171 if (now - lastLcd < 3000) return;
172 lastLcd = now;
173 page = (page + 1) % 3;
174 lcd.clear();
175 if (page == 0) {
176 lcd.print(F("Sicaklik: "));
177 if (isnan(tempC)) lcd.print(F("--")); else lcd.print(tempC, 1);
178 lcd.print('C');
179 lcd.setCursor(0, 1);
180 lcd.print(F("Hava nemi: %"));
181 if (isnan(hum)) lcd.print(F("--")); else lcd.print(hum, 0);
182 } else if (page == 1) {
183 lcd.print(F("Toprak: %"));
184 lcd.print(soil, 0);
185 lcd.setCursor(0, 1);
186 lcd.print(F("Isik: %"));
187 lcd.print(light, 0);
188 } else {
189 lcd.print(F("F:")); lcd.print(fanOn);
190 lcd.print(F(" I:")); lcd.print(heaterOn);
191 lcd.print(F(" P:")); lcd.print(pumpState == PUMPING);
192 lcd.print(F(" L:")); lcd.print(ledOn);
193 lcd.setCursor(0, 1);
194 if (dhtErrors >= 3) lcd.print(F("Sensor hatasi"));
195 else if (!tankOk) lcd.print(F("Depo bos!"));
196 else if (pumpFault) lcd.print(F("Sulama hatasi"));
197 else lcd.print(F("Sistem normal"));
198 }
199}
200
201// 6) Seri port CSV kaydı / Serial CSV log
202void logCsv(unsigned long now) {
203 if (now - lastLog < 10000) return;
204 lastLog = now;
205 Serial.print(now / 1000); Serial.print(',');
206 Serial.print(tempC, 1); Serial.print(',');
207 Serial.print(hum, 0); Serial.print(',');
208 Serial.print(soil, 0); Serial.print(',');
209 Serial.print(light, 0); Serial.print(',');
210 Serial.print(fanOn); Serial.print(',');
211 Serial.print(heaterOn); Serial.print(',');
212 Serial.print(pumpState == PUMPING); Serial.print(',');
213 Serial.println(ledOn);
214}| Функция | Задача | Как часто? |
|---|---|---|
| readSensors() (чтение датчиков) | Читает DHT22, влажность почвы, LDR и поплавковый датчик; переводит аналоговые значения в 0–100% по среднему из 10 измерений | Каждые 2,5 секунды |
| controlClimate() (климат) | Управляет вентилятором по гистерезису температуры или влажности, а нагревателем — по гистерезису температуры; при отказе датчика переходит в безопасное состояние | В каждом проходе цикла |
| controlLight() (свет) | Включает светодиоды с ШИМ, когда темнеет, и выключает их не позже чем через 4 часа | В каждом проходе цикла |
| controlPump() (насос) | Конечный автомат полива: полить 3 с, подождать 60 с, снова измерить; с защитой бака и защитой от перелива | В каждом проходе цикла |
| updateLcd() (дисплей) | Переключает три страницы: показания воздуха, влажность почвы и свет, состояние устройств | Каждые 3 секунды |
| logCsv() (журнал CSV) | Выводит показания и состояния в монитор порта строкой CSV | Каждые 10 секунд |
На первой странице LCD — температура и влажность воздуха, на второй — влажность почвы и уровень освещённости, на третьей — состояние устройств: «F:1 I:0 P:0 L:1» означает, что вентилятор работает, нагреватель (I — от турецкого ısıtıcı, «нагреватель») и насос выключены, а светодиоды горят. В нижней строке появляется одно из сообщений: «Sistem normal» (всё в порядке), «Depo bos!» (бак пуст), «Sulama hatasi» (ошибка полива) или «Sensor hatasi» (ошибка датчика); эти строки в коде легко перевести. После компиляции этот код занимает около 31% памяти программ Uno и 25% её ОЗУ, так что места с запасом хватит на второй датчик почвы, зуммер или модуль часов реального времени (RTC). Поскольку у Uno нет настоящих часов, правило для светодиодов работает как «не больше 4 часов после наступления темноты» — хороший способ продлить короткие зимние дни. Если нужно, чтобы светодиоды включались каждый день в заданное время, добавь модуль RTC или перейди на версию для ESP32 ниже.
Мониторинг и полив с телефона через ESP32

У Uno нет Wi-Fi. Если хочется видеть состояние теплицы на телефоне и поливать удалённо, замени «мозг» на ESP32. Код ниже переносит ту же логику управления на ESP32 и запускает небольшой веб-сервер. Подключи телефон к той же сети Wi-Fi, открой в браузере IP-адрес, показанный в мониторе порта, и увидишь температуру, влажность, влажность почвы, освещённость, состояние бака и то, включено или выключено каждое устройство; страница сама обновляется каждые 10 секунд. Кнопка Water now («Полить сейчас») один раз включает насос на 3 секунды, если в баке есть вода. При запуске ESP32 получает время из интернета (NTP), поэтому светодиоды включаются только с 06:00 до 20:00 и только когда снаружи темно, а ночью растения отдыхают.
1#include <WiFi.h>
2#include <WebServer.h>
3#include <DHT.h>
4#include <time.h>
5
6const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8
9// Pinler / Pins
10const int DHT_PIN = 4, FLOAT_PIN = 13, PUMP_RELAY = 25, FAN_RELAY = 26, HEATER_RELAY = 27;
11const int LED_PIN = 18; // MOSFET gate (PWM)
12const int SOIL_PIN = 34, LDR_PIN = 35; // ADC1: Wi-Fi açıkken çalışır / works with Wi-Fi on
13
14// Kalibrasyon (0-4095): kendin ölç / Calibration (0-4095): measure your own
15const int SOIL_DRY_RAW = 2800, SOIL_WET_RAW = 1200;
16const int LDR_DARK_RAW = 200, LDR_BRIGHT_RAW = 3800;
17
18// Eşikler (açma, kapama) / Thresholds (on, off)
19const float FAN_ON_C = 30, FAN_OFF_C = 27, HUM_ON = 85, HUM_OFF = 75;
20const float HEAT_ON_C = 16, HEAT_OFF_C = 19, SOIL_DRY = 35, SOIL_OK = 50;
21const float DARK_ON = 25, DARK_OFF = 35;
22const int LIGHT_FROM = 6, LIGHT_TO = 20; // LED yalnız 06:00-20:00 / LEDs only 06:00-20:00
23
24const unsigned long READ_MS = 2500, PUMP_MS = 3000, SOAK_MS = 60000;
25const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // saat yoksa yedek kural / fallback without a clock
26const int MAX_PULSES = 6, LED_LEVEL = 200;
27const int RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
28const int TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
29
30DHT dht(DHT_PIN, DHT22);
31WebServer server(80);
32
33float tempC = NAN, hum = NAN, soil = 0, light = 0;
34int dhtErrors = 0, pulses = 0;
35bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
36bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
37unsigned long lastRead = 0, pumpAt = 0, darkSince = 0;
38
39enum PumpState { IDLE, PUMPING, SOAKING };
40PumpState pumpState = IDLE;
41
42bool hysteresis(bool state, float value, float onAt, float offAt) {
43 if (onAt > offAt) { // yükselince aç / turn on when rising
44 if (value >= onAt) return true;
45 if (value <= offAt) return false;
46 } else { // düşünce aç / turn on when falling
47 if (value <= onAt) return true;
48 if (value >= offAt) return false;
49 }
50 return state;
51}
52
53void setRelay(int pin, bool on) {
54 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
55}
56
57float readPercent(int pin, int raw0, int raw100) {
58 long sum = 0;
59 for (int i = 0; i < 16; i++) sum += analogRead(pin); // 16 ölçüm ortalaması / average of 16
60 long pct = map(sum / 16, raw0, raw100, 0, 100);
61 return constrain(pct, 0, 100);
62}
63
64int hourNow() {
65 struct tm t;
66 if (!getLocalTime(&t, 0)) return -1; // saat henüz yok / time not set yet
67 return t.tm_hour;
68}
69
70void readSensors() {
71 float t = dht.readTemperature();
72 float h = dht.readHumidity();
73 if (isnan(t) || isnan(h)) {
74 dhtErrors++;
75 } else {
76 tempC = t;
77 hum = h;
78 dhtErrors = 0;
79 }
80 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
81 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
82 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
83}
84
85void controlClimate() {
86 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
87 heaterOn = false;
88 fanByTemp = true;
89 } else {
90 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
91 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
92 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
93 }
94 fanOn = fanByTemp || fanByHum;
95 setRelay(FAN_RELAY, fanOn);
96 setRelay(HEATER_RELAY, heaterOn);
97}
98
99void controlLight(unsigned long now) {
100 bool wasDark = isDark;
101 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
102 if (isDark && !wasDark) darkSince = now;
103 int h = hourNow();
104 if (h >= 0) ledOn = isDark && h >= LIGHT_FROM && h < LIGHT_TO; // saat kuralı / clock rule
105 else ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS); // yedek kural / fallback rule
106 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
107}
108
109void controlPump(unsigned long now) {
110 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
111 if (!needWater) {
112 pulses = 0;
113 pumpFault = false;
114 }
115 if (pumpState == IDLE && needWater && tankOk && !pumpFault) {
116 if (pulses >= MAX_PULSES) {
117 pumpFault = true; // nem yükselmiyor / moisture is not rising
118 } else {
119 pumpState = PUMPING;
120 pumpAt = now;
121 pulses++;
122 }
123 } else if (pumpState == PUMPING && (now - pumpAt >= PUMP_MS || !tankOk)) {
124 pumpState = SOAKING;
125 pumpAt = now;
126 } else if (pumpState == SOAKING && now - pumpAt >= SOAK_MS) {
127 pumpState = IDLE;
128 }
129 setRelay(PUMP_RELAY, pumpState == PUMPING);
130}
131
132String row(const char* name, const String& value) {
133 return String("<tr><td>") + name + "</td><td><b>" + value + "</b></td></tr>";
134}
135
136String onOff(bool on) {
137 return on ? "ON" : "OFF";
138}
139
140void handleRoot() {
141 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
142 "<meta name='viewport' content='width=device-width,initial-scale=1'>"
143 "<meta http-equiv='refresh' content='10'><title>BlueGrays Sera</title>"
144 "<style>body{font-family:sans-serif;max-width:420px;margin:auto;padding:12px}"
145 "td{padding:8px;border-bottom:1px solid #ddd}"
146 "a{display:block;margin-top:16px;padding:16px;border-radius:10px;background:#2e7d32;"
147 "color:#fff;text-align:center;text-decoration:none;font-size:18px}</style>"
148 "</head><body><h2>BlueGrays Sera</h2><table>";
149 html += row("Sıcaklık / Temperature", String(tempC, 1) + " °C");
150 html += row("Hava nemi / Humidity", "%" + String(hum, 0));
151 html += row("Toprak nemi / Soil", "%" + String(soil, 0));
152 html += row("Işık / Light", "%" + String(light, 0));
153 html += row("Fan", onOff(fanOn));
154 html += row("Isıtıcı / Heater", onOff(heaterOn));
155 html += row("Pompa / Pump", onOff(pumpState == PUMPING));
156 html += row("LED", onOff(ledOn));
157 html += row("Depo / Tank", tankOk ? "OK" : "BOŞ / EMPTY");
158 if (pumpFault) html += row("Uyarı / Warning", "Sulama hatası / Watering fault");
159 html += "</table><a href='/water'>Şimdi sula / Water now</a></body></html>";
160 server.send(200, "text/html", html);
161}
162
163void handleWater() { // elle tek darbe / one manual pulse
164 if (pumpState == IDLE && tankOk) {
165 pumpState = PUMPING;
166 pumpAt = millis();
167 }
168 server.sendHeader("Location", "/");
169 server.send(303);
170}
171
172void setup() {
173 Serial.begin(115200);
174 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT);
175 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
176 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
177 pinMode(LED_PIN, OUTPUT);
178 pinMode(FLOAT_PIN, INPUT_PULLUP);
179 dht.begin();
180 WiFi.begin(ssid, password);
181 while (WiFi.status() != WL_CONNECTED) {
182 delay(500);
183 Serial.print('.');
184 }
185 Serial.println();
186 Serial.println(WiFi.localIP()); // bu adresi telefonda aç / open this address on your phone
187 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3; kendi saat dilimini yaz / set your own time zone
188 server.on("/", handleRoot);
189 server.on("/water", handleWater);
190 server.begin();
191 readSensors();
192 lastRead = millis();
193}
194
195void loop() {
196 server.handleClient();
197 unsigned long now = millis();
198 if (now - lastRead >= READ_MS) {
199 lastRead = now;
200 readSensors();
201 }
202 controlClimate();
203 controlLight(now);
204 controlPump(now);
205}Вот что меняется по сравнению с кодом для Uno: АЦП ESP32 12-битный и выдаёт значения от 0 до 4095, поэтому калибровку нужно провести заново; к тому же датчики питаются от 3V3, так что сырые значения тоже будут другими. Часовой пояс задаётся в строке configTime(); для Турции (UTC+3) мы написали 3 × 3600 секунд, и это же значение подходит для московского времени. Для пояса без перехода на летнее время достаточно поменять число часов, а для пояса с переходом замени эту строку на configTzTime() со строкой часового пояса POSIX, например configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org") для Великобритании. Если интернета нет или время ещё не получено, правило для светодиодов возвращается к правилу Uno «не больше 4 часов». У страницы нет пароля, так что насос может запустить любой в твоей домашней сети; добавить простой пароль с помощью функции authenticate() библиотеки WebServer — хорошая привычка. Если LCD нужен и на ESP32, функцию updateLcd() из кода для Uno можно перенести без изменений; SDA — это GPIO 21, SCL — GPIO 22.
| Способ | Доступ вне дома | Сложность | Для чего лучше всего |
|---|---|---|---|
| Веб-сервер на ESP32 (код из этого руководства) | Нет, только в той же сети | Средняя | Школьные презентации, показ без интернета |
| Blynk IoT | Да | Низкая | Готовые шкалы, графики и уведомления |
| Telegram-бот | Да | Средняя | Оповещения «бак пуст» и «слишком высокая температура» |
| Google Таблицы | Да | Средняя | Запись данных за несколько недель и графики |
| MQTT и Home Assistant | Да, нужна настройка | Высокая | Настоящая автоматизация дома и сада |
Для школьной презентации самый надёжный вариант — веб-сервер на ESP32: он не зависит от облачного сервиса и работает в собственной сети, даже если в зале нет интернета. Если хочется заглядывать в теплицу и вне дома, проще всего пойти через Blynk IoT с готовыми шкалами и графиками. Чтобы получать сообщение на телефон, когда бак опустел или температура стала опасной, загляни в руководство о Telegram-боте на ESP32; чтобы собрать данные за несколько недель для отчёта — в статью об ESP32 и Google Таблицах; а чтобы подключить теплицу к настоящей домашней автоматизации — в статью о MQTT и Home Assistant.
Версия на MicroPython для Raspberry Pi Pico
Ту же теплицу можно запустить и на Raspberry Pi Pico в MicroPython. В прошивку MicroPython для Pico уже встроен драйвер DHT, так что копировать дополнительные файлы не нужно. Аналоговые входы — только GP26, GP27 и GP28, а read_u16() возвращает значения от 0 до 65535; калибровочные числа измеряй с учётом этого. Pico работает от 3,3 V, поэтому датчики питай от 3V3, а модуль реле — от VBUS (5 V от USB) или от шины LM2596. Код ниже содержит все правила для вентилятора, нагревателя, светодиодов и полива и выводит значения в окно Shell среды Thonny. Если сохранить файл на Pico под именем main.py, плата будет работать, даже когда не подключена к компьютеру. Если у тебя Pico W с Wi-Fi, можно добавить способ из статьи о веб-сервере на Pico W, чтобы следить за значениями с телефона.
1from machine import Pin, ADC, PWM
2import dht
3import time
4
5sensor = dht.DHT22(Pin(15)) # GP15: DHT22 DATA
6soil_adc = ADC(26) # GP26: toprak nem AOUT / soil moisture AOUT
7ldr_adc = ADC(27) # GP27: LDR bölücü (3V3) / LDR divider (3V3)
8tank = Pin(14, Pin.IN, Pin.PULL_UP) # şamandıra: suyla 0 / float switch: 0 with water
9fan = Pin(16, Pin.OUT, value=1) # aktif LOW röleler kapalı başlar / active-LOW relays start off
10heater = Pin(17, Pin.OUT, value=1)
11pump = Pin(18, Pin.OUT, value=1)
12led = PWM(Pin(19)) # MOSFET gate
13led.freq(1000)
14
15SOIL_DRY_RAW, SOIL_WET_RAW = 50000, 22000 # kendin ölç / measure your own
16LDR_DARK_RAW, LDR_BRIGHT_RAW = 2000, 60000
17EXTRA_LIGHT_MS = 4 * 3600 * 1000 # günü 4 saat uzat / extend the day by 4 h
18
19
20def percent(adc, raw0, raw100):
21 raw = sum(adc.read_u16() for _ in range(10)) // 10
22 pct = (raw - raw0) * 100 // (raw100 - raw0)
23 return max(0, min(100, pct))
24
25
26def hysteresis(state, value, on_at, off_at):
27 if on_at > off_at: # yükselince aç / turn on when rising
28 if value >= on_at:
29 return True
30 if value <= off_at:
31 return False
32 else: # düşünce aç / turn on when falling
33 if value <= on_at:
34 return True
35 if value >= off_at:
36 return False
37 return state # ölü bölge / dead band
38
39
40def relay(pin, on):
41 pin.value(0 if on else 1)
42
43
44t = h = None
45errors = pulses = 0
46fan_t = fan_h = heat_on = need_water = dark = led_on = False
47pump_state, pump_at, dark_since = "idle", 0, 0
48soil = light = 0
49tank_ok = True
50time.sleep(2) # DHT22 açılış süresi / DHT22 start-up time
51last_read = time.ticks_add(time.ticks_ms(), -2500)
52
53while True:
54 now = time.ticks_ms()
55 if time.ticks_diff(now, last_read) >= 2500: # 2,5 sn'de bir ölç / measure every 2.5 s
56 last_read = now
57 try:
58 sensor.measure()
59 t, h = sensor.temperature(), sensor.humidity()
60 errors = 0
61 except OSError:
62 errors += 1 # okuma hatası / read error
63 soil = percent(soil_adc, SOIL_DRY_RAW, SOIL_WET_RAW)
64 light = percent(ldr_adc, LDR_DARK_RAW, LDR_BRIGHT_RAW)
65 tank_ok = tank.value() == 0
66
67 if errors >= 3 or t is None: # güvenli durum / safe state
68 heat_on, fan_t = False, True
69 else:
70 fan_t = hysteresis(fan_t, t, 30, 27)
71 fan_h = hysteresis(fan_h, h, 85, 75)
72 heat_on = hysteresis(heat_on, t, 16, 19)
73 relay(fan, fan_t or fan_h)
74 relay(heater, heat_on)
75
76 was_dark = dark
77 dark = hysteresis(dark, light, 25, 35)
78 if dark and not was_dark:
79 dark_since = now # akşam oldu / dusk detected
80 led_on = dark and time.ticks_diff(now, dark_since) < EXTRA_LIGHT_MS
81 led.duty_u16(50000 if led_on else 0)
82
83 need_water = hysteresis(need_water, soil, 35, 50)
84 if not need_water:
85 pulses = 0
86 print("T", t, "N", h, "Toprak", soil, "Isik", light, "Pompa", pump_state,
87 "Depo OK" if tank_ok else "Depo BOS", "HATA" if pulses >= 6 else "")
88
89 # Sulama: 3 sn darbe, 60 sn bekleme / Watering: 3 s pulse, 60 s soak
90 if pump_state == "idle" and need_water and tank_ok and pulses < 6:
91 pump_state, pump_at = "pump", now
92 pulses += 1
93 elif pump_state == "pump" and (time.ticks_diff(now, pump_at) >= 3000 or not tank_ok):
94 pump_state, pump_at = "soak", now
95 elif pump_state == "soak" and time.ticks_diff(now, pump_at) >= 60000:
96 pump_state = "idle"
97 relay(pump, pump_state == "pump")
98 time.sleep_ms(100)Советы для школьных презентаций и научных выставок
Умная теплица — настолько популярная тема, что жюри видит десятки похожих макетов. Выделиться поможет не просто демонстрация того, что система работает, а ответ на вопрос, подкреплённый измерениями. На научной выставке (например, TÜBİTAK 4006 в Турции) должны быть понятны цель, метод и ожидаемый результат; в исследовательских конкурсах (например, TÜBİTAK 2204) ждут сравнительного эксперимента с данными. Хороший исследовательский вопрос для этой теплицы: «Сколько воды экономит полив по влажности почвы по сравнению с поливом каждый день в одно и то же время и как это влияет на рост растений?»
- Создай две группы: раздели рассаду одного вида и одной высоты на две группы. Одну группу поливай с помощью системы с датчиком, другую — вручную, одинаковым количеством воды каждый день; свет и температура для обеих групп должны быть одинаковыми.
- Измеряй и записывай: веди таблицу с недельным расходом воды (по замерам бака, в мл), высотой растений (см) и количеством листьев. CSV-вывод кода или журнал в Google Таблицах бесплатно даст тебе график температуры и влажности.
- Нарисуй структурную схему: датчики слева, Arduino в центре, исполнительные устройства справа; подпиши стрелки правилами («почва < 35% → насос»). Жюри не будет читать твой код, но эту схему запомнит.
- Объясни гистерезис: два графика, показывающие, сколько раз за час включается и выключается вентилятор с одним порогом и вентилятор с гистерезисом, — лучший способ показать жюри инженерную сторону проекта.
- Напиши сценарий живой демонстрации: согрей DHT22 в ладони, чтобы запустился вентилятор, переставь датчик почвы в сухой горшок, чтобы показать импульсы насоса, закрой LDR рукой, чтобы загорелись светодиоды. Каждая демонстрация — не дольше 10–20 секунд.
- Свяжи проект с реальной жизнью: расскажи о настоящих проблемах — расходе воды и энергии в теплицах, грибковых болезнях из-за избыточной влажности, нагрузке на фермеров — и покажи цифрами, какие из них решает твоя система.
- Подготовь запасной план: сними работающую систему на видео; возьми с собой запасные провода, запасной насос и блок питания; запиши калибровочные значения и настройки Wi-Fi на листке бумаги.
- Подготовься к вопросам жюри: держи наготове короткие ответы на вопросы «Что будет, если датчик откажет?», «Что случится с растениями при отключении электричества?» и «Как масштабировать это до настоящей теплицы?». Лучшие ответы на них — безопасное состояние и защита от перелива в коде.
Частые ошибки и как их исправить
| Признак | Вероятная причина | Решение |
|---|---|---|
| Arduino перезагружается или на LCD появляется мусор, когда работает насос | Насос и реле питаются от пина 5V Arduino, помехи от мотора | Питай насос и катушки реле от LM2596, соедини GND, поставь у насоса обратный диод и конденсатор 470 µF |
| Реле всё время щёлкают | Используется один порог или показания шумные | Используй отдельные пороги включения и выключения (гистерезис) и усредняй аналоговые показания |
| Вода продолжает течь после остановки насоса | Эффект сифона: конец шланга ниже уровня воды в баке | Поставь бак ниже или сделай маленькое воздушное отверстие в верхней точке шланга |
| Влажность почвы всегда 0% или всегда 100% | Неверные калибровочные значения или AOUT подключён не к тому пину | Измерь сырое значение датчика в воздухе и в воде и впиши эти значения в константы |
| Светодиоды то включаются, то выключаются | LDR видит свет светодиодов | Вынеси LDR за пределы теплицы, лицом к небу |
| DHT22 возвращает nan | Не тот пин, нет подтягивающего резистора или опрос чаще раза в 2 секунды | Проверь пин, используй модуль или поставь 10 kΩ между DATA и VCC |
| Температура выше, чем должна быть | На датчик прямо светит солнце или светодиоды | Затени его белым колпачком с отверстиями и отодвинь от нагревателя |
| Капли воды и плесень на стенках | Слишком высокая влажность, слишком слабая вентиляция | Снизь порог влажности, увеличь приточное отверстие, поливай меньше |
| На ESP32 показания почвы сбиваются при включении Wi-Fi | Датчик подключён к пину ADC2 | Перенеси его на пин ADC1 в диапазоне GPIO 32–39 |
| С ESP32 реле никогда не отпускает | HIGH 3,3 V не может выключить модуль 5 V с активным LOW | Раздели питание через JD-VCC или используй модуль реле, срабатывающий от 3,3 V |
Проект умной теплицы объединяет в одной работе чтение датчиков, калибровку, принятие решений с гистерезисом, управление реле и MOSFET, планирование питания и беспроводной мониторинг, а в итоге выращивает что-то по-настоящему живое — растение. Начни с полива и вентиляции, проверь каждую часть отдельно, а затем добавь нагреватель, светодиоды и телефон. В приложении BlueGrays есть пошаговые проекты со схемами и кодом: «Температура и влажность с DHT11», «Измерение освещённости с LDR», «Умная станция полива» и «Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)».
Частые вопросы
Что нужно для проекта умной теплицы?
Для базовой умной теплицы достаточно Arduino Uno или ESP32, датчика температуры и влажности DHT22, ёмкостного датчика влажности почвы, LDR с резистором 10 kΩ, модуля реле, погружного мини-насоса и вентилятора. В полной версии добавляются PTC-нагреватель 12 V, светодиодная лента с управлением через MOSFET, I2C LCD, поплавковый датчик уровня, блок питания 12 V 5 A и LM2596. Для корпуса проще и дешевле всего взять прозрачный контейнер для хранения с крышкой.
Как работает умная теплица?
Умная теплица работает по циклу «измерить, решить, выполнить, показать». Датчики измеряют температуру, влажность, влажность почвы и освещённость; Arduino сравнивает эти значения с порогами и включает и выключает вентилятор, нагреватель, насос и светодиоды через реле или MOSFET. Результаты выводятся на LCD, а с ESP32 — ещё и на телефон.
Что ставить в теплицу: DHT11 или DHT22?
Выбирай DHT22. DHT11 точен лишь до ±2 °C в пределах от 0 до 50 °C и не видит влажность выше 90%, а в теплице утром влажность может подняться выше 90%, зимой же температура может опуститься ниже нуля. DHT22 измеряет температуру от −40 до 80 °C с точностью ±0,5 °C и влажность с точностью около ±2%, а код для него почти не отличается от кода для DHT11.
Сколько стоит проект теплицы на Arduino?
Цены зависят от продавца, но без учёта платы базовая версия (DHT22, датчик влажности почвы, LDR, реле, насос и вентилятор) обычно обходится примерно в 25–40 $. Полная версия с нагревателем, светодиодной лентой, LCD, блоком питания 12 V и корпусом выходит около 70–120 $. Если стартовый набор Arduino уже есть, стоимость заметно снижается.
Как следить за теплицей с телефона?
Проще всего заменить «мозг» на ESP32 и запустить веб-сервер: в той же сети Wi-Fi значения видны в браузере телефона, и оттуда же можно включить насос. Для доступа вне дома подойдёт Blynk IoT, для уведомлений — Telegram-бот, а для долгой записи данных — Google Таблицы.
Как представить умную теплицу на научной выставке?
Вместо того чтобы просто показывать работающий макет, поставь измеримый исследовательский вопрос: например, в течение нескольких недель на двух группах растений измеряй, сколько воды экономит полив по датчику по сравнению с поливом по расписанию. Представь расход воды, высоту растений и графики температуры и влажности в таблицах и на диаграммах, объясни систему структурной схемой и подготовь короткий сценарий для живой демонстрации.
Что собрать после прочтения
Все проекты
Температура и влажность с DHT11
Насколько тепло в твоей комнате и влажный ли воздух или сухой? Датчик DHT11 измеряет температуру и влажность, а Arduino выводит значения на двухстрочный LCD-экран и обновляет их каждые пару секунд. Ес

Измерение освещённости с LDR
Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

Измерение освещённости модулем LDR на ESP32
Наблюдение в мониторе порта в реальном времени за сырым значением ADC модуля LDR и относительным процентом освещённости, вычисленным по опорным значениям темноты и света. Этот процент — не измерение в люксах. Учит логике калибровки модуля. Идеально выполнить его перед проектом 10, потому что он нужен для определения порога.

Умная станция полива
Садовник, который сам поливает растения, чтобы они не засохли, пока ты в отпуске. Датчик влажности почвы измеряет, насколько влажная земля; когда она сохнет, Arduino на несколько секунд включает водян

Температура и влажность с DHT22 на ESP32 (с LCD-дисплеем)
Этот проект измеряет температуру и влажность воздуха датчиком DHT22 и в реальном времени показывает данные на I2C LCD 16x2. Данные читаются с датчика по однопроводному протоколу через библиотеку Adafruit DHT. Проект служит основой для IoT-метеостанций, умного дома или систем мониторинга среды в теплицах. Связь с DHT22 идёт через пин GPIO 4 у ESP32.

Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Собери на ESP32 регистратор температуры с отметками времени! Каждую минуту читай температуру и влажность с DHT22, бери реальное время из DS1307 RTC и записывай всё на карту microSD в формате CSV. OLED показывает текущую температуру (крупным шрифтом), влажность в процентах, время и счётчик записей. Файл /temp_log.csv на SD-карте открывается в Excel, Google Sheets или Python pandas — идеально для теплиц, комнат, лабораторий и хранения продуктов.

Умное освещение на ESP32 (PIR + LDR + реле + OLED)
Сделай умное освещение для коридора, ванной или кладовки на ESP32! С PIR-датчиком движения, датчиком освещённости LDR и модулем реле собирается классический сценарий «свет включается при движении в темноте»: если вокруг уже светло, движение свет НЕ включает, и днём энергия экономится. Свет горит 30 секунд; если движение продолжается, таймер сбрасывается, и свет горит дольше. OLED SSD1306 крупно показывает состояние (ON / DAYTIME / WAITING) и обратный отсчёт.
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

