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

Автоматическая теплица. Часть 4. Управление поливом

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

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

Все части цикла “Теплица с зачатками разума на ESP32”

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


 

Зоны полива

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

Датчики полива

В теплице я применил те же самые датчики, про которые я написал в первой части описания устройства автоматического полива:

  • CWT-Soil-TH-S в огурцах
  • CWT-Soil-THCPH-S в помидорках

Оба сенсора подключены к одной шине, впечатления пока только положительные. Особой необходимости в применении более дорогой версии CWT-Soil-THCPH-S не было, я просто хотел попробовать что это такое и зачем это может быть нужно.

Датчик уровня воды в баке

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

Этому баку уже лет 50. Когда то давным-давно он использовался в местном совхозе, потом каким-то путем попал к моей бабушке, а лет 18 назад – уже ко мне. Внутри уже полностью ржавый, но, тьфу-тьфу, ещё крепкий. Умели раньше делать

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

  • линейку или рулетку, и какой-то пульт для ввода данных в систему
  • несколько последовательно соединенных поплавковых датчиков с резисторами (а что? – дешево и сердито), а потом подать напряжение с получившегося делителя на ADC-вход MCU
  • уже готовый гидростатический датчик давления QDY30A, который определяет высоту воды над собой по изменению давления (дорого).

Я предпочел последний вариант, хотя он существенно дороже. Ну лень мне было возится с самодельным уровнемером и я сильно сомневался, что поплавковые датчики будут долго стабильно работать. Кроме того, я планировал использовать гидростатический датчик в другом месте и мне необходимо было его протестировать.

Датчик уровня воды оказался гораздо тяжелее и больше, чем я ожидал. Его можно с успехом применять в качестве средства самообороны – в случае “прилета в лоб” как минимум сотрясение гарантировано.

Датчик уровня воды в лучших традициях: тяжелый и чугуниевый (на самом деле это сталь).

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

 

Датчик температуры воды

Для измерения температуры я использовал самые обычные датчики DS18B20 в гильзе. Но увы, в китайском исполнении датчик прожил не долго – три недели. Несмотря на дополнительно предпринятые меры по дополнительной герметизации места соединения гильзы и кабеля. Судя по всему – проржавел якобы нержавеющий корпус.

Заказал оригинальный в оформлении IP67 с силиконовым кабелем, но пока даже не получил.

 

Наполнение бака

Для автоматического наполнения бака применен шаровый кран с электроприводом. С его помощью каждым утром бак наполняется до полного из летнего водопровода из трубы ПНД.

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

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

Насос и клапаны

Чтобы напор в системе полива не зависел от уровня воды в баке, я поставил небольшой центробежный насос на 12В DC204-12V.

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

Перед ним установлен небольшой фильтр, чтобы “отсечь” крупные загрязнения. Купил в местном магазине сантехники. Все собрано на разборных муфтах, чтобы легче ремонтировать в случае проблем.

Изгибы водопровода нужны только для балансировки

Далее, от насоса труба “уходит” под землю и выходит из нее уже в теплице. Здесь стоят два клапана для раздачи воды в разные трубопроводы. Даже если у вас всего одна система полива, клапан все равно нужен, так как даже через отключенный насос вода потихонечку протекает (если точка забора воды выше уровня выхода).

Клапаны обычные “китайские” на 12В. Их достаточно просто купить и у нас и у дядюшки Ляо. Есть варианты и на 12В, и на 24 и на 220. Я предпочел безопасный вариант на 12В.

Но катушка у них достаточно сильно греется, так что не стоит включать её надолго. И на 220В, кстати, тоже. По этой же причине я не стал закрывать клапаны коробкой – пусть охлаждаются. Грязи и влаги снаружи они не особо боятся.

12 вольт – не страшно


 

Система капельного полива

В принципе, можно обойтись и без капельниц – развести по теплице микротрубки 3 или 4 мм к каждому растению, да так и оставить. Само вытекать будет. Но, если длина теплицы велика, в начале воды будет выливаться больше, чем в конце (а у меня вообще трубы буквой П расположены и вход только с одной стороны, а конец заглушен). Капельницы создают сопротивление вытекающей воде, что поддерживает определенное давление на всей длине трубы. В основном я применяю капельницы – колышки с лабиринтом, которые подают воду непосредственно в почву, к корням. Это позволяет уменьшить испарение влаги с поверхности почвы. Томатам лишняя влага в воздухе не желательна, а два огурца обойдутся.

