======Гидропонная система периодического затопления «Гидрогоршок»====== Гидропонные системы – это прогрессивный способ выращивания растений. Основная идея в том, что корни находятся не в почве или субстрате, а в питательном растворе, откуда растение берет все необходимые для роста вещества. {{ :projects:gydroponic:112.jpg?nolink |}} Существует много разновидностей таких систем, одна из которых основана на принципе периодического затопления корневой системы. Растение попеременно будет получать и необходимые для роста вещества из питательного раствора, и кислород для того, чтобы корни не начали загнивать. ======Видеообзор====== {{youtube>QQgArY2jjiQ?large}} ======Как устроен «Гидрогоршок»====== {{ :projects:gydroponic:annotation5.png?nolink |}} ==== Что понадобится ==== * [[amp>product/arduino-uno?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Arduino Uno]] * [[amp>product/arduino-troyka-slot-shield?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki |Slot Shield]] * [[amp>product/troyka-5mm-led-module?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Светодиод (Troyka-модуль)]] * [[amp>product/troyka-potentiometer?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Потенциометр (Troyka-модуль)]] * [[amp>product/power-supply-adapter-robiton-tn1000s?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Блок питания]] * [[amp>product/water-level-sensor-angle?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Угловой датчик уровня жидкости]] * [[amp>product/immersible-water-pump?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Помпа]] * [[amp>product/troyka-mosfet-n-channel-v3?utm_source=proj&utm_campaign=gydroponic&utm_medium=wiki | Силовой ключ (Troyka-модуль)]] * Сетчатый гидропонный горшок * Удобрения для питательного раствора * Керамзит * Двойной горшок или два ведёрка разного объёма ====== Как собрать ====== - Установи Slot Shield на Arduino Uno. Не забудь поставить джампер на Slot Shield в положение V2-VIN {{ :projects:gydroponic:step1.png?nolink |}} - Добавь на Slot Shield светодиод, потенциометр и силовой ключ. Не забудь поставить джампер на силовом ключе в положение V=P+ {{ :projects:gydroponic:step2.png?nolink |}} - Собери корпус гидрогоршка из двух ёмкостей, как показано на рисунке.{{ :projects:gydroponic:step3.png?nolink |}} - В верхнем резервуаре просверли отверстие для силиконовой трубки и дренажное отверстие для стекания питательного раствора обратно в нижнюю ёмкость. Дренажное отверстие не должно быть очень большим, иначе помпа не будет успевать накачать питательный раствор из одной ёмкости в другую. - Просверли отверстия под датчики уровня и установи их. Помпу помести в нижний резервуар, выведи провод питания наружу и зачисти изоляцию на проводах. - Установи сетчатый горшочек для гидропоники и засыпь его керамзитом. - Подключи помпу к силовому ключу, а датчики уровня к цифровым пинам на Slot Shield. {{ :projects:gydroponic:step5.png?nolink |}} ====== Исходный код ====== Прошей Arduino Uno скетчем // Пин, к которому подключен датчик минимального уровня питательного раствора #define LOWLEVEL 7 // Пин, к которому подключен датчик максимального уровня питательного раствора #define MAXLEVEL 6 // Пин, к которому подключен датчик уровня заполнения верхнего резервуара #define FLOODLEVEL 4 // Пин, к которому подключен светодиодный индикатор #define LEDPIN 13 // Пин, к которому подключена помпа #define PUMPPIN 12 // Пин, к которому подключен потенциометр регулировки #define POTPIN A0 // Переменные для хранения времени long previousMillis = 0; long blinkPreviousMillis = 0; unsigned long interval; // два часа = 7200000, 15 минут = 900000 // Переменные-флаги для хранения состояния работы гидрогоршка bool blinkState = 0; bool waterStart = 0; bool pumpState = 0; // Переменные для хранения состояния датчиков уровня bool lowLevelState; bool maxLevelState; bool floodLevelState; // Переменная для хранения состояния мигающего светодиода bool ledState; void setup() { // Притянем пины датчиков уровня к плюсу pinMode(LOWLEVEL, INPUT_PULLUP); pinMode(MAXLEVEL, INPUT_PULLUP); pinMode(FLOODLEVEL, INPUT_PULLUP); // Установим пины светодиода и помпы как выходы pinMode(LEDPIN, OUTPUT); pinMode(PUMPPIN, OUTPUT); // Проверим уровень раствора. Если уровень меньше минимального - зажигаем светодиодный индикатор. // Ждем срабатывания датчика максимального уровня, после этого гасим светодиод. if (digitalRead(LOWLEVEL)) { while (!digitalRead(MAXLEVEL)) { digitalWrite(LEDPIN, 1); } } digitalWrite(LEDPIN, 0); // Сохраняем текущее время для подсчета интервалов затопления. previousMillis = millis(); // Устанавливаем флаг для начала работы помпы. pumpState = 1; } void loop() { // Считываем значение потенциометра для установки следующего затопления. interval = map(analogRead(POTPIN), 0, 1023, 900000, 7200000); // Присваиваем переменным состояния датчиков уровня. // Некоторые датчики установлены "вверх ногами", поэтому значения с них нужно инвертировать. lowLevelState = !digitalRead(LOWLEVEL); maxLevelState = digitalRead(MAXLEVEL); floodLevelState = digitalRead(FLOODLEVEL); // Проверяем, настало ли время затапливать корневую систему растения. // Если настало - устанавливаем флаг затопления. if (millis() - previousMillis > interval) { waterStart = 1; } // Если необходимо затапливать корни, и уровень в резервуаре выше минимального - // включаем помпу и сбрасываем флаг затопления. if (waterStart && lowLevelState) { pumpState = 1; waterStart = 0; } // Проверяем, в каком состоянии находится флаг работы помпы. // В зависимости от этого включаем её или выключаем. if (pumpState) { digitalWrite(PUMPPIN, HIGH); } else { digitalWrite(PUMPPIN, LOW); } // Если сработал датчик уровня затопления - выключаем помпу и обновляем переменную хранения времени. // Если вдруг раствора стало слишком мало и сработал датчик минимального уровня - выключаем помпу. if (!lowLevelState || floodLevelState) { pumpState = 0; previousMillis = millis(); } // Если сработал датчик минимального уровня раствора - устанавливаем флаг мигания светодиода. if (!lowLevelState) { blinkState = 1; } // Если установлен флаг мигания светодиода - мигаем им ))) if (blinkState) { Alarm (); } // Если светодиод мигает, сигнализируя о нехватке уровня раствора, и в этот момент сработал // датчик максимального уровня - значит раствор долили и светодиод гаснет. if (blinkState && maxLevelState) { blinkState = 0; digitalWrite(LEDPIN, 0); } } // Функция мигания светодиодом void Alarm () { if (millis() - blinkPreviousMillis > 300) { blinkPreviousMillis = millis(); if (!ledState) { ledState = 1; } else { ledState = 0; } digitalWrite(LEDPIN, ledState); } } ====== Первый запуск ====== - Выставь на блоке питания 12 Вольт и подключи его к Arduino Uno. - Включи блок питания в розетку. {{ :projects:gydroponic:an1.png?nolink |}} Светодиодный индикатор горит, значит датчик минимального уровня сообщает о недостатке питательного раствора для начала работы. Доливай питательный раствор до тех пор, пока не сработает датчик максимального уровня. После этого светодиод погаснет и заработает помпа. {{ :projects:gydroponic:step6.png?nolink |}} ===== Режим работы ===== После первого затопления корневой системы питательный раствор стекает обратно в нижнюю ёмкость. Таким образом корневая система насыщается и питательными веществами, и кислородом. {{ :projects:gydroponic:step7.png?nolink |}} По истечении времени, заданного потенциометром, цикл повторится вновь. Если во время затопления датчик минимального уровня питательного раствора сработает раньше датчика уровня затопления, замигает светодиодный индикатор, сигнализирующий о нехватке питательного раствора. При этом работа системы не прекратится, а последующие затопления будут происходить по графику. Но питательный раствор необходимо долить. {{ :projects:gydroponic:anim1.gif?nolink |}} Если во время мигания светодиодного индикатора сработает датчик максимального уровня, индикатор погаснет, и гидрогоршок продолжит работу в штатном режиме. ====== Гидрогоршок + ====== Для лучшего роста растению нужен хороший свет. В природе с этим справляется солнце. В помещении света не достаточно, поэтому в продолжении истории о гидропонном горшке, мы решили добавить управление освещением. Кроме света, растение прихотливо к влажности воздуха в котором оно растет. С поддержанием его на необходимом уровне справится бытовой увлажнитель. Лампы и увлажнители работают от сети 220 Вольт. Такое напряжение опасно для жизни, поэтому мы разместили все компоненты, управляющие этими приборами, в герметичный корпус. Вслед за ними отправились и другие железки. Но обо всём по порядку. ===== Что понадобится ===== * [[amp>product/arduino-uno?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Arduino Uno]] * [[amp>product/arduino-troyka-shield?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Troyka Shield]] * [[amp>product/troyka-quad-display?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Четырёхразрядный индикатор (Troyka-модуль)]] * [[amp>product/troyka-touch-sensor?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Сенсорная кнопка (Troyka-модуль)]] * [[amp>product/troyka-rtc?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Часы реального времени (Troyka-модуль)]] * [[amp>product/troyka-pad-1x2?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Troyka Pad 1×2 (Troyka-модуль)]] * [[amp>product/troyka-pad-1x4?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Troyka Pad 1×4 (Troyka-модуль)]] * [[amp>product/troyka-relay?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Реле (Troyka-модуль)]] * [[amp>product/ac-dc-rs-25-12?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Встраиваемый блок питания (12 В, 2100 мА)]] * [[amp>product/water-level-sensor-angle?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Датчик уровня воды (угловой)]] * [[amp>product/immersible-water-pump?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Погружная помпа с трубкой]] * [[amp>product/troyka-mosfet-n-channel-v3?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Силовой ключ (Troyka-модуль)]] * [[amp>product/troyka-led-matrix?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Монохромная LED матрица 8×8 (Troyka-модуль)]] * [[amp>product/sealed-enclosure-160x160x60?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Герметичный корпус 160×160×60]] * [[amp>product/cable_outlet_3-5_mm?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Герметичный кабельный ввод]] * [[amp>product/troyka-meteo-sensor?utm_source=proj&utm_campaign=gydroponic2&utm_medium=wiki| Цифровой метеодатчик (Troyka-модуль)]] * Сетчатый гидропонный горшок * Удобрения для питательного раствора * Керамзит * Двойной горшок или два ведёрка разного объёма ======Видеообзор====== {{youtube>g86EA8g0XNk?large}} ===== Сборка блока управления ===== Для начала установи тройка шилд на Arduino Uno. {{ :projects:gydroponic:1.png?nolink |}} Подключать тройка модули удобно через тройка пады. Подключи их к тройка шилду согласно схеме приведенной ниже. {{ :projects:gydroponic:2.png?nolink |}} Затем вставь в них Тройка модули, как показано на рисунке. {{ :projects:gydroponic:3.png?nolink |}} Подключи блок питания к Arduino Uno и силовому ключу для помпы, и соедини розетки для увлажнителя и лампы по схеме {{ :projects:gydroponic:4.png?nolink |}} Добавь в схему сам гидрогоршок и метеосенсор. Помпу подключи к силовому ключу. * Верхний датчик подключи к пину ''А2''; * Средний датчик подключи к пину ''А1''; * Нижний датчик подключи к пину ''А0''; {{ :projects:gydroponic:5.png?nolink |}} ===== Исходный код ===== В коде программы используются библиотеки для работы с модулями. Скачай и установи их перед прошивкой платы. * [[https://github.com/amperka/TroykaMeteoSensor|Библиотека для метеосенсора]] * [[https://github.com/amperka/TroykaRTC|Библиотека для часов реального времени]] * [[https://github.com/amperka/QuadDisplay2|Библиотека для дисплея]] * [[https://github.com/amperka/TroykaLedMatrix|Библиотека для матрицы]] * [[https://github.com/amperka/TroykaButton|Библиотека для кнопки]] // библиотека для работы I²C #include // библиотека для работы с часами реального времени #include "TroykaRTC.h" // Подключаем библиотеку для работы с дисплеем #include // библиотека для работы с метеосенсором #include // библиотека для работы со светодиодной матрицей #include "TroykaLedMatrix.h" // библиотека для работы с кнопками #include // размер массива для времени #define LEN_TIME 12 // размер массива для даты #define LEN_DATE 12 // размер массива для дня недели #define LEN_DOW 12 // создаём объект для работы с часами реального времени RTC clock; // массив для хранения текущего времени char time[LEN_TIME]; // массив для хранения текущей даты char date[LEN_DATE]; // массив для хранения текущего дня недели char weekDay[LEN_DOW]; // создаём объект класса QuadDisplay и передаём номер пина CS QuadDisplay qd(3); // создаём объект для работы с датчиком TroykaMeteoSensor meteoSensor; // создаём объект matrix для работы с матрицей TroykaLedMatrix matrix; // Пин к которому подключен датчик минимального уровня питательного раствора #define LowLevel A0 // Пин к которому подключен датчик максимального уровня питательного раствора #define MaxLevel A1 // Пин к которому подключен датчик уровня заполнения верхнего резервуара #define FloodLevel A2 // Пин к которому подключена кнопка #define ButtonPin 2 // Пин к которому подключена помпа #define PumpPin 4 // Пин к которому подключено реле освещения #define LightPin 6 // Пин к которому подключено реле увлажнителя воздуха #define HumPin 5 // создаем объект для работы с кнопками (номер пина, длительность удержания кнопки, притяжка к минусу) TroykaButton button(ButtonPin, 2000, 1); // Переменные для хранения вренени int hour; int minute; // Переменная для хранения времени в минутах int curent; // Переменные для хранения времени включения освещения int hon=5; int mon=59; // Переменная для хранения времени включения освещения в минутах int on; // Переменные для хранения времени выключения освещения int hoff=23; int moff=59; // Переменная для хранения времени выключения освещения в минутах int off; // Переменные для хранения миллисекунд long previousMillis = 0; long BlinkPreviousMillis = 0; long DisplayPreviousMillis = 0; // переменная хранения значения интервала затопления корневой системы unsigned long interval = 1200000; // два часа = 7200000, 20 минут = 1200000 // Переменные-флаги для хранения состояния работы гидрогоршка bool blinkState = 0; bool waterStart = 0; bool pumpState = 0; // Переменные для хранения состояния датчиков уровня bool LowLevelState; bool MaxLevelState; bool FloodLevelState; // Переменная для хранения состояния индикации об ошибке bool ErrState; // Переменная для хранения значений температуры int temperature; // Переменная для хранения значений относительной влажности int humidity; // Переменная для хранения минимального значения относительной влажности int lowHumidity = 40; // Переменная режимов отображения int buttonState = 0; void setup(){ // Инициализируем работу сериал порта Serial.begin(9600); // начало работы с матрицей matrix.begin(); // очищаем матрицу matrix.clear(); // Установим уровень яркости matrix.setCurrentLimit(ROW_CURRENT_20MA); // устанавливаем шрифт matrix.selectFont(FONT_8X8_BASIC); // Инициализируем работу дисплея qd.begin(); // Инициализируем работу метеосенсора meteoSensor.begin(); // Инициализируем работу часов реального времени clock.begin(); // Установим время clock.set(__TIMESTAMP__); // начало работы с кнопкой button.begin(); // Притянем пины датчиков уровня к плюсу pinMode(LowLevel, INPUT_PULLUP); pinMode(MaxLevel, INPUT_PULLUP); pinMode(FloodLevel, INPUT_PULLUP); // Установим пины контроллера как выходы и входы pinMode(LightPin, OUTPUT); pinMode(ButtonPin, INPUT); pinMode(PumpPin, OUTPUT); pinMode(HumPin, OUTPUT); // Проверим уровень раствора. // Если уровень меньше минимального - отобразим на матрице восклицательный знак. // Ждем пока не сработает датчик максимального уровня, после этого очистим матрицу. if (digitalRead(LowLevel)){ while (!digitalRead(MaxLevel)){ matrix.drawSymbol('!'); qd.displayDigits(QD_NONE, QD_L, QD_0, QD_L); } } matrix.clear();; // Сохраняем текущее время для подсчета интервалов затопления. previousMillis = millis(); // Устанавливаем флаг для начала работы помпы. pumpState = 1; } void loop(){ // считываем данные с датчика int stateSensor = meteoSensor.read(); // Проверяем ответ и сохраняем значения температуры // и относительной влажности в переменные switch (stateSensor) { case SHT_OK: temperature = meteoSensor.getTemperatureC(); humidity = meteoSensor.getHumidity(); break; // Ошибка получения данных case SHT_ERROR_DATA: Serial.println("Data error or sensor not connected"); break; // Ошибка контрольной суммы case SHT_ERROR_CHECKSUM: Serial.println("Checksum error"); break; } // запрашиваем данные с часов clock.read(); hour = clock.getHour(); minute = clock.getMinute(); // Переводим текущее время и время работы освещения в минуты curent=timeto(hour,minute); on=timeto(hon, mon); off=timeto(hoff , moff ); // Сравниваем текущее время с временем работы освещения if (on>off){ if ((curent>=on)||(curent=on)&&(curent (lowHumidity+10)){ digitalWrite(HumPin, LOW); } // считывание данных с кнопки button.read(); // опеределяем клик кнопки if (button.justPressed()) { if (buttonState < 2){ buttonState = buttonState + 1; DisplayPreviousMillis = millis(); } else { buttonState = 0; matrix.clear(); } } // если после переключения режимов прошло 5 секунд- // сбрасываем счетчик нажжатий для отображения времени if( (millis())- DisplayPreviousMillis > 5000){ buttonState = 0; matrix.clear(); } // выводим значения на дисплей if(!blinkState){ switch (buttonState) { case 0: qd.displayScore(hour, minute, true); break; case 1: qd.displayTemperatureC(temperature); matrix.drawSymbol('T'); break; case 2: qd.displayHumidity(humidity); matrix.drawSymbol('H'); break; } } else { qd.displayDigits(QD_NONE, QD_L, QD_0, QD_L); } // Присваиваем переменным состояния датчиков уровня. // Некоторые датчики установлены "вверх ногами", поэтому значения с них нужно инвертировать. LowLevelState = !digitalRead(LowLevel); MaxLevelState = digitalRead(MaxLevel); FloodLevelState = digitalRead(FloodLevel); // Проверяем настало ли время затапливать корневую систему растения. // Если настало - устанавливаем флаг затопления. if (millis() - previousMillis > interval){ waterStart = 1; } // Если необходимо затапливать корни, и уровень в резервуаре выше минимального - // включаем помпу и сбрасываем флаг затопления. if (waterStart && LowLevelState){ pumpState = 1; waterStart = 0; } // Проверяем в каком состоянии находится флаг работы помпы. // В зависимости от этого включаем её или выключаем. if (pumpState){ digitalWrite(PumpPin, HIGH); } else{ digitalWrite(PumpPin, LOW); } // Если сработал датчик уровня затопления - выключаем помпу и обновляем переменную хранения времени. // Если вдруг раствора стало слишком мало и сработал датчик минимального уровня - выключаем помпу. if (!LowLevelState || FloodLevelState){ pumpState = 0; previousMillis = millis(); } // Если сработал датчик минимального уровня раствора - устанавливаем флаг отображения ошибки на матрице. if (!LowLevelState){ blinkState = 1; } // Если установлен флаг отображения ошибки на матрице - отображаем её ))) if (blinkState){ alarm (); } // Если восклицательный знак ошибки мигает сигнализируя, что уровня раствора не достаточно // и в этот момент сработал датчик максимального уровня - значит раствор долили и ошибка гаснет. if (blinkState && MaxLevelState){ blinkState = 0; matrix.drawSymbol(' '); } } // Функция мигания восклецательного знака void alarm (){ if (millis() - BlinkPreviousMillis > 300){ BlinkPreviousMillis = millis(); if (!ErrState){ ErrState = 1; matrix.drawSymbol('!'); } else{ ErrState = 0; matrix.clear(); } } } // Функция перевода времени в минуты int timeto(int x, int y){ int z; z=(60*x)+y; return z; } =====Готовое устройство===== Размести все элементы внутри герметичного корпуса. Толщины акриловой крышки достаточно для работы сенсорной кнопки, которой можно переключать отображаемую информации на дисплее.