#include <TFT_eSPI.h>
#include <SPI.h> // this is needed for display
#include <Wire.h> // this is needed for FT6206
#include <Adafruit_FT6206.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include "Free_Fonts.h"
#include <EEPROM.h> //Подключаем библиотеку для работы с энергонезависимой памятью
#include <OneWire.h> //Подключаем библиотеку для температурного датчика DS18B20
#include <DHTesp.h> //Подключаем библиотеку для датчика влажности и температуры
// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ctp = Adafruit_FT6206();
// The display also uses hardware SPI, plus #9 & #10
#define TFT_CS 15
#define TFT_DC 2
#define TFT_MOSI 23
#define TFT_SCLK 18
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height
//=====Button PS*
#define axis_X 26 //Ось Х подключена к Analog 0
#define axis_Y 25 //Ось Y подключена к Analog 1
#define axis_Z 34 //Ось Z (кнопка джойстика) подключена к Digital 6
#define pushLED 10 //Индикаторный светик нахожения в меню и нажатия кнопки (мигает при авариях)
unsigned long pressTime=0; //Запоминаем, когда произошло нажатие кнопки
bool prevButtonState;
int val_X, val_Y, val_Z=0; //Переменные для хранения значений осей
byte buttonState=0; //Флаг состояния нажатия кнопки
byte butLongState=0; //Флаг "удержания" кнопки, longClick
//=====Blink led*
#define BeepPin 12 //Пищалка
//=====Moisture*
#define moistIn 39 //Определяем Аналоговый вход А2 датчика влажности почвы (вилочка такая)
int moistAO=EEPROM.read(9); //Переменная влажности в % (итоговая), приравниваем, чтобы при старте неполивать :(
int moistTmp=0; //Переменная влажности в "попугаях" 0-1023 (промежуточная)
byte autoIrrig=0; //Флаг состояния автополива(необходимо поднять при включении помпы)
byte tIrrig=0; //Переменная интервала влючения датчика влажности грунта
//=====VoltMeter*
int voltInput=36; //Определяем Аналоговый вход A3 для измерения напряжения
float vout=0.0;
float vin=0.0;
float R1=32140.0; //Номинал первого резистора, тот которй ПОСЛЕДОВАТЕЛЬНО в цепи измерения
float R2=12810.0; //А тут номинал второго резистора, что ПАРАЛЕЛЬНО измерению и к земле
int valueVo=0;
//=====Led_Relay*
#define OUT0 2 //Используем цифровой ПОРТ 2 включения датчика измернеия влажности почвы
#define OUT1 3 //Используем цифровой ПОРТ 3 помпочки автополива
//#define OUT2 39 //Используем аналоговый ПОРТ A6 включения реле вентиляции теплички
//=====Humidity*
#define dhtPin 5 //Определяем цифровой ПОРТ 5 датчика влажности воздуха в тепличке (хреновая)
#define dhtType DHT11 //Ранее был А6 (он РАСПАЯН и обрезан, красный короткий)
DHTesp dht;
float hmdt;
//=====Termo_Sensor*
int tempPin=4; //Определяем порт шины OneWire (IC) для температурного датчика DS18B20
OneWire ds(tempPin); //Создаем объект для работы с термометром
byte flagDallas=0; //Флаг для обработки показаний с датчиков Dallas
byte data[12];
byte addr1[8]={0x28, 0x33, 0x4B, 0xEA, 0x05, 0x00, 0x00, 0x54}; //адрес датчика DS18B20_в парничке
byte addr2[8]={0x28, 0xFF, 0xB5, 0x24, 0x54, 0x14, 0x01, 0xC7}; //адрес датчика DS18B20_на улице
unsigned int raw; //Если экранированный кабель, можно подключать до 32 термо-датчиков DS18B20
float temp[2]; //Температура на уличке \ в парничке
//=====Rtc_Clock*
#define DS1307_I2C_ADDRESS 0x68
byte seconds, minutes, hours, day, date, month, year;
byte decToBcd (byte val){return((val/10*16)+(val%10));}
//=====Timer*
byte IrrigStat=0; //Флаг полива, если включена, то 1 конечное-жеж
byte TempStat=0; //Флаг вентиляции, если включена, то 1 конечное-жеж
byte isIrrigt=0; //Флаг для обработки необходимости включить автополив (по таймеру менюшному)
byte isTemp=0; //Флаг для обработки необходимости включить вентиляцию (по томуже таймеру)
byte isNight=0; //Если включаем на ночь, т.е. начальное время больше конечного
//=====Menu*
byte winMe; //Номер экрана меню
byte setHorClockOn; //Часы включения Таймера
byte setMinClockOn; //Минуты включения Таймера
byte setHorClockOff; //Часы вЫключения Таймера
byte setMinClockOff; //Минуты вЫключения Таймера
byte setTemptGist; //Гистерезис температур (по 1/2 в каждую сторону)
byte setTemptIn; //Максимальная температура воздуха в парничке для вентиляции
byte setTemptInOnOff; //Флаг Включения/Выключения НЕОЬХОДИМОСТИ "контроля" температуры воздуха в тепличке
byte setMoistIn; //Максимальная влажность воздуха в тепличке для вентиляции
byte setMoistInOnOff; //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контроля влажности воздуха
byte setSoiltIn; //Минимальная влажность почвы в парничке для полива
byte setSoiltTime; //Интервал работы датчика влажности грунта
byte setSoiltIrrig; //Интервал работы помпочки автополива
byte setSoiltInOnOff; //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контроля влажности грунта
byte setVoltCt; //Минимальная напряжение на батарее для замены
byte setVoltCtOnOff; //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контрорля напряжения батареечки ценной
//=====Delays*
unsigned long prvMlsTm=0; //Предыдущее показание миллисекунд для обновления показания часов
unsigned long prvMlsVo=0; //Предыдущее показание обновления показания вольтметра
unsigned long prvMlsMoist=0; //Предыдущее показание обновления показания влажности почвы
unsigned long prvMlsIrrig=0; //Предыдущее показание обновления показания автополива
unsigned long prvMlsTemp=0; //Предыдущее показание обновления температур
unsigned long prvMlsTimer=0; //Предыдущее показание обновления таймера
unsigned long prvMlsbLink=0; //Предыдущее показание миллисекунд для обновления "blink led"
unsigned long tzad=millis(); //Переменная задержки (пока для ds18b20, ds.write)
unsigned long zadM[4]; //Массив для задержек
const long zadTime[]={100,500,750,1000,2000,3000,5000,10000,60000};
const char menuTxt[6][17]={"set Timer Soil >","set Timer Temp >",
"set Irrigation >","set TermoRele >>",
"set VoltControl>","set Clock >>>>>>"}; //Массив с наименованиями для экрана
void setup(){
Wire.begin();
tone(BeepPin, 5040, 1000);
Serial.begin(9600); //Инициализация Serial-порта
dht.setup(dhtPin, DHTesp::DHT11); //Инициализация датчика влажности DHT-11
pinMode(voltInput,INPUT); //Определяем вход вольтметра
pinMode(moistIn, INPUT); //Определяем вход измерителя влажности почвы
pinMode(axis_Z, INPUT); //Задаем как вход нажатия кнопки джойстика
digitalWrite(axis_Z,HIGH); //ПОдтягиваем внутреннее сопротивнление ардуинки
pinMode(pushLED,OUTPUT); //Определяем светик нажатия и нахождения в меню
pinMode(OUT0,OUTPUT); //Определяем инверсные выходы реле датчика влажности почвы
pinMode(OUT1,OUTPUT); //Определяем инверсные выходы реле помпы автополива
// pinMode(bLink,OUTPUT);
digitalWrite(OUT0,HIGH); //Определяем инверсный выход в HIGH реле сенсора почвы
digitalWrite(OUT1,HIGH); //Определяем инверсный выход в HIGH реле помпы полива
digitalWrite(pushLED,LOW); //Определяем инверсный выход в HIGH реле помпы полива
// digitalWrite(bLink,HIGH);
// tft.initR(INITR_BLACKTAB);
tft.begin(); //Initialize a ST7735S chip, black tab
tft.fillScreen(TFT_BLACK); //Clear screen
}
//========== Обработка нажатия кнопки и джойстика PS
void buttonPS(){
val_X=analogRead(axis_X); //Считываем аналоговое значение оси Х
val_Y=analogRead(axis_Y); //Считываем аналоговое значение оси Y
val_Z=digitalRead(axis_Z); //Считываем цифровое значение оси Z (кнопка)
val_Z=val_Z^1; //Инвертируем значение
bool buttonState; //Читаем кнопку
buttonState=(digitalRead(axis_Z)==LOW);
if (buttonState==HIGH){
digitalWrite(pushLED,HIGH);
} else {
digitalWrite(pushLED,LOW);}
if(!prevButtonState && buttonState){ //Была не нажата, стала нажата
pressTime=millis();}
if(!buttonState)pressTime=0; //При отпускании кнопки - сразу останавливаем осчет
if(pressTime&&(millis()-pressTime>1000)){ //Если с момента нажатия прошло более 1- секунды
butLongState=1; //Поднимаем флаг "удержания" кнопки
tone(BeepPin, 240, 100); //Попискиваем при этом
pressTime=0;} //Останавливаем отсчет что-бы не переключить несколько раз по одному нажатию
prevButtonState=buttonState; //Запомнили "прошлое" состояние кнопки
}
//========== Обработка Входа в Меню
void setUp(){
if (butLongState==1){ //ВХОД В МЕНЮ и проверка блокировки от возврата. Принимаем код клавиши
digitalWrite(OUT0,HIGH); digitalWrite(OUT1,HIGH);//Отключаем датчик измерения влажности почвы и работу помпы !!!!
menu();} //Если флаг longClick идем в меню
}
//=========== Обработка Меню, выбор экрана
void menu(){
tft.fillScreen(TFT_BLUE); //Clear screen
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLUE);
byte pos=0;
while(1){ //Бесконечный цикл
// bLinkLed();
butLongState=0;
buttonPS();
digitalWrite(pushLED,HIGH);
tft.setCursor(5,55);
tft.print(pos+1); //Печатаем номер
tft.print(". ");
tft.print(menuTxt[pos]); //Печатаем название
if (val_Y>923 && pos<5) {pos++;}
else if (pos==5 && val_Y>923) {pos=0;}
if (val_Y<123 && pos>0) {pos--;} //Уменьшить/увеличить позицию окна меню
else if (pos==0 && val_Y<123) {pos=5;} //Уменьшить/увеличить (циклично)
winMe=pos; //Переменная номера таймера
if (butLongState==1 && pos==0) setOnOff(); //"set Timer Soil >" 0
if (butLongState==1 && pos==1) setOnOff(); //"set Timer Temp >" 1
if (butLongState==1 && pos==2) setIrrigation(); //"set Irrigation >" 2
if (butLongState==1 && pos==3) setTermoRele(); //"set TermoRele >>" 3
if (butLongState==1 && pos==4) setVoltControl(); //"set VoltControl>" 4
if (butLongState==1 && pos==5) setClock(); //"set Clock >>>>>>" 5
if (val_X>923){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
tone(BeepPin, 540, 200); break;}
}
}
//=========== Обработка Меню, параметры и настройка таймеров
void setOnOff(){
byte pos=0;
setHorClockOn= EEPROM.read(winMe*4+1); //Считываем записанные значения таймеров
setMinClockOn= EEPROM.read(winMe*4+2); //Адрес определяется как номер таймера*4 + четыре ячейки
setHorClockOff= EEPROM.read(winMe*4+3);
setMinClockOff= EEPROM.read(winMe*4+4);
tft.fillScreen(TFT_RED); //Clear screen
while(1){ //Бесконечный цикл
// while(buttonState==LOW){
butLongState=0; //Обнуляем флаг longClick
buttonPS();
digitalWrite(pushLED,HIGH);
if (millis()-prvMlsTimer>zadTime[7]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setTextSize(1);
tft.setCursor(45,40);
tft.setTextColor(TFT_YELLOW,TFT_RED);
tft.print(" On Off");
tft.setCursor(5,150);
tft.setTextColor(TFT_GREEN, TFT_RED);
tft.print("=== push to save ===");}
tft.setCursor(5,55);
tft.print(winMe+1,DEC); //Печатаем номер программы таймера
tft.print("|");
tft.print(pos/3,DEC); //Печатаем позицию настройки параметра (замена blink)
tft.print(" ->");
tft.setCursor(45,55); //Выводим инфу о часах и минутах
if (setHorClockOn<10) tft.print("0");
tft.print(setHorClockOn,DEC);
tft.print(":");
if (setMinClockOn<10) tft.print("0");
tft.print(setMinClockOn,DEC);
tft.print(" ");
if (setHorClockOff<10) tft.print("0");
tft.print(setHorClockOff,DEC);
tft.print(":");
if (setMinClockOff<10) tft.print("0");
tft.print(setMinClockOff,DEC);
tft.setCursor(pos,0); //Устанавливаем курсор согласно позиции
if (val_X>923 && pos==3){ //Выход из подменю без сохранения
tft.fillScreen(TFT_BLACK); //Clear screen
return; winMe=pos;}
if (pos<3) pos=3;
if (val_X<123 && pos<11) pos += 3; //Крутим позицию право-лево
else if (val_X>923 && pos>3) pos -= 3;
else if (pos==3 && val_Y>1020) setHorClockOn++; //Крутим значения
else if (pos==3 && val_Y<10) setHorClockOn--;
else if (pos==6 && val_Y>1020) setMinClockOn++;
else if (pos==6 && val_Y<10) setMinClockOn--;
else if (pos==9 && val_Y>1020) setHorClockOff++;
else if (pos==9 && val_Y<10) setHorClockOff--;
else if (pos==12 && val_Y>1020) setMinClockOff++;
else if (pos==12 && val_Y<10) setMinClockOff--;
if (setHorClockOn>23) setHorClockOn=0; //Ограничиваем значения
else if (setMinClockOn>59) setMinClockOn=0;
else if (setHorClockOff>23) setHorClockOff=0;
else if (setMinClockOff>59) setMinClockOff=0;
if (butLongState==1){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
break;}
} //Конец цикла
tft.fillScreen(TFT_BLACK); //Clear screen
EEPROM.write(winMe*4+1, setHorClockOn); //Записываем НОВЫЕ значения
EEPROM.write(winMe*4+2, setMinClockOn);
EEPROM.write(winMe*4+3, setHorClockOff);
EEPROM.write(winMe*4+4, setMinClockOff);
tft.setCursor(25,105);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(1);
tft.print("== Saved =="); tone(BeepPin, 3040, 500);
delay(zadTime[4]);
}
//=========== Обработка Меню, параметры и настройка автополива (влажность грунта)
void setIrrigation(){
byte pos=0;
setSoiltIn= EEPROM.read(winMe*4+1); //Считываем записанные значения
setSoiltTime= EEPROM.read(winMe*4+2); //Адрес определяется как номер *2 + две ячейки
setSoiltIrrig= EEPROM.read(winMe*4+3);
setSoiltInOnOff= EEPROM.read(winMe*4+4);
tft.fillScreen(TFT_RED); //Clear screen
while(1){ //Бесконечный цикл
butLongState=0; //Обнуляем флаг longClick
buttonPS();
digitalWrite(pushLED,HIGH);
tft.setTextSize(1);
if (millis()-prvMlsTimer>zadTime[7]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setCursor(45,40);
tft.setTextColor(TFT_YELLOW,TFT_RED);
tft.print("Soil% Tsens");
tft.setCursor(45,70);
tft.setTextColor(TFT_YELLOW,TFT_RED);
tft.print("Tpomp On/Of");
tft.setCursor(5,150);
tft.setTextColor(TFT_GREEN, TFT_RED);
tft.print("=== push to save ===");}
tft.setCursor(5,55);
tft.print(winMe+1,DEC); //Печатаем номер программы
tft.print("|");
tft.print(pos/3,DEC); //Печатаем позицию настройки параметра (замена blink)
tft.print(" ->");
tft.setCursor(45,55); //Выводим инфу
if (setSoiltIn<10) tft.print("0");
tft.print(setSoiltIn,DEC);
tft.print(" : ");
if (setSoiltTime<10) tft.print("0");
tft.print(setSoiltTime,DEC);
tft.setCursor(45,85);
if (setSoiltIrrig<10) tft.print("0");
tft.print(setSoiltIrrig,DEC);
tft.print(" : ");
tft.print(setSoiltInOnOff,DEC);
tft.setCursor(pos,0); //Устанавливаем курсор согласно позиции
if (val_X>923 && pos==3){ //Выход из подменю без сохранения
tft.fillScreen(TFT_BLACK); //Clear screen
return;}
if (pos<3) pos=3;
if (val_X<123 && pos<11) pos += 3; //Крутим позицию право-лево
else if (val_X>923 && pos>3) pos -= 3;
else if (pos==3 && val_Y>1020) setSoiltIn++; //Крутим значения
else if (pos==3 && val_Y<10) setSoiltIn--;
else if (pos==6 && val_Y>1020) setSoiltTime++;
else if (pos==6 && val_Y<10) setSoiltTime--;
else if (pos==9 && val_Y>1020) setSoiltIrrig++;
else if (pos==9 && val_Y<10) setSoiltIrrig--;
else if (pos==12 && val_Y>1020) setSoiltInOnOff++;
else if (pos==12 && val_Y<10) setSoiltInOnOff--;
if (setSoiltIn>99) setSoiltIn=0; //Ограничиваем значения
if (setSoiltTime>59) setSoiltTime=0;
if (setSoiltIrrig>19) setSoiltIrrig=0;
if (setSoiltInOnOff>1) setSoiltInOnOff=0;
if (butLongState==1){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
break;}
} //Конец цикла
tft.fillScreen(TFT_BLACK); //Clear screen
EEPROM.write(winMe*4+1, setSoiltIn); //Записываем НОВЫЕ значения
EEPROM.write(winMe*4+2, setSoiltTime);
EEPROM.write(winMe*4+3, setSoiltIrrig);
EEPROM.write(winMe*4+4, setSoiltInOnOff);
tft.setCursor(25,105);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(1);
tft.print("== Saved =="); tone(BeepPin, 3040, 500);
delay(zadTime[4]);
}
//=========== Обработка Меню, параметры и настройка термореле (вентиляция)
void setTermoRele(){
byte pos=0;
setTemptIn= EEPROM.read(winMe*4+1); //Считываем записанные значения
setTemptGist= EEPROM.read(winMe*4+2); //Адрес определяется как номер *4 + четыре ячейки
setTemptInOnOff= EEPROM.read(winMe*4+3);
tft.fillScreen(TFT_RED); //Clear screen
while(1){ //Бесконечный цикл
// while(buttonState==LOW){
butLongState=0; //Обнуляем флаг longClick
buttonPS();
digitalWrite(pushLED,HIGH);
if (millis()-prvMlsTimer>zadTime[7]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setTextSize(1);
tft.setCursor(45,40);
tft.setTextColor(TFT_YELLOW,TFT_RED);
tft.print("Temp Gst On/f");
tft.setCursor(5,150);
tft.setTextColor(TFT_GREEN, TFT_RED);
tft.print("=== push to save ===");}
tft.setCursor(5,55);
tft.print(winMe+1,DEC); //Печатаем номер программы
tft.print("|");
tft.print(pos/3,DEC); //Печатаем позицию настройки параметра (замена blink)
tft.print(" ->");
tft.setCursor(45,55); //Выводим инфу о часах и минутах
if (setTemptIn<10) tft.print("0");
tft.print(setTemptIn,DEC);
tft.print(" : ");
tft.print(setTemptGist,DEC);
tft.print(" : ");
tft.print(setTemptInOnOff,DEC);
tft.setCursor(pos,0); //Устанавливаем курсор согласно позиции
if (val_X>923 && pos==3){ //Выход из подменю без сохранения
tft.fillScreen(TFT_BLACK); //Clear screen
return;}
if (pos<3) pos=3;
if (val_X<123 && pos<8) pos += 3; //Крутим позицию право-лево
else if (val_X>923 && pos>3) pos -= 3;
else if (pos==3 && val_Y>1020) setTemptIn++; //Крутим значения
else if (pos==3 && val_Y<10) setTemptIn--;
else if (pos==6 && val_Y>1020) setTemptGist++;
else if (pos==6 && val_Y<10) setTemptGist--;
else if (pos==9 && val_Y>1020) setTemptInOnOff++;
else if (pos==9 && val_Y<10) setTemptInOnOff--;
if (setTemptIn>50) setTemptIn=0; //Ограничиваем значения
if (setTemptGist>9) setTemptGist=0;
if (setTemptInOnOff>1) setTemptInOnOff=0;
if (butLongState==1){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
break;}
} //Конец цикла
tft.fillScreen(TFT_BLACK); //Clear screen
EEPROM.write(winMe*4+1, setTemptIn); //Записываем НОВЫЕ значения
EEPROM.write(winMe*4+2, setTemptGist);
EEPROM.write(winMe*4+3, setTemptInOnOff);
tft.setCursor(25,105);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(1);
tft.print("== Saved =="); tone(BeepPin, 3040, 500);
delay(zadTime[4]);
}
//=========== Обработка Меню, параметры и настройка ВольтКонтроля (замена батареи)
void setVoltControl(){
byte pos=0;
setVoltCt= EEPROM.read(winMe*4+1); //Считываем записанные значения
setVoltCtOnOff= EEPROM.read(winMe*4+2); //Адрес определяется как номер *4 + четыре ячейки
tft.fillScreen(TFT_RED); //Clear screen
while(1){ //Бесконечный цикл
// while(buttonState==LOW){
butLongState=0; //Обнуляем флаг longClick
buttonPS();
digitalWrite(pushLED,HIGH);
if (millis()-prvMlsTimer>zadTime[7]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setTextSize(1);
tft.setCursor(40,40);
tft.setTextColor(TFT_YELLOW,TFT_RED);
tft.print("volt On/Off");
tft.setCursor(5,150);
tft.setTextColor(TFT_GREEN, TFT_RED);
tft.print("=== push to save ===");}
tft.setCursor(5,55);
tft.print(winMe+1,DEC); //Печатаем номер программы
tft.print("|");
tft.print(pos/3,DEC); //Печатаем позицию настройки параметра (замена blink)
tft.print(" ->");
tft.setCursor(45,55); //Выводим инфу о часах и минутах
if (setVoltCt<10) tft.print("0");
tft.print(setVoltCt,DEC);
tft.print(" : ");
tft.print(setVoltCtOnOff,DEC);
tft.setCursor(pos,0); //Устанавливаем курсор согласно позиции
if (val_X>923 && pos==3){ //Выход из подменю без сохранения
tft.fillScreen(TFT_BLACK); //Clear screen
return;}
if (pos<3) pos=3;
if (val_X<123 && pos<5) pos += 3; //Крутим позицию право-лево
else if (val_X>923 && pos>3) pos -= 3;
else if (pos==3 && val_Y>1020) setVoltCt++; //Крутим значения
else if (pos==3 && val_Y<10) setVoltCt--;
else if (pos==6 && val_Y>1020) setVoltCtOnOff++;
else if (pos==6 && val_Y<10) setVoltCtOnOff--;
if (setVoltCt>15) setVoltCt=7; //Ограничиваем значения
else if (setVoltCt<7) setVoltCt=15;
if (setVoltCtOnOff>1) setVoltCtOnOff=0;
if (butLongState==1){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
break;}
} //Конец цикла
tft.fillScreen(TFT_BLACK); //Clear screen
EEPROM.write(winMe*4+1, setVoltCt); //Записываем НОВЫЕ значения
EEPROM.write(winMe*4+2, setVoltCtOnOff);
tft.setCursor(25,105);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(1);
tft.print("== Saved =="); tone(BeepPin, 3040, 500);
delay(zadTime[4]);
}
//=========== Обработка Меню, параметры установки времени
void setClock(){
byte pos=0;
tft.fillScreen(TFT_RED); //Clear screen
while(1){ //Бесконечный цикл
// while(buttonState==LOW){
butLongState=0; //Обнуляем флаг longClick
buttonPS();
digitalWrite(pushLED,HIGH);
if (millis()-prvMlsTimer>zadTime[7]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setTextSize(1);
tft.setCursor(5,150);
tft.setTextColor(TFT_GREEN, TFT_RED);
tft.print("=== push to save ===");}
tft.setCursor(5,55);
tft.print(winMe+1,DEC); //Печатаем номер программы
tft.print("|");
tft.print(pos/3,DEC); //Печатаем позицию настройки параметра (замена blink)
tft.print(" ->");
tft.setCursor(45,55);
if (hours<10) tft.print("0");
tft.print(hours);
tft.print(":");
if (minutes<10) tft.print("0");
tft.print(minutes);
tft.print(":");
if (seconds<10) tft.print("0");
tft.print(seconds);
tft.setCursor(5,75);
if (day<10) tft.print("0");
tft.print(day);
tft.print(" | ");
if (date<10) tft.print("0");
tft.print(date);
tft.print("/");
if (month<10) tft.print("0");
tft.print(month);
tft.print("/");
if (year<10) tft.print("0");
tft.print(year);
tft.setCursor(pos,0);
if (val_X>923 && pos==3){ //Выход из подменю без сохранения
tft.fillScreen(TFT_BLACK); //Clear screen
return;}
if (pos<3) pos=3;
if (val_X<123 && pos<21) pos += 3; //Крутим позицию право-лево
else if (val_X>923 && pos>3) pos -= 3;
else if (pos==3 && val_Y>1020) hours++; //Крутим значения
else if (pos==3 && val_Y<10) hours--;
else if (pos==6 && val_Y>1020) minutes++;
else if (pos==6 && val_Y<10) minutes--;
else if (pos==9 && val_Y>1020) seconds++;
else if (pos==9 && val_Y<10) seconds--;
else if (pos==12 && val_Y>1020) day++;
else if (pos==12 && val_Y<10) day--;
else if (pos==15 && val_Y>1020) date++;
else if (pos==15 && val_Y<10) date--;
else if (pos==18 && val_Y>1020) month++;
else if (pos==18 && val_Y<10) month--;
else if (pos==21 && val_Y>1020) year++;
else if (pos==21 && val_Y<10) year--;
if (hours>23) hours=0;
else if (minutes>59) minutes=0;
else if (seconds>59) seconds=0;
else if (day>7) day=1;
else if (date>31) date=1;
else if (month>12) month=1;
else if (year>99) year=0;
if (butLongState==1){ //Выход из меню с проверкой и установкой блокировки от возврата
tft.fillScreen(TFT_BLACK); //Clear screen
break;}
}
tft.fillScreen(TFT_BLACK);
setRtc(seconds, minutes, hours, day, date, month, year);
tft.setCursor(25,105);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(1);
tft.print("== Saved =="); tone(BeepPin, 3040, 500);
delay(zadTime[4]);
}
//========== Обработка таймера включения датчика Влажности почвы, измерение и автополив
void irrigation(){
if (EEPROM.read(12)==1){ //Если установен флаг необходимости проверки датчика влажности почвы (в меню)
if (seconds<EEPROM.read(10)){ //Сверяем значение текущего времени с интервалом вклчюения датчика
digitalWrite(OUT0,LOW); tIrrig=1;} //Включаем реле датчика измерения влажности почвы, поднимаем флаг интервала
else if (seconds>EEPROM.read(10)){ //Ежели время непришло, реле разомкнуто и некорродирует :) флаг низкий
digitalWrite(OUT0,HIGH); tIrrig=0;}
//========== Считывание Влажности почвы
if (tIrrig==1){ //Если датчик влажности включен
if (millis()-prvMlsIrrig>zadTime[1]){ //Проверяем интервал для обновления
prvMlsIrrig=millis();
moistTmp=analogRead(moistIn); //Считываем показатели влажности почвы
moistAO=map(moistTmp,0,1023,99.9,0);} //Преобразуя в процентное соотношение :)
}
}
//========== Обработка помпы автополива
if (IrrigStat==1){ //Если таймер необходимости автополива в меню (день) задан и
if (moistAO<EEPROM.read(9)&&tIrrig==0){autoIrrig=1;} //Если показания влажности почвы МЕНЬШЕ сохраненных
// tone(BeepPin, 9040, 100); //И датчик влажности отключен, поднимаем флаг необходимости полива
else if (moistAO>EEPROM.read(9)){autoIrrig=0;} //Ежели показания ВЫШЕ то ничего неделаем и флаг держим низким
if (autoIrrig==1){ //Если флажок необходимости автополива поднят
digitalWrite(OUT1,LOW); //tone(BeepPin, 5040, 800); //Включаем помпочку на время из заданного в меню и попискиваем
if (millis()-prvMlsIrrig>EEPROM.read(11)*1000){ //Проверяем интервал для обновления
prvMlsIrrig=millis();
autoIrrig=0; moistAO=EEPROM.read(9);}}
if (autoIrrig==0){
digitalWrite(OUT1,HIGH);}
}
}
//========== Считывание Влажности воздуха
void humiDity(){
hmdt=(dht.getHumidity()+11); //Reading temperature or humidity takes about 250 milliseconds!
}
//========== Считывание напряжения АКБ
void voltM(){
valueVo=analogRead(voltInput); //Само собой считываем Аналоговый вход (с делителя)
vout=(valueVo*5.00)/1023.0; //Делим именно на 1023, а 5.00 - это опроное "стабилизированное" питание
vin=vout/0.284983; //vin=vout/(R2/(R1+R2));
if (vin<0.09) {vin=0.0;} //Округляем с высокой точностью
if (EEPROM.read(18)==1){ //Если установен флаг необходимости контроля напряжения батареи (в меню)
if (vin<EEPROM.read(17)){
bLinkLed();
tone(BeepPin, 3040, 100);} //Пищщим как угорелые и просим заменить батарейку!!!
}
// Serial.println(vin);
}
//=========== Считывание температур
void dallas(){
ds.reset();
ds.write(0xCC); //Команда инициации
ds.write(0x44); //Start conversion, with parasite power on at the end
tzad=millis()+750; flagDallas=1;}
float DS18B20(byte *adres){
ds.reset();
ds.select(adres);
ds.write(0xBE); //Read Scratchpad
for (byte i=0; i<9; i++) data[i]=ds.read(); //We need 9 bytes
int raw=(data[1]<<8) | data[0]; //Переводим в температуру
float celsius=(float)raw/16.0; //Для ds18b20 делим на "16", для ds18s20 на "2"
return celsius;
}
//=========== Обработка Термо-Реле
void tempDallas(){
if (tzad<millis()&&flagDallas==1){
temp[0]=DS18B20(addr1);
temp[1]=DS18B20(addr2);
prvMlsTemp=millis();
flagDallas=0;}
// if (TempStat==1){ //Если таймер необходимости вентиляции в меню (день) задан и
// if (temp[1]>EEPROM.read(13)-(EEPROM.read(14)/2)){ //Если температура в тепличке выше сохранённой в ЕПРОМ и гистерезис пополам
// digitalWrite(OUT2,LOW);} //Реле вентиляции включается
// else if (temp[1]<EEPROM.read(13)+(EEPROM.read(14)/2)){ //Если температура в тепличке ниже сохранённой в ЕПРОМ и гистерезис пополам
// digitalWrite(OUT2,HIGH);} //Реле вентиляции включается
// }
}
//=========== Обработка установки RTC часов
void setRtc(byte seconds, byte minutes, byte hours, byte day, byte date, byte month, byte year){
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x00);
Wire.write(decToBcd(seconds));
Wire.write(decToBcd(minutes));
Wire.write(decToBcd(hours));
Wire.write(decToBcd(day));
Wire.write(decToBcd(date));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
}
//=========== Обработка RTC часов
void timeRtc(){
Wire.beginTransmission(DS1307_I2C_ADDRESS); //104 is DS3231 device address
Wire.write(0x00); //Start at register 0
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7); //Request seven bytes
if(Wire.available()) {
seconds = Wire.read(); //Get second
minutes = Wire.read(); //Get minute
hours = Wire.read(); //Get hour
day = Wire.read();
date = Wire.read();
month = Wire.read(); //Get month
year = Wire.read();
seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); //Convert BCD to decimal
minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
hours = (((hours & B00110000)>>4)*10 + (hours & B00001111)); //Convert BCD to decimal (assume 24 hour mode)
day = (day & B00000111); // 1-7
date = (((date & B00110000)>>4)*10 + (date & B00001111)); //Convert BCD to decimal 1-31
month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
year = (((year & B11110000)>>4)*10 + (year & B00001111));
}
}
//=========== Обработка Таймеров
void timer(){
int fulMin=hours*60+minutes; //Переводим часы + минуты к полным минутам
int fulMinOn1= EEPROM.read(1)*60+EEPROM.read(2); //Переводим часы + минуты включения Soil реле к полным минутам
int fulMinOff1= EEPROM.read(3)*60+EEPROM.read(4); //Переводим часы + минуты выключения Soil реле к полным минутам
int fulMinOn2= EEPROM.read(5)*60+EEPROM.read(6); //Переводим часы + минуты включения Temp реле к полным минутам
int fulMinOff2= EEPROM.read(7)*60+EEPROM.read(8); //Переводим часы + минуты выключения Temp реле к полным минутам
//========== Таймер №1 (автополивалочка)
if (fulMinOn1>fulMinOff1) {isNight=1;} //Если ночное время
if (isNight==0) { //Если день
if (fulMin>=fulMinOn1&&fulMin<fulMinOff1) {isIrrigt=1;} //Проверяем интервал
else {isIrrigt=0;} //Если необходимо включить автополив
} else { //Если ночь
if(fulMin-fulMinOn1>=0) {isIrrigt=1;} //Если больше или равно верхнему значению, то необходимо включить автополив
else { //Если необходимо включить автополив
if(fulMin<fulMinOff1) {isIrrigt=1;} //Если меньше нижнего значения, то необходимо включить автополив
else {isIrrigt=0;} //Если необходимо включить автополив
}
}
if((isIrrigt==1)&&(IrrigStat==0)){ //Если автополив еще не включен и выставлен флаг необходимости включить
{IrrigStat=1;}
} else {
if(isIrrigt==0&&IrrigStat==1){
{IrrigStat=0;}
}
}
//========== Таймер №2 (вентиялция парничковых газов)
if (fulMinOn2>fulMinOff2) {isNight=1;} //Если ночное время
if (isNight==0){ //Если день
if (fulMin>=fulMinOn2&&fulMin<fulMinOff2) {isTemp=1;} //Проверяем интервал
else {isTemp=0;} //Если необходимо включить вентиляцию
} else { //Если ночь
if(fulMin-fulMinOn2>=0) {isTemp=1;} //Если больше или равно верхнему значению, то необходимо включить вентиляцию
else { //Если необходимо включить вентиляцию
if(fulMin<fulMinOff2) {isTemp=1;} //Если меньше нижнего значения, то необходимо включить вентиляцию
else {isTemp=0;} //Если необходимо включить вентиляцию
}
}
if((isTemp==1)&&(TempStat==0)){ //Если вентиляция еще не включена и выставлен флаг необходимости включить
{TempStat=1;}
} else {
if(isTemp==0&&TempStat==1){
{TempStat=0;}
}
}
}
//=========== Обработка Аврийного индикатора
void bLinkLed(){
if (millis()-prvMlsbLink>zadTime[1]){ //Проверяем интервал для обновления
prvMlsbLink=millis(); //Установка задержки
digitalWrite(pushLED,!digitalRead(pushLED));} //Инверсия значения
}
//=========== Обработки печати и вывода на дисплейчик (часы)
void printTime() {
if (millis()-prvMlsTm>zadTime[3]){ //Проверяем интервал для обновления часов
prvMlsTm=millis(); //Вызываем ф-цию вывода времени на экран
tft.setTextSize(2);
tft.setCursor(15,2);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
if (hours<10) {tft.print(0);tft.print(hours);} else {tft.print(hours);}
tft.print(":");
if (minutes<10) {tft.print(0);tft.print(minutes);} else {tft.print(minutes);}
tft.print(":");
if (seconds<10) {tft.print(0);tft.print(seconds);} else {tft.print(seconds);}
}
}
//=========== Обработки печати и вывода на дисплейчик (напряжения, температуры, давление)
void printDin(){
if (millis()-prvMlsVo>zadTime[6]){ //Проверяем интервал для обновления показателй динамичных
prvMlsVo=millis();
tft.setTextSize(2);
tft.setCursor(0,40);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.print(temp[0],1);
tft.setTextSize(3);
tft.setCursor(55,40);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.print(temp[1],1);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setCursor(55,70);
tft.print(hmdt,1);
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.setCursor(55,100);
tft.print((moistAO*1.001),1);
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.setCursor(55,130);
tft.print(vin,1);
}
}
//=========== Обработки печати и вывода на дисплейчик СТАТИКИ
void printStat(){
if (millis()-prvMlsTimer>zadTime[8]){ //Проверяем интервал для обновления
prvMlsTimer=millis();
tft.setTextSize(1);
// tft.setCursor(0,45);
// tft.setTextColor(TFT_YELLOW);
// tft.print("Temp C: ");
tft.setCursor(0,75);
tft.setTextColor(TFT_YELLOW);
tft.print("Atmo %: ");
tft.setCursor(0,105);
tft.setTextColor(TFT_YELLOW);
tft.print("Soil %: ");
tft.setCursor(0,135);
tft.setTextColor(TFT_YELLOW);
tft.print("Volt V: ");
tft.drawLine(0,35, tft.width()-1, 35, TFT_WHITE);
tft.drawLine(0,65, tft.width()-1, 65, TFT_WHITE);
tft.drawLine(0,95, tft.width()-1, 95, TFT_WHITE);
tft.drawLine(0,125, tft.width()-1, 125, TFT_WHITE);
tft.drawLine(0,155, tft.width()-1, 155, TFT_WHITE);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(1);
tft.setCursor(15,22);
tft.setTextColor(TFT_GREEN,TFT_BLACK);
if (date<10) {tft.print(0);tft.print(date);} else {tft.print(date);}
tft.print("/");
if (month==1){tft.print ("Jan");}
if (month==2){tft.print ("Feb");}
if (month==3){tft.print ("Mar");}
if (month==4){tft.print ("Apr");}
if (month==5){tft.print ("May");}
if (month==6){tft.print ("Jun");}
if (month==7){tft.print ("Jul");}
if (month==8){tft.print ("Aug");}
if (month==9){tft.print ("Sep");}
if (month==10){tft.print ("Oct");}
if (month==11){tft.print ("Nov");}
if (month==12){tft.print ("Dec");}
tft.print("/");
tft.print(year+2000);
if (day==1){tft.print (" Sun.");}
if (day==2){tft.print (" Mon.");}
if (day==3){tft.print (" Tue.");}
if (day==4){tft.print (" Wed.");}
if (day==5){tft.print (" Thu.");}
if (day==6){tft.print (" Fri.");}
if (day==7){tft.print (" Sat.");}
}
}
void loop(){
timeRtc();
timer();
buttonPS();
setUp();
if (millis()-prvMlsTemp>zadTime[6]&&flagDallas!=1){dallas();}
tempDallas();
irrigation();
voltM();
humiDity();
printTime();
printDin();
printStat();
}