При покупке обратите внимание: они бывают для трубки 3/5 мм и 4/7 мм. Я использовал трубку 4/7 мм, кстати её проще и дешевле купить в России, чем на Али. Но сами капельницы 4/7 мм найти на наших маркетплейсах почему-то гораздо сложнее и стоят они здесь заметно дороже, поэтому пришлось ждать из Китая. Почему-то все наши продавцы уверены, что в России нужны только капельницы 3/5 мм и других не возят. 50 штук не хватило, пришлось докупать ещё партию.

А вообще меня удивляет позиция многих садовых магазинов – они предлагают только готовые комплекты капельного полива с таймером или без. А отдельных компонентов не поставляют принципиально.

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

Трубки через адаптер подключены к общей раздаточной трубе из ПНД, из которой собственно и собран весь водопровод в огороде. Сверлим отверстие, руками вкручиваем адаптер, надеваем трубку. Все просто.


 

Алгоритмы

Ну и немного расскажу про собственно алгоритмы. Так как я использовал кнопочную панель для ручного управления поливом, пришлось “мудрить” с флагами и группами событий. Без кнопочной панели алгоритмы можно сделать гораздо проще (об чем я и расскажу потом в статьях про автоматический полив комнатных растений).

В системе используется “импульсный” режим полива – насос и клапан включаются на несколько минут, затем выключаются и “ждут” в 3-5 раз дольше. Это позволяет избежать “болота” прежде чем вода дойдет до сенсора влажности и он получит новые данные. Весь цикла полива также ограничен по времени для предотвращения перелива в случае какой-либо неисправности или сбоя. Все интервалы, разумеется, настраиваются.

// Интервал суток полива и наполнения бака
static uint32_t wateringTimespan1 = 18002100U;
static uint32_t wateringTimespan2 = 18002100U;

// Режим работы
typedef enum {
  WATERING_OFF      = 0,     // Отключено
  WATERING_FORCED   = 1,     // Включено принудительно
  WATERING_SENSORS  = 2      // Управление по датчикам
} watering_mode_t;
static watering_mode_t wateringMode1 = WATERING_SENSORS;
static watering_mode_t wateringMode2 = WATERING_SENSORS;

// Уведомления в telegram
static notify_type_t wateringNotify = NOTIFY_OFF;

// Минимальный уровень воды в баке
static float wateringWaterLevelStart = 50.0;
static float wateringWaterLevelStop  = 10.0;
// Температура воды в баке
static float wateringWaterTempMin = 15.0;
static float wateringWaterTempMax = 30.0;
// Температура почвы
static float wateringSoilTempMin = 10.0;
static float wateringSoilTempMax = 30.0;
// Влажность почвы 
static float wateringMoisture1Min = 20.0;
static float wateringMoisture1Max = 40.0;
static float wateringMoisture2Min = 30.0;
static float wateringMoisture2Max = 50.0;
// Общая максимальная длительность полива в минутах
static uint32_t wateringMoisture1MaxDuration = 3*60;
static uint32_t wateringMoisture2MaxDuration = 3*60;
// Длительность включения клапана в секундах в пределах одного цикла
static uint32_t wateringMoisture1CycleTime = 1*60;
static uint32_t wateringMoisture1CycleInterval = 5*60;
static uint32_t wateringMoisture2CycleTime = 1*60;
static uint32_t wateringMoisture2CycleInterval = 5*60;

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

// Текущие значения с сенсора почвы 1
static float sensorsGetSoil1Temp()
{
  #if SENSOR_SOIL1_IS_CHANNEL1
    if (sensorSoil1.getStatus() == SENSOR_STATUS_OK) {
      return sensorSoil1.getTemperature(false).filteredValue;
    };
  #else
    if (sensorSoil2.getStatus() == SENSOR_STATUS_OK) {
      return sensorSoil2.getTemperature(false).filteredValue;
    };
  #endif // SENSOR_SOIL1_IS_CHANNEL1
  return NAN;
}

static float sensorsGetSoil1Moisture()
{
  #if SENSOR_SOIL1_IS_CHANNEL1
    if (sensorSoil1.getStatus() == SENSOR_STATUS_OK) {
      return sensorSoil1.getHumidity(false).filteredValue;
    };
  #else
    if (sensorSoil2.getStatus() == SENSOR_STATUS_OK) {
      return sensorSoil2.getHumidity(false).filteredValue;
    };
  #endif // SENSOR_SOIL1_IS_CHANNEL1
  return NAN;
}

