Система иммитации климата на 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 минут после старта. Пока курю код.