//Υπολογισμός θέσης του ήλιου
//Δίνει την θέση του ήλιου στον ουρανό σε μοίρες όσο αφορά το ύψος και την ανατολή-δύση

#include <Timer.h>                    //Timer Library

Timer t;                              //defines t as a timer

#define pi    3.14159265358979323846 //Το π
#define rad   (pi/180)               //Για την μετατροπή ακτίνια=μοίρες*(π/180)


#define DS1307_ADDR_R       0xd1     //rtc i2c address for reading
#define DS1307_ADDR_W       0xd0     //rtc i2c address for writing
#define _sda_pin            A4       //i2c data line arduino pin
#define _scl_pin            A5       //i2c clock line arduino pin
#define REG_SEC		    0x00     //rtc second register address
#define REG_MIN		    0x01     //rtc minute register address
#define REG_HOUR	    0x02     //rtc hour register address
#define REG_DOW		    0x03     //rtc day of week register address
#define REG_DATE	    0x04     //rtc date register address
#define REG_MON		    0x05     //rtc month register address
#define REG_YEAR	    0x06     //rtc year register address
#define ELEV_UP_POT_LIMIT   0x08     //rtc elevetion potensiometer upper limit ram register
#define ELEV_DOWN_POT_LIMIT 0x09     //rtc elevetion potensiometer down limit ram register
#define AZIM_EAST_POT_LIMIT 0x0a     //rtc elevetion potensiometer east limit ram register
#define AZIM_WEST_POT_LIMIT 0x0b     //rtc elevetion potensiometer west limit ram register
#define REG_DST             0x0c     //rtc DST winter-summer ram register (0=Winter, 1=Summer)

//-------------------------------------------------------------//

#define elev_UP 2         //output pin for relay elevation UP
#define elev_DOWN 3       //output pin for relay elevation DOWN

#define azim_EAST 4       //output pin for relay azimuth EAST
#define azim_WEST 5       //output pin for relay azimuth WEST 

//--------------------------------------------------------//

#define but_UP 6          //input pin for button elevation UP
#define but_DOWN 7        //input pin for button elevation DOWN

#define but_EAST 8        //input pin for button azimuth EAST
#define but_WEST 9        //input pin for button azimuth WEST

//---------------------------------------------------------//

#define limit_UP 10       //input pin for limit switch elevation UP
#define limit_DOWN 11     //input pin for limit switch elevation DOWN

#define limit_EAST 12     //input pin for limit switch azimuth EAST
#define limit_WEST 13     //input pin for limit switch azimuth WEST

#define limits_LED A0     //Led for indicating that one or more limit switches in activated
#define auto_man A1       //input pin for auto/manual switch

#define elev_pot A2       //Analog input pin for elevation pontensiometer
#define azim_pot A3       //Analog input pin for azimuth pontensiometer

//----------The mechanical limits in degrees of tracker-------------------//
//-----------they depend from the tracker construction--------------------//
#define UP_LIMIT_DEG   90    //elevation up limit of tracker
#define DOWN_LIMIT_DEG 30    //elevation down limit of tracker
#define EAST_LIMIT_DEG 120   //azimuth east limit of tracker
#define WEST_LIMIT_DEG 240   //azimuth west limit of tracker
#define SOUTH_DEG      180   //degrees of south
//----------------------------------------------------------------//

//Εισαγωγή Μεταβλητών
//Η ημερομηνία και η ώρα την διαβάζει απο το RTC (Real Time Clock) μέσω I2C
//υπολογίζεται με αλγοριθμο αν είναι καλοκαιρινή ώρα DST(Daylight Saving Time)

  int date;         //Ημέρα
  int month;        //Μήνας  
  int year;         //Χρονιά

  float hour;       //Ώρα
  float minute;     //Λεπτά
  byte sec;
 
 //-------------το timezone και οι συντεταγμένες πρέπει να οριστούν εδώ ανάλογα με την τοποθεσία εγκατάστασης----------// 
  int timezone= 2;   //Η ζώνη ώρας της Ελλάδος (ουσιαστικά δεν μας ενδιαφέρει το πρόσημο αλλά η διαφορά από το μηδέν)
 
  float latitude=  39.549159;    //Γεωγραφικό Πλάτος σε μοίρες
  float longitude= 20.325640;    //Γεωγραφικό Μήκος σε μοίρες
//-------------------------------------------------------------------//
  
  
  