// Текущее значение температуры воды в баке
static float sensorsGetWaterTemp()
{
  if (sensorWaterTemp.getStatus() == SENSOR_STATUS_OK) {
    return sensorWaterTemp.getTemperature(false).filteredValue;
  };
  return NAN;
}

// Текущее значение уровня воды в баке
static float sensorsGetWaterLevel()
{
  if (sensorWaterLevel.getStatus() == SENSOR_STATUS_OK) {
    return sensorWaterLevel.getLevel(false).filteredValue;
  };
  return NAN;
}

// Контроллеры управления насосом и клапанами
static rLoadGpioController lcPump(CONFIG_GPIO_PUMP, CONFIG_GPIO_RELAY_LEVEL, false, CONFIG_PUMP_KEY, nullptr, nullptr, TI_SECONDS,
      nullptr, nullptr, pumpCbStateChange, relaysPublish);
static rLoadGpioController lcValve1(CONFIG_GPIO_VALVE1, CONFIG_GPIO_RELAY_LEVEL, true, CONFIG_VALVE1_KEY, 
      &wateringMoisture1CycleTime, &wateringMoisture1CycleInterval, TI_SECONDS,
      relaysCbBeforeChange, valve1CbAfterChange, valve1CbStateChange, relaysPublish);
static rLoadGpioController lcValve2(CONFIG_GPIO_VALVE2, CONFIG_GPIO_RELAY_LEVEL, true, CONFIG_VALVE2_KEY, 
      &wateringMoisture2CycleTime, &wateringMoisture2CycleInterval, TI_SECONDS,
      relaysCbBeforeChange, valve2CbAfterChange, valve2CbStateChange, relaysPublish);

// Проверка уроаня воды в баке
bool wateringWaterLevelCheck(bool state)
{
  float tankLevel = sensorsGetWaterLevel();
  if (!isnan(tankLevel)) {
    if (state) {
      return tankLevel > wateringWaterLevelStop;
    } else {
      if (tankLevel < wateringWaterLevelStart) {
        time_t now = time(nullptr);
        if ((now - wateringLowLevelNotify) >= CONFIG_WATERING_NOTIFY_PERIOD) {
          wateringLowLevelNotify = now;
          #if CONFIG_TELEGRAM_ENABLE
            if (wateringNotify != NOTIFY_OFF) {
              tgSend(CONFIG_WATERING_NOTIFY_KIND, CONFIG_WATERING_NOTIFY_PRIORITY, wateringNotify == NOTIFY_SOUND, CONFIG_TELEGRAM_DEVICE, 
              "⚠ <b>Уровень воды в баке слишком мал</b>: %.1f%%!", sensorsGetWaterLevel());
            };
          #endif // CONFIG_TELEGRAM_ENABLE
        };
        return false;
      };
    };
  };
  return true;
}

char* wateringNotifyData1()
{
  return malloc_stringf("Почва:   %.1f°C %.1f%%\nТеплица: %.1f°C %.1f%%\nБак:     %.1f°C %.1f%%", 
    sensorsGetSoil1Temp(), sensorsGetSoil1Moisture(), 
    sensorsGetInternalTemp(), sensorsGetInternalHumd(), 
    sensorsGetWaterTemp(), sensorsGetWaterLevel());
}

void valve1CbStateChange(rLoadController *ctrl, bool state, time_t duration)
{
  ioexp1LedsSetWatering1(state);

  #if CONFIG_TELEGRAM_ENABLE
    if (wateringNotify != NOTIFY_OFF) {
      char* data = wateringNotifyData1();
      if (data) {
        if (state) {
          tgSend(CONFIG_WATERING_NOTIFY_KIND, CONFIG_WATERING_NOTIFY_PRIORITY, wateringNotify == NOTIFY_SOUND, CONFIG_TELEGRAM_DEVICE, 
          "💦 Полив томатов <b>включён</b>\n\n<code>%s</code>", data);
        } else {
          uint16_t last_h = duration / 3600;
          uint16_t last_m = duration % 3600 / 60;
          uint16_t last_s = duration % 3600 % 60;
          tgSend(CONFIG_WATERING_NOTIFY_KIND, CONFIG_WATERING_NOTIFY_PRIORITY, wateringNotify == NOTIFY_SOUND, CONFIG_TELEGRAM_DEVICE, 
          "✅ Полив томатов <b>завершен</b>\n\n<code>%s\nВремя работы: %.2dч %.2dм %.2dc</code>", data, last_h, last_m, last_s);
        };
        free(data);
      };
    };
  #endif // CONFIG_TELEGRAM_ENABLE
}

