Система иммитации климата на Arduino MEGA 2560
- Войдите на сайт для отправки комментариев
Давно мечтал сделать автоматизированную систему управления освещением для оранжереи матери, однако на тот момент небыло ни знаний ни умений в разработке микроконтроллерных систем и уж тем более в программировании таковых. Прошли годы и появилась Ардуина и еще годы, прежде чем я узнал о ней =)
Это мои первые шаги и ниже я приведу свой первый скетч.
Идея такова. С наступлением "весны" (день 1) световой день еще короткий. "Солнце" светит мало, да и температура "днем" и "ночью" еще ниже "расчетной" (на последний день цикла перед плодоношением). По мере приближения к этому "дню Х" световой день увеличивается - "Солнце" встает" раньше и "садится" позже. Так же растет "среднесуточная температура". По достижении "крайнего дня" световой день" не меняется и максимален, температура так же максимальна ("днем" допустим 25С, "ночью" 18С).
Параллельно МЕГА нагружена системой полива-опрыскивания (автомобильный насос, либо омывателей лобового стекла, либо топливный - там давление оГО!), которая работает постоянно, с небольшими перерывами на "перекур))". В систему полива по мере падения уровня в основном баке будет "докачиваться" вода из запасного.
Планирую на МЕГУ навешать:
1) DS1307 для реализации календаря;
2) 8 Channel 5V Relay Shield Module for Arduino для коммутации "Солнца", включения инфракрасных обогревателей по необходимости, питания системы полива и долива, запуск "ветра" время от времени, и вентиляция приточно-вытяжная на случай повышенной влажности или избыточной температуры; (http://arduino.ru/forum/apparatnye-voprosy/podklyuchenie-n-channel-12v-relay-shield-module-arduino второго варианта подключения, только на 5 Вольт)
3) LCD 16x2 с 6-ю кнопочками для визуального контроля и управления параметрами;
4) DHT22 Х 2шт. естесственно для мониторинга того, для чего они созданы)) на двух уроннях верх-низ и вычисления средних показателей;
5) Датчик влажности почвы/уровня жидкости, состоящий из двух пластин на стеклотексталите и компаратора;
6) SD card модуль для энергонезависимого хранения некоторых переменных, и основной - текущего дня цикла;
7) АТХ блок питания для питания МЕГИ от 12 Вольт (читал тут, что если питать только логику, то проблем не должно возникнуть) и переферии (вроде все остальное от 5 Вольт функционирует.
#include <Wire.h> #include <RTClib.h> #include <LiquidCrystal.h> RTC_DS1307 RTC; DateTime now; LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define variables int work_time = 0; // время работы освещения int light_state = LOW; // этой переменной устанавливаем состояние освещения int current_day = 0; // сюда записываем текущий день int current_light_day_hours = 0; // расчетная длина светового дня в часах int current_light_day_minutes = 0; // расчетная днина светового дня в минутах int lcd_key = 0; int adc_key_in = 0; int lastDay = 0; int lastMonth = 0; int lastYear = 0; int lastHour = 0; int lastMinute = 0; int movementTimer = 0; int menuOptions = 3; int menuOption = 0; int end_light_on_hours = 1; //Час включения на последний день int end_light_on_minutes = 0; //Минуты включения на последний день int end_light_off_hours = 22; //Час выключения на последний день int end_light_off_minutes = 0; //Минуты выключения на последний день int correction_time = 0; //Сюда сохраняем расчетное время корректировки включения в минутах int correction_hours_on = 0; //Корректировка часов включения освещения int correction_minutes_on = 0; //Корректировка минут включения освещения int correction_hours_off = 0; //Корректировка часов отключения освещения int correction_minutes_off = 0; //Корректировка минут отключения освещения int current_light_on_hours = 0; //Расчетное время включения освещения в часах int current_light_on_minutes = 0; //Расчетное время включения освещения в минутах int current_light_off_hours = 0; //Расчетное время выключения освещения в часах int current_light_off_minutes = 0; //Расчетное время выключения освещения в минутах bool alarmSet = true; //0; bool backLightOn = 1; bool resetClock = false; // define constants const int backLight = 10; const int led_Pin = 53; // номер выхода, подключенного к освещению #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 #define beeper A1 #define shortBeep 100 #define longBeep 500 #define max_light_day_hours 20 // максимальная длина светового дня в часах - базовая #define day_X 24 // день на который приходится базовая длина дня с базовым времинем включения #define korrection 5 // шаг корректировки базового времени включения и базовой длины светового дня void setup () { Serial.begin(57600); pinMode(backLight, OUTPUT); digitalWrite(backLight, HIGH); // turn backlight off LOW pinMode(beeper, OUTPUT); digitalWrite(beeper, LOW); pinMode(led_Pin, OUTPUT); // PIN освещения Wire.begin(); RTC.begin(); if (! RTC.isrunning()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled //RTC.adjust(DateTime(__DATE__, __TIME__)); } } void loop () { now = RTC.now(); digitalClockDisplay( ); // update clock for (int i = 0; i < 10000; i++) { button_loop(); //check for button pushed } } void printDigits(byte digits) { // utility function for digital clock display: prints preceding colon and leading 0 lcd.print(":"); if(digits < 10) lcd.print('0'); lcd.print(digits,DEC); } void digitalClockDisplay() { lcd.setCursor(0,1); lcd.print(current_light_on_hours); lcd.print(":"); lcd.print(current_light_on_minutes); lcd.print("|"); lcd.print(current_light_off_hours); lcd.print(":"); lcd.print(current_light_off_minutes); lcd.print("|"); lcd.print(correction_time); lcd.print("|"); lcd.print(current_day); if (now.day() != lastDay) { current_day++; //Текущий день + 1 } if (now.day() != lastDay || resetClock == true) { lcd.begin(16,2); lcd.setCursor(6,0); if(now.day() < 10) lcd.print('0'); lcd.print(now.day(), DEC); lcd.print("/"); if(now.month() < 10) lcd.print('0'); lcd.print(now.month(), DEC); lcd.print("/"); int thisYear = now.year(); lcd.print(thisYear, DEC); } if (now.minute() != lastMinute || resetClock == true) { lcd.setCursor(0,0); if(now.hour() < 10) lcd.print('0'); lcd.print(now.hour(), DEC); printDigits(now.minute()); if (alarmSet) lcd.print("*"); } resetClock = false; lastDay = now.day(); lastMonth = now.month(); lastYear = now.year(); lastHour = now.hour(); lastMinute = now.minute(); // мой код начало correction_time = korrection * (day_X - current_day); //Время корректировки включения освещения в минутах зависит от разницы дня по-умолчанию и текущего дня if (correction_time > 0 && correction_time < 60) { correction_hours_on = 0; correction_minutes_on = correction_time; correction_hours_off = 1; correction_minutes_off = 60 - correction_time; } if (correction_time == 60) { correction_hours_on = 1; correction_minutes_on = correction_time - 60; correction_hours_off = 1; correction_minutes_off = 60 - correction_time; } if (correction_time > 60 && correction_time < 120) { correction_hours_on = 1; correction_minutes_on = correction_time - 60; correction_hours_off = 2; correction_minutes_off = 120 - correction_time; } if (correction_time == 120) { correction_hours_on = 2; correction_minutes_on = correction_time - 120; correction_hours_off = 3; correction_minutes_off = 120 - correction_time; } if (correction_time > 120 && correction_time < 180) { correction_hours_on = 2; correction_minutes_on = correction_time - 120; correction_hours_off = 3; correction_minutes_off = 180 - correction_time; } current_light_on_hours = end_light_on_hours + correction_hours_on; //Описывает расчетное время включения освещения в часах current_light_on_minutes = end_light_on_minutes + correction_minutes_on; //Описывает расчетное время включения освещения в минутах current_light_off_hours = end_light_off_hours - correction_hours_off; //Описывает расчетное время выключения освещения в часах current_light_off_minutes = end_light_off_minutes + correction_minutes_off; //Описывает расчетное время выключения освещения в минутах if (current_light_on_hours <= lastHour && current_light_on_minutes <= lastMinute) { light_state = HIGH; if (current_light_off_hours <= lastHour && current_light_off_minutes <= lastMinute) { light_state = LOW; //включаем освещение light_state = LOW; // выключаем освещение } digitalWrite(led_Pin, light_state); } } // мой код конец void button_loop() { int button = read_LCD_buttons(); if (button == btnSELECT) { timedBeep(shortBeep,1); selectMenu(); } } void selectMenu() { int button = 0; menuOption = 1; lcdClear(); lcd.print("Minute Timer"); while (menuOption <= menuOptions) { button = read_LCD_buttons(); if (button == btnSELECT) { timedBeep(shortBeep,1); menuOption++; if (menuOption == 2) { lcdClear(); lcd.print("Set/Clear Alarm"); } if (menuOption == 3) { lcdClear(); lcd.print("Set Date/Time"); } } if (button == btnLEFT) { if (menuOption == 1) { timedBeep(shortBeep,1); minuteTimer(); return; } if (menuOption == 2) { timedBeep(shortBeep,1); if (alarmSet) { clearAlarm(); } else { setAlarm(); } return; } if (menuOption == 3) { timedBeep(shortBeep,1); setDateTime(); return; } } } } void clearAlarm() { int button = 0; bool clearIt = true; lcdClear(); lcd.print("Alarm Set For"); lcd.setCursor(0,1); lcd.print(current_light_on_hours); lcd.print(":"); lcd.print(current_light_on_minutes); lcd.print(" "); delay(2000); lcdClear(); lcd.print("Clear Alarm?"); lcd.setCursor(0,1); lcd.print("Yes"); while (button != btnSELECT) { button = read_LCD_buttons(); if (button == btnUP) { timedBeep(shortBeep,1); clearIt = !clearIt; } if (button == btnDOWN) { timedBeep(shortBeep,1); clearIt = !clearIt; } if (button == btnRIGHT) { timedBeep(shortBeep,1); alarmSet = !clearIt; if (clearIt) { lcdClear(); timedBeep(shortBeep,2); lcd.print("Alarm Cleared!"); delay(2000); } return; } lcd.setCursor(0,1); if (clearIt) { lcd.print("Yes"); } else{ lcd.print("No "); } } } void minuteTimer() { int timerMinutes = getTimerMinutes("Set Minutes", 0, 60); if (timerMinutes > 0) { timedCountDown(timerMinutes*60, "Minute Timer"); } else { timerCancelled("Timer"); } return; } void setAlarm() //Время включения освещения по-умолчанию { int button = 0; end_light_on_hours = getTimerMinutes("Set Alarm Hour", end_light_on_hours, 23); if (end_light_on_hours >= 0 && end_light_on_hours < 24) { end_light_on_minutes = getTimerMinutes("Set Minutes", end_light_on_minutes, 59); if (end_light_on_minutes < 60) { lcdClear(); lcd.setCursor(0,1); lcd.print(end_light_on_hours); lcd.print(":"); if (end_light_on_minutes < 10) lcd.print("0"); lcd.print(end_light_on_minutes); if (button == btnRIGHT) { timedBeep(shortBeep,1); alarmSet = true; lcd.setCursor(0,0); lcd.print("Alarm Set for"); delay(1000); return; } else { timerCancelled("Alarm"); return; } } else { timerCancelled("Alarm"); } } else { timerCancelled("Alarm"); } } void setDateTime() { int button = 0; //get month int setMonth = getTimerMinutes("Set Month", lastMonth, 12); if (setMonth > 0 && setMonth < 13) { int setDay = getTimerMinutes("Set Day", lastDay, 31); if (setDay > 0 && setDay < 32) { //get year int setYear = getTimerMinutes("Set Year", lastYear, 2999); if (setYear > 2000 && setYear < 3000) { //get hour int thisHour = lastHour; int setHour = getTimerMinutes("Set Hour", thisHour, 23); if (setHour >= 0 && setHour < 24) { //get minutes int setMinute = getTimerMinutes("Set Minute", lastMinute, 59); if (setMinute < 60) { lcdClear(); lcd.setCursor(0,1); //display alarm time lcd.print(setHour); lcd.print(":"); if (setMinute < 10) lcd.print("0"); lcd.print(setMinute); if (button == btnRIGHT) { timedBeep(shortBeep,1); RTC.adjust(DateTime(setYear,setMonth,setDay,setHour,setMinute)); //прописываем дату и время в модуль реал-тайм lcd.setCursor(0,0); lcd.print("Saving... "); delay(1000); return; } else { timerCancelled(""); return; } } else { timerCancelled(""); } } else { timerCancelled(""); } } else { timerCancelled(""); } } else { timerCancelled(""); } } else { timerCancelled(""); } } // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void timedCountDown(int secondCount, char countLabel[]) { long seconds = 0; long minutes = 0; lcdClear(); lcd.print(countLabel); for (int i = secondCount; i >= 0; i--) { seconds = i; minutes = i / 60; if (minutes > 0) { seconds = seconds - (minutes * 60); } if (minutes > 0) { lcd.setCursor(0,1); lcd.print(minutes); lcd.print(" min "); } else { lcd.setCursor(0,1); } if (seconds < 10) lcd.print("0"); lcd.print(seconds); lcd.print(" sec remaining"); if (seconds > 0) delay(1000); if (read_LCD_buttons() == btnSELECT) //cancel { timerCancelled("Timer"); i = 0; return; } } lcd.setCursor(6,1); timedBeep(longBeep,3); } int getTimerMinutes(char timerText[], int startNum, int maxCount) { int minutes = startNum; int button = 0; lcdClear(); lcd.print(timerText); lcd.setCursor(0,1); lcd.print(minutes); while (button != btnSELECT) { button = read_LCD_buttons(); Serial.println(button); if (button == btnLEFT) { if ((minutes + 10) <= maxCount) { timedBeep(shortBeep,1); minutes = minutes + 10; } else { timedBeep(shortBeep,2); } } if (button == btnUP) { if (minutes < maxCount) { timedBeep(shortBeep,1); minutes++; } else { timedBeep(shortBeep,2); } } if (button == btnDOWN) { if (minutes > 0) { timedBeep(shortBeep,1); minutes--; } else { timedBeep(shortBeep,2); } } if (button == btnRIGHT) { timedBeep(shortBeep,1); return minutes; } lcd.setCursor(0,1); lcd.print(minutes); lcd.print(" "); } return 0; } void timedBeep(int beepTime, int beepCount) { for (int i = 0; i < beepCount; i ++) { digitalWrite(beeper, HIGH); delay(beepTime); digitalWrite(beeper, LOW); delay(beepTime); } } void lcdClear() { resetClock = true; lcd.clear(); lcd.begin(16,2); lcd.setCursor(0,0); } void timerCancelled(char message[]) { lcdClear(); lcd.print(message); lcd.print(" Cancelled"); timedBeep(shortBeep,3); }
По скетчу. Часть кода отвечающего за работу RTC и LCD с кнопками, установку даты и времени взял там:http://www.chemcool.co.za/p/593221/arduino-lcd-clock-with-alarm-and-timer это не реклама, а ссылка на источник. Остальное писал как видел (я художник - я так вижу =)) Но я начинающий художник и могу ошибаться, так что критика приветствуется.
Пока в скетче описан алгоритм "плавающей переодичности" (громко назвал! =)) и пока не подключен модуль реле, "Солнце" наблюдаю по светодиоду =)
В ближайшее время ожидаю DHT22 и начну городить слежением за температурой и клацаньем релёшками "отопления". К этому времени и освещение переедет со светодиода на релёхи.
Есть и прикол - после подачи питания на МЕГУ все данные сразу отображаются на дисплее, а вот если сейчас время светового дня, то диод загорается не сразу, а иногда через 3-5 минут после старта. Пока курю код.