//Μεταβλητές Προγράμματος
  
  int doy;                //Day of year, θα υπολογίζεται απο αλγόριθμο
  int dow;                //Day of Week, υπολογιζεται απο αλγόριθμο.
  int dst;                //Καλοκαιρινή ώρα 1=Ναι 0=Όχι, θα υπολογίζεται απο αλγόριθμο

  float easn;             //Elevation Angle at Solar Noon (Οι μοίρες του ύψους του ήλιου κατά το ηλιακό μεσημέρι)
  
  float elevation;        //Οι τρέχουσες μοίρες ύψους του ήλιου
  int elevation_int;      //Το elevation σε ακεραια μορφή
  float zenith;           //η συμπληρωματική γωνία ως προς τις 90 μοίρες, η γωνία δηλαδή που πρέπει να έχουν τα φωτοβολταικά πάνελς 
                          //ως προς το έδαφος την δεδομένη στιγμή
 
  float azimuth;          //οι μοίρες στις οποίες βρίσκεται ο ήλιος σε σχέση με την Ανατολή-Δύση, Νότος είναι 180
  int azimuth_int;        //Το azimuth σε ακέραια μορφή
  
    
  int motion_flag=0;      //για την αναγνώριση της κίνησης 8=UP, 4=DOWN, 2=EAST, 1=WEST, 0=STOP
    
  int auto_man_flag=0;    //για την ενδειξη της κατάστασης (mode) 0=Manual, 1=Auto, 2=Calibrating
  
  int elev_limits_flag=0; //για την ένδειξη των ορίων (ακραιοι δικόπτες) του elevation 2=UP, 1=DOWN, 0=NoLimit
 
  int azim_limits_flag=0; //για την ένδειξη των ορίων (ακραιοι δικόπτες) του azimuth 2=EAST, 1=WEST, 0=NoLimit
  
  int elev_pot_up_limit;    //η αναλογική τιμή του ποντενσιόμετρου για το πάνω όριο του elevation
  int elev_pot_down_limit;  //η αναλογική τιμή του ποντενσιόμετρου για το κάτω όριο του elevation
  int azim_pot_east_limit;  //η αναλογική τιμή του ποντενσιόμετρου για το east όριο του azimuth
  int azim_pot_west_limit;  //η αναλογική τιμή του ποντενσιόμετρου για το west όριο του azimuth
 
  int num_pot_readings=10;  //αριθμός τιμών που διαβάζει (analogread) απο τα ποντενσιόμετρα για να βγάλει το μεσο όρο (smoothing)
   
  int auto_move_elev_flag;//=0;//για τον έλεγχο αν πρέπει να κινηθεί αυτόματα σε νεα θέση κάθετα (elevation) 0=πρέπει να κινηθεί, 1=οχι
  int auto_move_azim_flag;//=0;//για τον έλεγχο αν πρέπει να κινηθεί αυτόματα σε νεα θέση οριζόντια (azimuth) 0=πρέπει να κινηθεί, 1=οχι
  
  int auto_move_elev_deg;  //μοίρες στις οποίες πρέπει να κινηθεί αυτόματα κάθετα (elevation)
  int auto_move_azim_deg;  //μοίρες στις οποίες πρέπει να κινηθεί αυτόματα οριζόντια (azimuth)
  
  int calibration_flag;    //ένδειξη για calibration mode 1=calibration, 0=no
  int calibration_state;   //φάση στην οποία βρίσκετε το calibration 8=up calibration, 4=down calibration, 2=east calibration, 1=west calibration
  
  int mins_to_next_move_count;  //αντιστροφη μέτρηση των λεπτών που απομένουν μέχρι την επόμενη κίνηση
  int mins_to_move_ahead=16;    //μεσοδιάστημα λεπτών που θα υπολογίζονται οι συντεταγμένες και θα γίνετε η κίνηση
  int just_moved_flag;          //ένδειξη ότι μόλις κινήθηκε 1=μόλις κινήθηκε, 0=οχι
  
  //-----------------------------------------------------------------------------------------------------------------------------------------------------//
  
 //--------------------- RTC DS1307 Routines-----------------//
  void sendStart(byte addr)
{
	pinMode(_sda_pin, OUTPUT);
        digitalWrite(_scl_pin, HIGH);
	digitalWrite(_sda_pin, HIGH);
	digitalWrite(_sda_pin, LOW);
	digitalWrite(_scl_pin, LOW);
	shiftOut(_sda_pin, _scl_pin, MSBFIRST, addr);
}

//--------------------------------------------------------//

void sendStop()
{
	pinMode(_sda_pin, OUTPUT);
	digitalWrite(_sda_pin, LOW);
	digitalWrite(_scl_pin, HIGH);
	digitalWrite(_sda_pin, HIGH);
	pinMode(_sda_pin, INPUT);
}

//--------------------------------------------------------//

void sendNack()
{
	pinMode(_sda_pin, OUTPUT);
	digitalWrite(_scl_pin, LOW);
	digitalWrite(_sda_pin, HIGH);
	digitalWrite(_scl_pin, HIGH);
	digitalWrite(_scl_pin, LOW);
	pinMode(_sda_pin, INPUT);
}

//--------------------------------------------------------//

void waitForAck()
{
	pinMode(_sda_pin, INPUT);
	digitalWrite(_scl_pin, HIGH);
	if (digitalRead(_sda_pin)==LOW) 
           digitalWrite(_scl_pin, LOW);
}

//--------------------------------------------------------//

byte readByte()
{
	pinMode(_sda_pin, INPUT);

	byte value = 0;
	byte currentBit = 0;

	for (int i = 0; i < 8; ++i)
	{
		digitalWrite(_scl_pin, HIGH);
		currentBit = digitalRead(_sda_pin);
		value |= (currentBit << 7-i);
		delayMicroseconds(1);
		digitalWrite(_scl_pin, LOW);
	}
	return value;
}

//--------------------------------------------------------//

void writeByte(byte value)
{
	pinMode(_sda_pin, OUTPUT);
	shiftOut(_sda_pin, _scl_pin, MSBFIRST, value);
}

//--------------------------------------------------------//