bool wateringSensorsControl(bool oldState, float soilMoisture, float minMoisture, float maxMoisture, float soilTemp)
{
  bool newState = oldState;

  // Проверяем уровень влажности почвы
  if (!isnan(soilMoisture)) {
    if (oldState) {
      newState = soilMoisture < maxMoisture;
    } else {
      newState = soilMoisture <= minMoisture;
    };
  };

  // Если полив еще не начат, проверяем температуру воды
  if (newState && !oldState) {
    float waterTemp = sensorsGetWaterTemp();
    if (!isnan(waterTemp)) {
      newState = (waterTemp >= wateringWaterTempMin) && (waterTemp <= wateringWaterTempMax);
    };
  };

  // Если полив еще не начат, проверяем температуру почвы
  if (newState && !oldState && !isnan(soilTemp)) {
    newState = (soilTemp >= wateringSoilTempMin) && (soilTemp <= wateringSoilTempMax);
  };

  return newState;
}

void wateringControl1(EventBits_t bits) 
{
  time_t now = time(nullptr);
  bool stateValve = lcValve1.getState();
  bool manualStarted = false;
  
  // Ручное управление: нажата кнопка
  if (bits & BTN_WATERING1) {
    if (stateValve || (xEventGroupGetBits(_sensorsFlags) & MANUAL_WATERING1)) {
      // Полив включен, нужно остановить принудительно
      xEventGroupClearBits(_sensorsFlags, MANUAL_WATERING1);
      stateValve = false;
    } else {
      // Полив выключен, принудительная активация полива
      xEventGroupSetBits(_sensorsFlags, MANUAL_WATERING1);
      manualStarted = true;
      stateValve = true;
    };
  }
  // Ручное управление: поддержание до верхнего уровня влажности
  else if (xEventGroupGetBits(_sensorsFlags) & MANUAL_WATERING1) {
    stateValve = wateringWaterLevelCheck(stateValve) 
      && wateringSensorsControl(stateValve, 
          sensorsGetSoil1Moisture(), wateringMoisture1Min, wateringMoisture1Max,
          sensorsGetSoil1Temp());
    if (!stateValve) {
      xEventGroupClearBits(_sensorsFlags, MANUAL_WATERING1);
    };
  }
  // Автоматическое управление
  else {
    if (wateringMode1 == WATERING_FORCED) {  
      stateValve = checkTimespanTimeEx(now, wateringTimespan1, true) 
        && wateringWaterLevelCheck(stateValve);
    } else if (wateringMode1 == WATERING_SENSORS) {
      stateValve = checkTimespanTimeEx(now, wateringTimespan1, true) 
        && wateringWaterLevelCheck(stateValve)
        && wateringSensorsControl(stateValve, 
              sensorsGetSoil1Moisture(), wateringMoisture1Min, wateringMoisture1Max,
              sensorsGetSoil1Temp());
    } else {
      stateValve = false;
    };
  };
  
  // Управление клапанами
  if (stateValve && !lcValve1.getState() && (wateringMoisture1MaxDuration > 0)) {
    stateValve = manualStarted || ((now - lcValve1.getLastOff()) >= (wateringMoisture1MaxDuration*60));
    if (stateValve) {
      lcValve1.loadSetTimer(wateringMoisture1MaxDuration * 60000);
    };
  } else {
    lcValve1.loadSetState(stateValve, false, true);
  };
}

Точно так же реализован и второй канал управления поливом.

 


Результаты

Растения в теплице очень быстро “приспособились” к поддержанию постоянной температуры воздуха и влажности почвы, и результаты не заставили себя долго ждать.

 

На этом пока всё, до встречи на сайте и на dzen-канале! Всем добра!

Все части цикла “Теплица с зачатками разума на ESP32”

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


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

