======Гидропонная система периодического затопления «Гидрогоршок»======
Гидропонные системы – это прогрессивный способ выращивания растений. Основная идея в том, что корни находятся не в почве или субстрате, а в питательном растворе, откуда растение берет все необходимые для роста вещества.
{{ :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;
}
=====Готовое устройство=====
Размести все элементы внутри герметичного корпуса.
Толщины акриловой крышки достаточно для работы сенсорной кнопки, которой можно переключать отображаемую информации на дисплее.