byte readRegister(byte reg)
{
	byte	readValue=0;

	sendStart(DS1307_ADDR_W);
	waitForAck();
	writeByte(reg);
	waitForAck();
	sendStop();
	sendStart(DS1307_ADDR_R);
	waitForAck();
	readValue = readByte();
	sendNack();
	sendStop();
	return readValue;
}

//--------------------------------------------------------//

void writeRegister(byte reg, byte value)
{
	sendStart(DS1307_ADDR_W);
	waitForAck();
	writeByte(reg);
	waitForAck();
	writeByte(value);
	waitForAck();
	sendStop();
}

//--------------------------------------------------------//

byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}

//--------------------------------------------------------//

void setTime(byte hour, byte min, byte sec)
{
	if (((hour>=0) && (hour<24)) && ((min>=0) && (min<60)) && ((sec>=0) && (sec<60)))
	{
		writeRegister(REG_HOUR, decToBcd(hour));
		writeRegister(REG_MIN, decToBcd(min));
		writeRegister(REG_SEC, decToBcd(sec));
	}
}

//--------------------------------------------------------//

void setDate(byte date, byte mon, uint16_t year)
{
	if (((date>0) && (date<=31)) && ((mon>0) && (mon<=12)) && ((year>=2000) && (year<3000)))
	{
		year -= 2000;
		writeRegister(REG_YEAR, decToBcd(year));
		writeRegister(REG_MON, decToBcd(mon));
		writeRegister(REG_DATE, decToBcd(date));
	}
}

//--------------------------------------------------------//

void setDOW(byte dow)
{
	if ((dow>0) && (dow<8))
		writeRegister(REG_DOW, dow);
}

//--------------------------------------------------------//

byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}

//--------------------------------------------------------//

void halt(bool enable)
{
  byte reg = readRegister(REG_SEC);
  reg &= ~(1 << 7);
  reg |= (enable << 7);
  writeRegister(REG_SEC, reg);
}

//--------------------------------------------------------//

void getRTC(){
  
  sec=bcdToDec(readRegister(REG_SEC)&0x7f);
  minute=bcdToDec(readRegister(REG_MIN));
  hour=bcdToDec(readRegister(REG_HOUR)&0x3f);
  
  dow=bcdToDec(readRegister(REG_DOW));
  
  date=bcdToDec(readRegister(REG_DATE));
  month=bcdToDec(readRegister(REG_MON));
  year=2000+bcdToDec(readRegister(REG_YEAR));
}
//---------------------------------------------------------------------------------------------------------------------------------------------------//
//------------------ Motion Routines ------------------------------------------//
 
 // Elevetion UP
 void move_elev_UP(){
 
 digitalWrite(elev_UP,HIGH);
 digitalWrite(elev_DOWN,LOW);
 digitalWrite(azim_EAST,LOW);
 digitalWrite(azim_WEST,LOW); 
 motion_flag=8;
}

//--------------------------------------------------------//

void automove_elev(int deg){
  
  if (read_elev_pot()==deg) {auto_move_elev_flag=1; move_all_stop();}
  else if ((read_elev_pot()<deg) && (check_elev_limits()!=2)) move_elev_UP(); 
  else if ((read_elev_pot()>deg) && (check_elev_limits()!=1)) move_elev_DOWN();  
  else  move_all_stop();
  check_elev_limits();  
}

//--------------------------------------------------------//
//Elevation DOWN
void move_elev_DOWN(){
  
 digitalWrite(elev_UP,LOW);
 digitalWrite(elev_DOWN,HIGH);
 digitalWrite(azim_EAST,LOW);
 digitalWrite(azim_WEST,LOW);
 motion_flag=4;
}

//--------------------------------------------------------//
//Azimuth EAST
void move_azim_EAST(){

 digitalWrite(elev_UP,LOW);
 digitalWrite(elev_DOWN,LOW);
 digitalWrite(azim_EAST,HIGH);
 digitalWrite(azim_WEST,LOW);   
 motion_flag=2;
}

//--------------------------------------------------------//

void automove_azim(int deg){ 
  
  if (read_azim_pot()==deg) {auto_move_azim_flag=1; move_all_stop();}
  else if ((read_azim_pot()<deg) && (check_azim_limits()!=1)) move_azim_WEST(); 
  else if ((read_azim_pot()>deg) && (check_azim_limits()!=2)) move_azim_EAST();  
  else  move_all_stop();
  check_azim_limits();  
}

//--------------------------------------------------------//
//Azimuth WEST
void move_azim_WEST(){
 
 digitalWrite(elev_UP,LOW);
 digitalWrite(elev_DOWN,LOW);
 digitalWrite(azim_EAST,LOW);
 digitalWrite(azim_WEST,HIGH);
 motion_flag=1;
}

//--------------------------------------------------------//
//BOTH motor STOP
void move_all_stop(){
 
 digitalWrite(elev_UP,LOW);
 digitalWrite(elev_DOWN,LOW);
 digitalWrite(azim_EAST,LOW);
 digitalWrite(azim_WEST,LOW);
 motion_flag=0;
 
}   