10 комментариев для “Автоматическая теплица. Часть 4. Управление поливом”

  1. Николай

    Контроль воды в баке можно сделать гораздо проще (при условии, что не нужно выполнять какие-то расчёты основываясь на уровне воды в баке). Я использовал поплавок с клапаном, который используется в баках запаса воды для систем водоснабжения дома. Этот клапан, по принципу работы, похож на тот что используется в сливных бачках. Когда уроверь воды понижается, клапан сам открывается и вода поступает в бак. Как только уровень востановился, клапан сам закрывается. У меня так сделано в системе полива газона.

    1. И сразу же нарисуется проблема температуры воды: вода из скважины ледяная в любое время года. Если бак наполовину пустой, то температуры воды резко падает, а поливать холодной водой в жару не желательно. Ваш же способ приведет к тому, что пополнение бака будет начинаться сразу же во время любого отбора воды из бака.
      Тогда уж лучше вообще отказаться от накопительной емкости и поливать напрямую из водопровода. Кстати, для некоторых других культур я так и делаю.

  2. У меня похожая система работала, только управляла поливом и досветкой – Екатеринбург, у нас света мало, а тепла и того меньше, форточки даже не делал. Управлялось все через Raspberry Pi кажется первого поколения еще. Интересно было не только параметры записывать и управлять, но и таймлапсы снимать как помидоры/перцы растут.
    Потом система переехала в курятник, также следила за температурой (зимой) и освещенностью.

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

    1. Добрый день! Воду из бочки слил через простой “ручной” кран, труба в теплицу у меня с разъемной муфтой, просто развинтил и снял. Подземная часть осталась с водой, но как показала практика, черные ПНД трубы весьма лояльно относятся к замораживанию. Насос, датчики и фильтры промыл, закрыл под бочкой в емкости с крышкой. Снял капельницы с трубок – пойдут под промывку и повторное использование. В общем-то это вся консервация.

      1. Сами ПНД трубы без проблем переносят холода, но вот фиттинги, углы, краны – всё ломает 🙁 У меня каждый сезон начинается с замены по кругу… Пробовал сжатым воздухом продувать – без толку.
        Уменьшил количество “стационарных” труб, а капельный полив сделал с помощью полиэтиленового “шланга” с отверстиями через каждые 30 см. У нас такие подаются кусками по 10 метров – размотал по грядке весной, он там под зеленью капает. Осенью всё убрал – смотал.

        Кстати спасибо за интересный сайт и большую работу, пошел читать про IDF. Мне тоже не зашли ESP8266 и ардуины, пробовал NodeMCU с Lua но это прям глюкало страшное. FreeRTOS это прям отличное решение.

        Хотел просить – у вас нет опыта работы с LoRa? Модули недорогие, удобные, вроде дальнобойные. У меня wi-fi не везде берет и ESP32 оказывается беспомощным.

        1. Да вроде бы и фитинги пока целые ツ
          Бывает, весной начинают подтекать – разбираю/собираю и норм. А серый полипропилен вообще давно на улице замораживается – тоже норм.

          По LoRa нет опыта ツ
          Я решил проблему просто покупкой еще одного роутера и созданием mesh через адаптеры pover link (это когда сигнал передается по 220в), но у pover link задержки дикие бывают.
          Еще можно использовать сами ESP32 в режиме wifi long range – две ESP32 можно связать между собой до 1 км, но с низкой скоростью. Хочу, кстати, попробовать

    1. Это не клапан, это шаровый кран с электроприводом. Их тьма тьмущая разных модификаций. Ну например вот: https://aliexpress.ru/item/1005003214000529.html?spm=a2g2w.orderdetail.0.0.46d14aa6pqBCS4&sku_id=12000024693756304
      В дом я поставил поаккуратней, беленькие. Зато этот имеет более-менее герметичный корпус редуктора

  3. И еще такой вопрос. Хочу тоже нечто подобное собрать, но скорее не для теплицы, а просто для полива грядок с вибрационного насоса в колодце. У вас как я понял через весь участок 20 пнд труба закопана? Достаточно ли такого диаметра под эту цель будет? Простите за глупый вопрос, просто раньше дел с этими вот водопроводными делами не имел, поливал напрямик, из шланга. Хотелось бы узнать, как это все по уму лучше сделать=)

    1. Да, именно так – ПНД труба 20 мм. Я тоже в гидравлике не разбираюсь, на глазок делал.
      Ну так у меня до этого тоже шланги были 20 мм, хватало. Скважина у меня не даст особо сильный напор.

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

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