// библиотека для работы 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; }