//--------------------------------------------------------//
//Check Elevation Limits
int check_elev_limits(){
  
  int limits_reading;

  limits_reading=0;
  limits_reading=limits_reading + digitalRead(limit_UP);    //2 limit up
  limits_reading=limits_reading << 1;
  limits_reading=limits_reading + digitalRead(limit_DOWN);  //1 limit down
  
  elev_limits_flag=limits_reading;  
  return limits_reading;
}

//--------------------------------------------------------//
//Check Azimuth Limits
int check_azim_limits(){
  
  int limits_reading;

  limits_reading=0;
  limits_reading=limits_reading + digitalRead(limit_EAST);  //2 limit EAST
  limits_reading=limits_reading << 1;
  limits_reading=limits_reading + digitalRead(limit_WEST);  //1 limit WEST
  
  azim_limits_flag=limits_reading;
  return limits_reading;
}

//--------------------------------------------------------//
//Check Buttons
int check_buttons(){

  int buttons_reading;
  
  int old_button_state=0;
  int button_state=0;
  long last_debounce_time =0;
  long debounce_delay=20;

  buttons_reading=digitalRead(but_UP);                      //8 UP
  buttons_reading=buttons_reading << 1;
  buttons_reading=buttons_reading + digitalRead(but_DOWN);  //4 DOWN
  buttons_reading=buttons_reading << 1;
  buttons_reading=buttons_reading + digitalRead(but_EAST);  //2 EAST
  buttons_reading=buttons_reading << 1;
  buttons_reading=buttons_reading + digitalRead(but_WEST);  //1 WEST
  
  if (buttons_reading!=old_button_state){
    last_debounce_time=millis();  
  }
  
  if((millis()-last_debounce_time)>debounce_delay){  
   button_state=buttons_reading;
  }
  
  old_button_state=buttons_reading;

  return buttons_reading;
}


//---------------------------------------------------------------------//

//Αλγόριθμος υπολογισμού του αριθμού της ημέρας του χρόνου από 1 εώς 365 ή 366 για δύσεκτο έτος
void dayofyear() {
  int dom[11]={31,28,31,30,31,30,31,31,30,31,30};  //Πίνακας από  11 τιμές με τις μέρες των μηνών (Φεβρουάριος 28). 
                                                   //Δεν χρειαζόμαστε τον Δεκέμβριο.
 
  boolean leapyear;                                //Για τον υπολογισμό Δύσεκτου Έτους
  int x;
  
  
  doy=0;

  if (month>1) {    
    for (x=1 ; x<month ; x=x+1) {    
      doy=doy+dom[x-1];    
    }
  }

  doy=doy+date;
  
  if (year%400==0) leapyear=true;          //
  else if (year%100==0) leapyear=false;    //Υπολογισμός δύσεκτου έτους
  else if (year%4==0) leapyear=true;       //
  else leapyear=false;                     //
  
  if ((leapyear) && (month>2))  doy = doy+1;

}

//--------------------------------------------------------//
//Αλγοριθμος για τον υπολογισμό της μέρας της βδομαδας ώστε να χρησιμοποιηθεί στον υπολογισμό του DST
//1=Δευτέρα, 2=Τρίτη, 3=Τετάρτη, 4=Πέμπτη, 5=Παρασκευή, 6=Σάββατο, 7=Κυριακή
/*void dayofweek(){
  
  byte d;
  /*int a,y,m,d;
  
  a=(14-month)/12;
  y=year-a;
  m=month+(12*a)-2;
  d=(date+y+y/4-y/100+y/400+(31*m)/12)%7;*/
  
  
 /* d=dow-1;
  if (d==0) dow=7; else dow=d;
  
}*/

//--------------------------------------------------------//
//Αλγόριθμος για τον υπολογισμό Θερινής-Χειμερινής ώρας μιας ημερονηνίας, DST
//Γινεται θερινή την τελευταία Κυριακή του Μαρτίου
//Γίνεται χειμερινή την τελευταία Κυριακή του Οκτωβρίου

void dstcalc(){
  //dst=0 -->Winter
  //dst=1 -->Summer
 
 if (month>3 && month<10) dst=1;
 if (month<3 || month>10) dst=0;
 
 if ((month==3) && (date>=24)) {
           if ((dow==7) && (int(hour)>1)) dst=1;}
 //else if ((month==3) && (date<24)) dst=0; 
 
 if ((month==10) && (date>=24)) {
           if ((dow==7) && (int(hour)>1)) dst=0;} 
 //else if ((month==10) && (date<24)) dst=1;
 
           
 time_change();
}

//--------------Αλλαγή της Ώρας---------------------------//

void time_change(){
  int dst_in_ram;
  

 dst_in_ram=readRegister(REG_DST);

 if ((dst==1) && (dst_in_ram==0)){//αρχιζει η καλοκαιρινή ώρα
   hour=hour+1.0;
   setTime((int)hour,(int)minute,(int)sec);
   writeRegister(REG_DST,dst);
 }
 
 if ((dst==0) && (dst_in_ram==1)){//αρχιζει η χειμερινή ώρα
   hour=hour-1.0;
   setTime((int)hour,(int)minute,(int)sec);
   writeRegister(REG_DST,dst);
 }
 
}

//--------------------------------------------------------//

