Введение
В данной статье мы рассмотрим процесс сборки, программирования и настройки модулей долива, которые могут быть использованы при сборке автоматической гидропоники с автокорректировкой параметров и управлением через интернет. Модули также могут использоваться в качестве системы для полива растений. Если вы собираете систему автополива растений, предлагаем продолжить сборку по отдельной статье.
Видео
Редактируется...
Нам понадобится
Обратите внимание, что в зависимости от необходимого функционала системы, некоторые модули можно не использовать. Подробнее читайте далее.
Количество представлено для сборки одного модуля долива.
- 1х Piranha Trema (с колодками) или другой контроллер
- 1х Мембранный насос 280, 6 - 12В или Перистальтический насос (5-12В)
- 1х Силовой ключ (P или N-канал)
- 1х Датчик расхода воды 0.3-6л./мин.
- 1х Нормально закрытый соленоидный клапан, 12V
- 1х Кнопка
- 1х Светодиод 10мм
- 1х Гнездо питания на корпус 5.5x2.1 мм (DC-022B) (или 2шт. для удобства)
- 1х DC 5.5x2.5 mm Штекер питания
- 1х 3-проводной шлейф «мама-мама»
- 2х Стойка М3*20 Nylon-black
- 1х Стойка М3*8 Nylon-black
- 2х Винт М3х6
- 1х Гайка М3 Nylon-black
- 1х Силиконовый шланг 6*8 мм
- 1х Силиконовый шланг 4*6 мм
- 2х Мягкий силиконовый провод 20AWG
- 1х ПВХ-корпус
- Стяжки
- Также вам может понадобиться USB-UART преобразователь для программирования контроллера Piranha Trema
Принцип работы и назначение модулей
Принцип работы
Модули долива могут работать как в режиме дозирования порции (по времени или по объёму жидкости), так и в режиме прямого долива (пока есть управляющий сигнал). Вне зависимости от способа управления (кнопкой или управляющим контроллером) дозирование жидкости будет производиться следующим образом:
- Если кнопка нажата кратковременно, то происходит подача порции. Размер порции и способ её измерения задаётся в коде программы модуля долива. Например, можно установить время подачи 2 секунды, или, например, объём подачи 20мл. Задание объёма возможно в случае, если установлен расходомер. Соответственно, если вы не собираетесь использовать задание порции по расходу, расходомер можно не ставить. При этом стоит отметить, что в случае, если будет превышено установленное аварийное время работы модуля, он отключится и светодиод сообщит об ошибке (см. раздел "Режимы защиты и световой индикации").
- Если кнопка удерживается, то происходит подача жидкости до момента, пока кнопка не будет отпущена. При сигнале с управляющего контроллера режим ошибки по аварийному времени также активен.
Используемые элементы
В зависимости от функционала системы, некоторые элементы можно не ставить. В таблице представлен перечень элементов, их предназначение, а также является ли их использование обязательным.
Элемент | Использование | Назначение |
Контроллер Piranha Trema | Обязательно | Управление модулем |
Насос | Обязательно | Подача жидкости |
Светодиод | Обязательно | Индикация состояния модуля |
Кнопка | Обязательно | Запуск долива вручную, сброс ошибки |
Силовой ключ / реле | Обязательно | Управление насосом |
Расходомер | Не обязательно | Определение объёма поданной жидкости |
Электромагнитный клапан | Не обязательно | Предотвращает стекание жидкости |
Электромагнитный клапан предотвращает стекание жидкости из шланга. Это актуально для ситуаций, где необходимо точное дозирование. В модуле долива воды схему можно упростить и клапан не ставить, поскольку её точное дозирование в гидропонике не так важно (имея ввиду точное дозирование речь идёт о нескольких миллилитрах - объёме шланга, который стечёт обратно в резервуар модуля).
При использовании перистальтического насоса клапан можно не ставить, поскольку из-за конструктивных особенностей насоса стекание жидкости в принципе невозможно.
В качестве ориентира приводим конфигурацию наших модулей долива.
Схема модуля долива
(картинка кликабельна)
На обратной стороне расходомера есть стрелка, указывающая направление потока. Жидкость должна двигаться строго в указанную сторону.
Подробное описание подключения элементов
Расходомер
Вывод модуля | Вывод Piranha Trema |
GND (чёрный) | GND (черная колодка) |
Vсс (красный) | Vcc (красная колодка) |
S (желтый) | 2 |
Кнопка
Вывод модуля | Вывод Piranha Trema |
G | GND (черная колодка) |
V | Vcc (красная колодка) |
S | 3 |
Управляющий контроллер
Вывод модуля (провод к Piranha ESP-32) | Вывод Piranha Trema |
G | GND (черная колодка) |
V | Vcc (красная колодка) |
S | 4 |
Силовой ключ
Вывод модуля | Вывод Piranha Trema |
G | GND (черная колодка) |
V | Vcc (красная колодка) |
S | 5 |
Светодиод
Вывод модуля | Вывод Piranha Trema |
G | GND (черная колодка) |
V | Vcc (красная колодка) |
S | 6 |
Мотор и электромагнитный клапан подключены параллельно (полярность не важна*)
Вывод мотора | Вывод силового ключа |
1 | Vout+ |
2 | Vout- |
*Полярность не важна в случае, если вы используете мембранный насос, как у нас. При применении, например, перистальтического насоса от полярности будет зависеть направление подачи жидкости.
Особенности схемы
1) Обратите внимание на установку джампера: его положение указывает, что питание нагрузки осуществляется не от колодки GVS, а от от внешнего источника.
2) Обратите внимание на подключение расходомера: порядок проводов в его колодке не соответствует порядку проводов колодки GVS.
Скетч и настройки модулей долива
Для подключения контроллера Piranha Trema к компьютеру понадобится USB-UART преобразователь и кабель microUSB. В меню "Инструменты" указываем плату "Arduino Mini".
Перед загрузкой модуль долива может быть собран, однако убедитесь, что модуль долива не подключен к управляющему блоку, ИЛИ на схему подано внешнее питание. Такие меры необходимы, чтобы вся система не начала питаться от контроллера, когда вы подключите его к компьютеру. Это может вызвать выход элементов из стоя.
Настройки и конфигурация модулей
Рассмотрим основные настройки, которые требуют комментария:
- mode - режим дозирования жидкости: по времени или по объёму. В случае дозирования по времени расходомер можно не ставить. При этом подача жидкости будет производиться заданное время (см. дальше в настройках). При дозировании по объёму необходим расходомер. При необходимости точного дозирования также рекомендуем поставить электромагнитный клапан, он предотвратит стекание жидкости после выключения насоса.
- powerDevice - устройство коммутации насоса. Вместо силового ключа (у нас на схеме) можно использовать модуль реле (например, такой).
- percentPowerPump - определяет процент мощности, подаваемой на насос. Если используется реле, то настройка не имеет смысла. Если же используется силовой ключ, то можно указать число меньше 100%, тогда насос будет работать не на полную мощность. При этом, мы не рекомендуем снижать мощность слишком сильно, т.к. это может вызвать невозможность старта насоса и/или невозможность срабатывания электромагнитного клапана.
- timeFlow - время работы насоса. Настройка актуальна при режиме дозирования "по времени".
- volumeFlow - объём жидкости, который должен перекачать насос. Настройка актуальна при режиме дозирования "по объёму".
- timePompBreak - максимальное время работы насоса, по истечении которого он отключится, а модуль начнёт сигнализировать об ошибке (см. раздел "Режимы защиты и световой индикации").
В качестве ориентира приводим настройки наших модулей долива, которые мы указывали в скетче. В таблицах ниже "-" значит, что в выбранной конфигурации параметр не актуален, однако значение (любое) должно стоять.
pH down, pH up
Режим | Значение | Пояснение |
mode | 1 | 0 - по времени, 1 - по объёму |
powerDevice | 1 | 0 - реле, 1 - силовой ключ |
percentPowerPump | 100 | % мощности |
timeFlow | - | время работы, мс. |
volumeFlow | 5 | Объём, мл |
timePompBreak | 3000 | Максимальное время (аварийное), мс |
Концентрат А, концентрат В
Режим | Значение | Пояснение |
mode | 1 | 0 - по времени, 1 - по объёму |
powerDevice | 1 | 0 - реле, 1 - силовой ключ |
percentPowerPump | 100 | % мощности |
timeFlow | - | время работы, мс. |
volumeFlow | 10 | Объём, мл |
timePompBreak | 3000 | Максимальное время (аварийное), мс |
Вода
Режим | Значение | Пояснение |
mode | 0 | 0 - по времени, 1 - по объёму |
powerDevice | 1 | 0 - реле, 1 - силовой ключ |
percentPowerPump | 100 | % мощности |
timeFlow | 10000 | время работы, мс. |
volumeFlow | 5 | Объём, мл |
timePompBreak | 35000 | Максимальное время (аварийное), мс |
Обратите внимание на задание времени: оно указывается в миллисекундах (мс). 1 сек = 1000мс. Например, если необходимо, чтобы насос работал 3 секунды, то указываем 3000.
Скетч для модуля долива
#define consuptPin 2 // Определяем № вывода Arduino, к которому подключён датчик расхода воды #define buttonPin 3 // Определяем № вывода Arduino, к которому подключёна кнопка для ручного управления. Кнопка при нажатии подаёт + #define signPin 4 // Определяем № вывода Arduino, который подключен к центральному контроллеру для получения сигнала. Контроллер подаёт + #define pumpPin 5 // Определяем № вывода Arduino, к которому подключён силовой ключ или реле для управления насосом #define ledPin 6 // Определяем № вывода Arduino, к которому подключён светодиод-индикатор // ---------- НАСТРОЙКИ ------------ // #define mode 0 // Режим дозирования жидкости. 0 - по времени, 1 - по объёму поданной жидкости (в случае если есть расходомер) #define powerDevice 1 // Используемый модуль: 0 - реле / твердотельное реле, 1 - силовой ключ #define percentPowerPump 100 // Процент мощности, подаваемой на насос (настройка имеет смысл только при работе с силовым ключом). Рекомендуется не указывать близкие к 0 значения. const uint16_t timeFlow = 10000; // Время работы насоса (в режиме дозирования по времени), мс. (2000 мс = 2 сек) const uint16_t volumeFlow = 5; // Объём жидкости, который должен перекачать насос (в режиме дозирования по объёму жидкости), мл. const uint16_t timePompBreak = 35000; // Максимальное время работы насоса, по истечении которого он отключится, мс. (10000 мс = 10 сек) const uint16_t flowImpulse = 4550; // Кол-во импульсов расходомера при прохождении 1л. Вы можете откалибровать это значение для более точного измерения объёма. // __________________________________// volatile uint16_t varCount = 0; // Определяем переменную для подсчёта количества импульсов поступивших от датчика расхода float consumption = 0.0; // Расход жидкости в текущей порции, мл bool prevSignState = false; // Предыдущее состояние сигнала управляющего контроллера bool prevButState = false; // Предыдущее состояние кнопки uint32_t startSignTime; // Время фиксации управляющего сигнала uint32_t startButTime; // Время нажатия кнопки bool pressFlag = false; // Флаг удержания (кнопки или сигнала управляющего контроллера) bool clickFlag = false; // Флаг клика (кнопки или сигнала управляющего контроллера) uint32_t timeStartPump; // Время включения насоса String message = "OK"; // Сообщение для передачи в функцию световой индикации bool pumpState = false; // Флаг состояния насос (вкл/выкл) uint32_t timeOff; // Переменная для фиксации времени выключения светодиода-индикатора uint32_t timeOn; // Переменная для фиксации времени включения светодиода-индикатора bool ledState = false; // Переменная состояния светодиода-индикатора uint16_t timeHigh = 50; // Время включения светодиода индикации, мс uint16_t timeLow = 3000; // Время выключения светодиода индикации, мс void funCountInt(void); // Определяем функцию счётчика импульсов расходомера void startFlow(void); // Определяем функцию включения насоса void stopFlow(void); // Определяем функцию выключения насоса void indication(void); // Определяем функцию работы световой индикации void setup() { delay(5000); Serial.begin(9600); // Инициируем работу последовательного порта pinMode(pumpPin, OUTPUT); // Вывод управления насосом (силовой ключ или реле) - выход pinMode(ledPin, OUTPUT); // Вывод светодиода-индикатора - выход pinMode(signPin, INPUT); // Вывод для получения сигнала с центрального контроллера (Piranha ESP-32) - вход pinMode(buttonPin, INPUT); // Вывод кнопки для ручного управления - вход pinMode(consuptPin, INPUT_PULLUP); // Вывод датчика расхода - вход uint8_t intConsupt = digitalPinToInterrupt(consuptPin); // Определяем № прерывания который использует вывод pinSensor attachInterrupt(intConsupt, funCountInt, RISING); // Назначаем функцию funCountInt как обработчик прерываний intConsupt при каждом выполнении условия RISING - переход от 0 к 1 if (timePompBreak < timeFlow) { // Если время аварийного отключения насоса меньше заданного времени работы Serial.println("Ошибка! Время аварийного отключения насоса меньше заданного времени работы. Проверьте настройки."); // Выводим сообщение об ошибке. } } void loop() { Serial.println(message); // ОБРАБОТЧИК РАСХОДОМЕРА consumption = consumption + (float)varCount / ((float)flowImpulse / 1000.0); // Определяем количество мл, прошедших через расходомер с момента предыдущего цикла varCount = 0; // Сбрасываем счётчик после того как получили от него значения // ОБРАБОТЧИК НАЖАТИЯ КНОПКИ bool nowButState = digitalRead(buttonPin); // Определяем текущее состояние кнопки if (nowButState && !prevButState) { // Если сейчас кнопка нажата и до этого была не нажата startButTime = millis(); // Фиксируем время срабатывания prevButState = true; // Обновляем флаг предыдущего состояния } if (prevButState && (startButTime + 400 <= millis()) && !pressFlag && !clickFlag) { // Если кнопка была нажата ранее и прошло 400мс и не обработан ни клик, ни удеражние if (nowButState) pressFlag = true; // Если до сих пор (спустя 400мс) кнопка удерживается, записываем флаг "удержание" else clickFlag = true; // Иначе, записываем флаг "клик" if (message != "ERROR") startFlow(); // Если нет ошибки, запускаем функцию подачи жидкости else { // Если ошибка есть stopFlow(); // Сброс всех переменных message = "OK"; // Записываем в переменную сообщения значение consumption = 0; // Сбрасываем расход digitalWrite(ledPin, LOW); // Выключаем светодиод while(digitalRead(buttonPin)){;} // Ждём, пока кнопка будет отпущена delay(100); // Задержка } } // ОБРАБОТЧИК СИГНАЛА С КОНТРОЛЛЕРА bool nowSignState = digitalRead(signPin); // Определяем текущий уровень сигнала if (nowSignState && !prevSignState && message != "ERROR") { // Если сейчас высокий уровень, а ранее был низкий и устройство не находится в ошибке delay(30); // Задержка. Отсеиваем случайные сигналы. if (digitalRead(signPin)){ // Если спустя 30мс сигнал еще есть startSignTime = millis(); // Фиксируем время prevSignState = true; // Обновляем флаг предыдущего состояния } } if (prevSignState && (startSignTime + 100 <= millis()) && !pressFlag && !clickFlag) { // Если сигнал был зафиксирован ранее и прошло 100мс и событие не обработано if (nowSignState) pressFlag = true; // Если до сих пор (спустя 100мс) сигнал еще поступает, записываем флаг "удержание" else clickFlag = true; // Иначе, записываем флаг "клик" startFlow(); // Запускаем функцию подачи жидкости } if (clickFlag) { // Если был клик (или кнопки, или управляющего сигнала) if (timeStartPump + timePompBreak < millis()) { // Если превышено максимально разрешенное время работы насоса stopFlow(); // Останавливаем насос message = "ERROR"; // Записываем в переменную значение: есть ошибка } if ((mode == 0) && (timeStartPump + timeFlow < millis())) { // Если режим дозирования - по времени и пришло время отключить насос stopFlow(); // Останавливаем насос message = "OK"; // Записываем в переменную сообщения значение } else if ((mode == 1) && (consumption >= volumeFlow)) { // Если режим дозирования - по объёму и перекаченный объём больше или равен заданному stopFlow(); // Останавливаем насос message = "OK"; // Записываем в переменную сообщения значение } } else if (pressFlag) { // Если было удержание (кнопки или управляющего сигнала) if (!nowButState && ((timeStartPump + timePompBreak) < millis())) { // Если кнопка отпущена и превышено максимально разрешенное время работы насоса stopFlow(); // Останавливаем насос message = "ERROR"; // Записываем в переменную значение: есть ошибка } if (!nowSignState && !nowButState) { // Если сигнала нет и кнопка не удерживается stopFlow(); // Останавливаем насос message = "OK"; // Записываем в переменную сообщения значение } } indication(); // Запускаем функцию световой индикации if ((message == "ERROR") || (consumption >= volumeFlow) || (timeStartPump + timePompBreak) < millis()) digitalWrite(pumpPin, LOW); // Подстраховка выключения насоса } void funCountInt() { varCount++; // Функция, увеличивающая значение счётчика импульсов с расходомера } void startFlow() { // Функция включения насоса consumption = 0; // Сбрасываем значение объёма pumpState = true; // Инвертируем флаг: насос сейчас будет включен timeStartPump = millis(); // Записываем время включения насоса message = "WORK"; // Записываем в переменную значение: идёт залив жидкости if (powerDevice == 1) analogWrite(pumpPin, map(percentPowerPump, 0, 100, 0, 255)); // Если коммутационное устройство - транзистор (силовой ключ), включаем насос, применяем ШИМ (переводим из %) else digitalWrite(pumpPin, HIGH); // Иначе (коммутационное устройство - реле) Включаем насос } void stopFlow() { // Функция выключения насоса digitalWrite(pumpPin, LOW); // Выключаем насос pressFlag = false; // Сбрасываем флаг удержания clickFlag = false; // Сбрасываем флаг клика prevSignState = false; // Сбрасываем предыдущее состояние управляющего сигнала prevButState = false; // Сбрасываем предыдущее состояние кнопки } void indication() { // Функция индикации if (message == "WORK") { // Если сообщение WORK digitalWrite(ledPin, HIGH); // Включаем светодиод-индикатор ledState = true; // Записываем флаг состояния светодиода } else { // Иначе if (message == "ERROR") { // Если сообщение ERROR timeHigh = 200; // Устанавливаем время горения светодиода 200мс timeLow = 200; // Устанавливаем время паузы светодиода 200мс } else if (message == "OK") { // Если сообщение OK timeHigh = 70; // Устанавливаем время горения светодиода 70мс timeLow = 3000; // Устанавливаем время паузы светодиода 3с } if (ledState && (timeOn + (uint32_t)timeHigh <= millis())) { // Если светодиод горит и пришло время его выключить digitalWrite(ledPin, LOW); // Выключаем светодиод timeOff = millis(); // Записываем время выключения ledState = false; // Записываем флаг - светодиод выключен } if (!ledState && (timeOff + (uint32_t)timeLow <= millis())) { // Если светодиод не горит и пришло время его включить digitalWrite(ledPin, HIGH); // Включаем светодиод timeOn = millis(); // Записываем время включения ledState = true; // Записываем флаг - светодиод включен } } }
Режимы защиты и световой индикации
Модуль долива имеет защиту от излишнего времени работы насоса. Переменная timePompBreak
отвечает за время, через которое насос отключится, а модуль начнёт сигнализировать об ошибке, мигая светодиодом. Подобная ситуация может произойти в случае, если, например, в резервуаре модуля закончилась жидкость.
Функция защиты работает при любом режиме и способе дозирования жидкости, кроме ситуации, когда подача жидкости производится удержанием кнопки.
В нормальном режиме работы светодиод вспыхивает раз в несколько секунд.
Обсуждение