Система иммитации климата на Arduino MEGA 2560

Нет ответов
AndyResh
Offline
Зарегистрирован: 08.04.2013

Давно мечтал сделать автоматизированную систему управления освещением для оранжереи матери, однако на тот момент небыло ни знаний ни умений в разработке микроконтроллерных систем и уж тем более в программировании таковых. Прошли годы и появилась Ардуина и еще годы, прежде чем я узнал о ней =)

Это мои первые шаги и ниже я приведу свой первый скетч.

Идея такова. С наступлением "весны" (день 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 минут после старта. Пока курю код.