//Υπολογισμοί για την θέση του ήλιου
void sunposition(int hour_to_calculate,int min_to_calculate){
  
 float b;                 //μια μεταβλητή χρήσιμη
 float bradians;
 float eot;
 int lstm;
 float tcf;
 float lst;
 float sha;
 float declination;
 float decimaltime;      //Μετατροπή της ώρας σε δεκαδική μορφή, είναι ίσο με hour+(minutes/60)
 int dst_in_ram;
  

 
 
 
 decimaltime=(float)hour_to_calculate+((float)min_to_calculate/60);        //Μετατροπή της ώρας σε δεκαδική μορφή

 b=(360.00*(doy-81.00))/365.00;       //Μεταβλητή για τον υπολογισμό άλλων παραμέτρων.
 bradians=b*rad;                      //το b σε ακτίνια
 
 //Equation of Time.
 //The equation of time (EoT) (σε λεπτά) είναι μια εμπειρική συνάρτηση 
 //η οποία διορθώνει την ελλειπτική τροχιά της γής και την κλίση της γής ως προς τον άξονά της.
 
 eot=(9.87*sin(2*bradians))-(7.53*cos(bradians))-(1.5*sin(bradians));
 
 
 //Local Standard Time Meridian (LSTM)
 //είναι ένας μεσημβρινός αναφοράς για την συγκεκριμένη ώρα ζώνης 
 //παρόμοιος με κύριο μεσημβρινό ο οποίος χρησιμοποιείται για Greenwich Mean Time. 
 //μας ενδιαφέρει η διαφορά της τοπικής ζωνης ώρας από το 0 και όχι το πρόσημο.
 //εδώ πρέπει να προστεθεί και η 1 ώρα διαφορά της καλοκαιρινής ώρας.
 //15=360/24; 

 dst_in_ram=readRegister(REG_DST);
 lstm=15*(timezone+dst_in_ram);
 
 //Time Correction Factor (TC)
 //The net Time Correction Factor (in minutes) accounts for the variation 
 //of the Local Solar Time (LST) within a given time zone due to the longitude 
 //variations within the time zone and also incorporates the EoT above.
 //The factor of 4 minutes comes from the fact that the Earth rotates 1° every 4 minutes. 
 
 tcf=(4.00*(longitude-float(lstm)))+eot;
 
 
 //Local Solar Time (LST)
 //The Local Solar Time (LST) can be found by using the previous two corrections 
 //to adjust the local time (LT).
  
 lst=decimaltime+(tcf/60);
 
 
 
 //Hour Angle (HRA)
 //The Hour Angle converts the local solar time (LST) into the number of degrees 
 //which the sun moves across the sky. By definition, the Hour Angle is 0° at solar noon. 
 //Since the Earth rotates 15° per hour, each hour away from solar noon corresponds 
 //to an angular motion of the sun in the sky of 15°. 
 //In the morning the hour angle is negative, in the afternoon the hour angle is positive.
 
 sha=15.00*(lst-12.00);
 
 
 
 //The declination angle, 
 //denoted by δ, varies seasonally due to the tilt of the Earth on its axis of rotation 
 //and the rotation of the Earth around the sun. If the Earth were not tilted on its axis 
 //of rotation, the declination would always be 0°. However, the Earth is tilted by 23.45° 
 //and the declination angle varies plus or minus this amount. Only at the spring and fall 
 //equinoxes is the declination angle equal to 0°.The declination of the sun is the angle 
 //between the equator and a line drawn from the centre of the Earth to the centre of the sun.
 //Despite the fact that the Earth revolves around the sun, it is simpler to think of the sun 
 //revolving around a stationary Earth. This requires a coordinate transformation. 
 //Under this alternative coordinate system, the sun moves around the Earth. 
 //The declination is zero at the equinoxes (March 22 and September 22), positive during 
 //the northern hemisphere summer and negative during the northern hemisphere winter. 
 //The declination reaches a maximum of 23.45° on June 22 (summer solstice in the northern 
 //hemisphere) and a minimum of -23.45° on December 22 (winter solstice in the northern hemisphere). 
 
 declination=23.45*sin(bradians);
 
 
 //elevation angle at solar noon 
 
 easn=90.00-latitude+declination;
 
 //Elevation & Zenith angle
 //The elevation angle (used interchangeably with altitude angle) is the angular height of 
 //the sun in the sky measured from the horizontal. Confusingly, both altitude and elevation 
 //are also used to describe the height in meters above sea level. 
 //The elevation is 0° at sunrise and 90° when the sun is directly overhead 
 //(which occurs for example at the equator on the spring and fall equinoxes). 
 //The zenith angle is similar to the elevation angle but it is measured from the vertical 
 //rather than from the horizontal, thus making the zenith angle = 90° - elevation.
 
 float decrad, latrad, sharad, elev, azi;
 decrad=declination*rad;
 latrad=latitude*rad;
 sharad=sha*rad;
 
 elev=asin((sin(decrad)*sin(latrad))+(cos(decrad)*cos(latrad)*cos(sharad)));
 elevation=elev/rad;
 
 //zenith=90.00-elevation;
 
 
 //Azimuth Angle
 //The azimuth angle is the compass direction from which the sunlight is coming. 
 //At solar noon, the sun is always directly south in the northern hemisphere 
 //and directly north in the southern hemisphere. The azimuth angle varies throughout the day 
 //as shown in the animation below. At the equinoxes, the sun rises directly east and 
 //sets directly west regardless of the latitude, thus making the azimuth angles 90° at sunrise 
 //and 270° at sunset. In general however, the azimuth angle varies with the latitude and time 
 //of year and the full equations to calculate the sun's position throughout the day are 
 //given on the following page. 
 //The azimuth angle is like a compass direction with North = 0° ,South = 180°, West = 270° and East = 90°
 //The above equation only gives the correct azimuth in the solar morning so that: 
 //Azimuth = Azi, for lst <12 or sha < 0 
 //Azimuth = 360° - Azi, for lst > 12 or sha >0 

 
 
 azi=acos(((sin(decrad)*cos(latrad))-(cos(decrad)*sin(latrad)*cos(sharad)))/cos(elev));
 
 if (sha>0) azimuth=360.00-(azi/rad); else azimuth=azi/rad;
 
           
 
}


//------------------------------------------------------------//

int read_elev_pot(){
  int elev_pot_val;
  int elev_pot_reading;
  
  elev_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    if (motion_flag!=0) elev_pot_reading=(analogRead(elev_pot)/3); else elev_pot_reading=analogRead(elev_pot)/3;
    elev_pot_val=elev_pot_val+elev_pot_reading;
}
  
  elev_pot_val=elev_pot_val/num_pot_readings;
  
    
  elev_pot_val=constrain(elev_pot_val,elev_pot_down_limit,elev_pot_up_limit);
  elev_pot_val=map(elev_pot_val,elev_pot_down_limit,elev_pot_up_limit,DOWN_LIMIT_DEG,UP_LIMIT_DEG);
  
  return elev_pot_val;
}
//------------------------------------//
int read_azim_pot(){
  int azim_pot_val;
  int azim_pot_reading;
  
  azim_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    if (motion_flag!=0) azim_pot_reading=(analogRead(azim_pot)/2); else azim_pot_reading=analogRead(azim_pot)/2;
    azim_pot_val=azim_pot_val+azim_pot_reading;
}
  
  azim_pot_val=azim_pot_val/num_pot_readings;
  
   
  azim_pot_val=constrain(azim_pot_val,azim_pot_east_limit,azim_pot_west_limit);
  azim_pot_val=map(azim_pot_val,azim_pot_east_limit,azim_pot_west_limit,EAST_LIMIT_DEG,WEST_LIMIT_DEG);
 
  return azim_pot_val;
}
//-----------------------------------------//
int check_auto_man(){
  int auto_man_state;
  
  auto_man_state=digitalRead(auto_man);
  
  auto_man_flag=auto_man_state;
  return auto_man_state;
  
}
//--------------------------------------//
void printdata(){
 int dst_in_ram;
  

 
 Serial.flush();
 
 Serial.println("ACK");
  
   
   delay(10);
   
   switch(dow){
    case 1:
    Serial.print("Mon");
    break;
    case 2:
    Serial.print("Tue");
    break;
    case 3:
    Serial.print("Wed");
    break;
    case 4:
    Serial.print("Thu");
    break;
    case 5:
    Serial.print("Fri");
    break;
    case 6:
    Serial.print("Sat");
    break;
    case 7:
    Serial.print("Sun");
    break;
   }
   Serial.print("   ");
   
   if (date<10){
    Serial.print("0");
   }
   Serial.print(date);
   Serial.print("/");
   if (month<10){
    Serial.print("0");
   }
   Serial.print(month);
   Serial.print("/");
   Serial.print(year);
   Serial.print("    ");
   if (hour<10){
    Serial.print("0");
   }
   Serial.print(hour,0);
   Serial.print(":");
   if (minute<10){
    Serial.print("0");
   }
   Serial.print(minute,0);
   Serial.print(":");
   if (sec<10){
    Serial.print("0");
   }
   Serial.print(sec);
   Serial.print("    Time Zone = ");
   Serial.print(timezone,DEC);
   Serial.print("      DST = ");
   dst_in_ram=readRegister(REG_DST);
   switch(dst_in_ram){
     case 1:Serial.print("Summer");
     break;
     case 0:Serial.print("Winter");
     break;
   }
   Serial.print("      Coordinates = ");
   Serial.print(latitude,6);
   Serial.print(" , ");
   Serial.println(longitude,6);
   
   delay(10);
   
   
  Serial.println(int(elevation));
  delay(10);
 
  //Serial.println(zenith,0);
  //delay(10);
  
  Serial.println(int(azimuth));
  
  delay(10);
  Serial.print(auto_move_elev_deg); 
  Serial.print("-->");
  Serial.println(read_elev_pot());
  delay(10);
  Serial.print(auto_move_azim_deg);
  Serial.print("-->");
  Serial.println(read_azim_pot());
    
  delay(10);
  Serial.println(elev_limits_flag);  
  delay(10);
  Serial.println(azim_limits_flag);
  
  delay(10);
  Serial.println(auto_man_flag);
  
  delay(10);
  Serial.println(motion_flag);
  
  delay(10);
  Serial.println(mins_to_next_move_count);
  
} 
//-----------------------------------------------------//
void current_sun_calculate(){
 
    getRTC();
    dayofyear();
    //dayofweek();
    dstcalc();
    
    sunposition(hour,minute);
    
    if (just_moved_flag==1) just_moved_flag=0;
    else mins_to_next_move_count=mins_to_next_move_count-1;
}

//-----------------------------------------------------//
void sun_calculate(){
  int future_min;
  int future_hour;
  
    getRTC();
    dayofyear();
    //dayofweek();
    dstcalc();
    future_min=minute+mins_to_move_ahead/2;
    future_hour=hour;
    if (future_min>59) {
      future_min=future_min-60;
      future_hour=future_hour+1;
      if (future_hour>23) future_hour=future_hour-24;
    }
    sunposition(future_hour,future_min);
    
    elevation_int=int(elevation);
    azimuth_int=int(azimuth);
    
    //elevation_int=-1;
    //elevation_int=40;
    //azimuth_int=170;
    
    if (elevation_int<=0) {auto_move_elev_deg=UP_LIMIT_DEG;auto_move_azim_deg=SOUTH_DEG;}
    if (elevation_int>0) {
            if (elevation_int<DOWN_LIMIT_DEG) auto_move_elev_deg=DOWN_LIMIT_DEG; else auto_move_elev_deg=elevation_int;
            if (azimuth_int<EAST_LIMIT_DEG) auto_move_azim_deg=EAST_LIMIT_DEG; else if (azimuth_int>WEST_LIMIT_DEG) auto_move_azim_deg=WEST_LIMIT_DEG; else auto_move_azim_deg=azimuth_int;
         }
    
    auto_move_elev_flag=0;
    auto_move_azim_flag=0;
    
    if ((mins_to_next_move_count<mins_to_move_ahead) && (just_moved_flag==0) ){
     mins_to_next_move_count=mins_to_move_ahead;
     just_moved_flag=1;
    }  
}
//-----------------------------------------------------//
void calibrate_pot_UP(){
  int cal_elev_pot_val;
  int cal_elev_pot_reading;
  
  
  delay(2000);
  cal_elev_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    /*if (motion_flag!=0) elev_pot_reading=(analogRead(elev_pot)/2)+1; else */cal_elev_pot_reading=analogRead(elev_pot)/3;
    cal_elev_pot_val=cal_elev_pot_val+cal_elev_pot_reading;
  }
  cal_elev_pot_val=cal_elev_pot_val/num_pot_readings;
  writeRegister(ELEV_UP_POT_LIMIT,cal_elev_pot_val);
  delay(10);
  elev_pot_up_limit=readRegister(ELEV_UP_POT_LIMIT);
  read_elev_pot();
  
  calibration_state=4;
  
}
//-----------------------------------------------------//
void calibrate_pot_DOWN(){
  int cal_elev_pot_val;
  int cal_elev_pot_reading;
  
  
  delay(2000);
  cal_elev_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    /*if (motion_flag!=0) elev_pot_reading=(analogRead(elev_pot)/2)+1; else */cal_elev_pot_reading=analogRead(elev_pot)/3;
    cal_elev_pot_val=cal_elev_pot_val+cal_elev_pot_reading;
  }
  cal_elev_pot_val=cal_elev_pot_val/num_pot_readings;
  writeRegister(ELEV_DOWN_POT_LIMIT,cal_elev_pot_val);
  delay(10);
  elev_pot_down_limit=readRegister(ELEV_DOWN_POT_LIMIT);
  read_elev_pot();
  
  calibration_state=2;
}
//-----------------------------------------------------//
void calibrate_pot_EAST(){
  int cal_azim_pot_val;
  int cal_azim_pot_reading;
  
  delay(2000);
  cal_azim_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    /*if (motion_flag!=0) azim_pot_reading=(analogRead(azim_pot)/2)+1; else */cal_azim_pot_reading=analogRead(azim_pot)/2;
    cal_azim_pot_val=cal_azim_pot_val+cal_azim_pot_reading;
  }
  cal_azim_pot_val=cal_azim_pot_val/num_pot_readings;
  writeRegister(AZIM_EAST_POT_LIMIT,cal_azim_pot_val);
  delay(10);
  azim_pot_east_limit=readRegister(AZIM_EAST_POT_LIMIT);
  read_azim_pot();
  
  calibration_state=1;
  
  
}

//-----------------------------------------------------//
void calibrate_pot_WEST(){
  int cal_azim_pot_val;
  int cal_azim_pot_reading;
  //int azim_pot_EAST;
  //int azim_pot_WEST;
 
  delay(2000);
  cal_azim_pot_val=0;
  for(int i=0;i<num_pot_readings;i++){
    /*if (motion_flag!=0) azim_pot_reading=(analogRead(azim_pot)/2)+1; else */cal_azim_pot_reading=analogRead(azim_pot)/2;
    cal_azim_pot_val=cal_azim_pot_val+cal_azim_pot_reading;
  }
  cal_azim_pot_val=cal_azim_pot_val/num_pot_readings;
  writeRegister(AZIM_WEST_POT_LIMIT,cal_azim_pot_val);
  delay(10);
  azim_pot_west_limit=readRegister(AZIM_WEST_POT_LIMIT);
  read_azim_pot();
  
  //azim_pot_EAST=readRegister(AZIM_EAST_POT_LIMIT);
  //azim_pot_WEST=readRegister(AZIM_WEST_POT_LIMIT);
  //writeRegister(AZIM_SOUTH,azim_pot_EAST+((azim_pot_WEST-azim_pot_EAST)/2)-2);
  
  
  
  calibration_state=0;
  

}
 
//-----------------------------------------------------//

void setup() {
 Serial.begin(9600);
 Serial.flush();

// Set the clock to run-mode
  
  pinMode(_scl_pin, OUTPUT);
  digitalWrite(_scl_pin, LOW);
  pinMode(_sda_pin, OUTPUT);
  digitalWrite(_sda_pin, LOW);
  halt(false);
  
  //setTime(16,41,1);
  //setDate(5,10,2012);
  //setDOW(5);
  //writeRegister(REG_DST,1);
  
  dst=readRegister(REG_DST);
  
 pinMode(elev_UP, OUTPUT);
 pinMode(elev_DOWN, OUTPUT);
 pinMode(azim_EAST, OUTPUT);
 pinMode(azim_WEST, OUTPUT);

 pinMode(but_UP, INPUT);
 pinMode(but_DOWN, INPUT);
 pinMode(but_EAST, INPUT);
 pinMode(but_WEST, INPUT);

 pinMode(limit_UP, INPUT);
 pinMode(limit_DOWN, INPUT);
 pinMode(limit_EAST, INPUT);
 pinMode(limit_WEST, INPUT);

 digitalWrite(elev_UP,LOW);
 digitalWrite(elev_DOWN,LOW);
 digitalWrite(azim_EAST,LOW);
 digitalWrite(azim_WEST,LOW);
 
 pinMode(limits_LED,OUTPUT);
 pinMode(auto_man,INPUT);
 
 pinMode(elev_pot,INPUT);
 pinMode(azim_pot,INPUT);
 
 t.every(2000,printdata); //2 sec
 t.every(60000,current_sun_calculate); //1 min
 t.every((long)mins_to_move_ahead*60*1000,sun_calculate); // 16 min  (16 * 60sec/min* 1000msec/sec)=960000

 //writeRegister(ELEV_UP_POT_LIMIT,250);
 //writeRegister(ELEV_DOWN_POT_LIMIT,146);
 //writeRegister(AZIM_EAST_POT_LIMIT,2);
 //writeRegister(AZIM_WEST_POT_LIMIT,251);
 //writeRegister(AZIM_SOUTH,2+((251-2)/2));
 
 elev_pot_up_limit=readRegister(ELEV_UP_POT_LIMIT);
 elev_pot_down_limit=readRegister(ELEV_DOWN_POT_LIMIT);
 azim_pot_east_limit=readRegister(AZIM_EAST_POT_LIMIT);
 azim_pot_west_limit=readRegister(AZIM_WEST_POT_LIMIT);
 //azim_south=readRegister(AZIM_SOUTH);
 
 mins_to_next_move_count=0;
 just_moved_flag=0;
 sun_calculate();
 current_sun_calculate();
 
}

//-----------------------------------------------------//
//Κυρίως Πρόγραμμα
void loop(){
 
   if (calibration_flag==HIGH){
    switch (calibration_state){
       case 8:if (check_elev_limits()==2) {move_all_stop(); calibrate_pot_UP();} else {move_elev_UP(); auto_man_flag=2;auto_move_elev_flag=0;}
       break;
       case 4:if (check_elev_limits()==1) {move_all_stop(); calibrate_pot_DOWN();} else {move_elev_DOWN(); auto_man_flag=2;auto_move_elev_flag=0;}
       break;
       case 2:if (check_azim_limits()==2) {move_all_stop();calibrate_pot_EAST();} else {move_azim_EAST(); auto_man_flag=2; auto_move_azim_flag=0;} 
       break;
       case 1:if (check_azim_limits()==1) {move_all_stop();calibrate_pot_WEST();} else {move_azim_WEST(); auto_man_flag=2; auto_move_azim_flag=0;}
       break;
       case 0:{move_all_stop();auto_man_flag=LOW;calibration_flag=LOW;}
       break;
    }
  } else {
   
   if (check_auto_man()==LOW){
     switch(check_buttons()){
       case 8:if (check_elev_limits()==2) move_all_stop(); else {move_elev_UP(); auto_move_elev_flag=0;}
       break;
       case 4:if (check_elev_limits()==1) move_all_stop(); else {move_elev_DOWN(); auto_move_elev_flag=0;}
       break;
       case 2:if (check_azim_limits()==2) move_all_stop(); else {move_azim_EAST();  auto_move_azim_flag=0;} 
       break;
       case 1:if (check_azim_limits()==1) move_all_stop(); else {move_azim_WEST();  auto_move_azim_flag=0;}
       break;
       case 10:{calibration_flag=HIGH; calibration_state=8;}
       break;
       case 0:move_all_stop();
       break;
      }
   } 
   
   
   
   if (check_auto_man()==HIGH){
     if (elevation_int<=0) auto_man_flag=3;
     if (auto_move_elev_flag==0) automove_elev(auto_move_elev_deg);
     else if (auto_move_azim_flag==0) automove_azim(auto_move_azim_deg);
   }
  }
  
    if ((check_elev_limits()>0) || (check_azim_limits()>0)) digitalWrite(limits_LED,HIGH); else digitalWrite(limits_LED,LOW);

   
     
    t.update();
}