КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Модуль подачи жидкости для гидропоники

Введение

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

Видео

Редактируется...

Нам понадобится

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

Количество представлено для сборки одного модуля долива. 

Принцип работы и назначение модулей

Принцип работы

Модули долива могут работать как в режиме дозирования порции (по времени или по объёму жидкости), так и в режиме прямого долива (пока есть управляющий сигнал). Вне зависимости от способа управления (кнопкой или управляющим контроллером) дозирование жидкости будет производиться следующим образом:

  • Если кнопка нажата кратковременно, то происходит подача порции. Размер порции и способ её измерения задаётся в коде программы модуля долива. Например, можно установить время подачи 2 секунды, или, например, объём подачи 20мл. Задание объёма возможно в случае, если установлен расходомер. Соответственно, если вы не собираетесь использовать задание порции по расходу, расходомер можно не ставить. При этом стоит отметить, что в случае, если будет превышено установленное аварийное время работы модуля, он отключится и светодиод сообщит об ошибке (см. раздел "Режимы защиты и световой индикации").
  • Если кнопка удерживается, то происходит подача жидкости до момента, пока кнопка не будет отпущена. При сигнале с управляющего контроллера режим ошибки по аварийному времени также активен.

Используемые элементы

В зависимости от функционала системы, некоторые элементы можно не ставить. В таблице представлен перечень элементов, их предназначение, а также является ли их использование обязательным. 

ЭлементИспользованиеНазначение
Контроллер Piranha TremaОбязательноУправление модулем
НасосОбязательноПодача жидкости
СветодиодОбязательноИндикация состояния модуля
КнопкаОбязательноЗапуск долива вручную, сброс ошибки
Силовой ключ / релеОбязательноУправление насосом
РасходомерНе обязательноОпределение объёма поданной жидкости
Электромагнитный клапанНе обязательноПредотвращает стекание жидкости

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

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

В качестве ориентира приводим конфигурацию наших модулей долива.

Схема модуля долива

(картинка кликабельна)

Схема модуля долива

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

Подробное описание подключения элементов

Расходомер

Вывод модуляВывод Piranha Trema
GND (чёрный)GND (черная колодка)
Vсс (красный)Vcc (красная колодка)
S (желтый)2

Кнопка

Вывод модуляВывод Piranha Trema
GGND (черная колодка)
VVcc (красная колодка)
S3

Управляющий контроллер

Вывод модуля (провод к Piranha ESP-32)Вывод Piranha Trema
GGND (черная колодка)
VVcc (красная колодка)
S4

Силовой ключ

Вывод модуляВывод Piranha Trema
GGND (черная колодка)
VVcc (красная колодка)
S5

Светодиод

Вывод модуляВывод Piranha Trema
GGND (черная колодка)
VVcc (красная колодка)
S6

Мотор и электромагнитный клапан подключены параллельно (полярность не важна*)

Вывод мотораВывод силового ключа
1Vout+
2Vout-

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

Особенности схемы

1) Обратите внимание на установку джампера: его положение указывает, что питание нагрузки осуществляется не от колодки GVS, а от от внешнего источника.

2) Обратите внимание на подключение расходомера: порядок проводов в его колодке не соответствует порядку проводов колодки GVS.

Скетч и настройки модулей долива

Для подключения контроллера Piranha Trema к компьютеру понадобится USB-UART преобразователь и кабель microUSB. В меню "Инструменты" указываем плату "Arduino Mini".

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

Настройки и конфигурация модулей

Рассмотрим основные настройки, которые требуют комментария:

  • mode - режим дозирования жидкости: по времени или по объёму. В случае дозирования по времени расходомер можно не ставить. При этом подача жидкости будет производиться заданное время (см. дальше в настройках). При дозировании по объёму необходим расходомер. При необходимости точного дозирования также рекомендуем поставить электромагнитный клапан, он предотвратит стекание жидкости после выключения насоса.
  • powerDevice - устройство коммутации насоса. Вместо силового ключа (у нас на схеме) можно использовать модуль реле (например, такой). 
  • percentPowerPump - определяет процент мощности, подаваемой на насос. Если используется реле, то настройка не имеет смысла. Если же используется силовой ключ, то можно указать число меньше 100%, тогда насос будет работать не на полную мощность. При этом, мы не рекомендуем снижать мощность слишком сильно, т.к. это может вызвать невозможность старта насоса и/или невозможность срабатывания электромагнитного клапана.
  • timeFlow - время работы насоса. Настройка актуальна при режиме дозирования "по времени".
  • volumeFlow - объём жидкости, который должен перекачать насос. Настройка актуальна при режиме дозирования "по объёму".
  • timePompBreak - максимальное время работы насоса, по истечении которого он отключится, а модуль начнёт сигнализировать об ошибке (см. раздел "Режимы защиты и световой индикации").

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

pH down, pH up

РежимЗначениеПояснение
mode10 - по времени, 1 - по объёму
powerDevice10 - реле, 1 - силовой ключ
percentPowerPump100% мощности
timeFlow-время работы, мс.
volumeFlow5Объём, мл
timePompBreak3000Максимальное время (аварийное), мс

Концентрат А, концентрат В

РежимЗначениеПояснение
mode10 - по времени, 1 - по объёму
powerDevice10 - реле, 1 - силовой ключ
percentPowerPump100% мощности
timeFlow-время работы, мс.
volumeFlow10Объём, мл
timePompBreak3000Максимальное время (аварийное), мс

Вода

РежимЗначениеПояснение
mode00 - по времени, 1 - по объёму
powerDevice10 - реле, 1 - силовой ключ
percentPowerPump100% мощности
timeFlow10000время работы, мс.
volumeFlow5Объём, мл
timePompBreak35000Максимальное время (аварийное), мс

Обратите внимание на задание времени: оно указывается в миллисекундах (мс). 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 отвечает за время, через которое насос отключится, а модуль начнёт сигнализировать об ошибке, мигая светодиодом. Подобная ситуация может произойти в случае, если, например, в резервуаре модуля закончилась жидкость. 

Функция защиты работает при любом режиме и способе дозирования жидкости, кроме ситуации, когда подача жидкости производится удержанием кнопки. 

В нормальном режиме работы светодиод вспыхивает раз в несколько секунд. 

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями