SOFTWARE

 

-The last problem is the software, it is very difficult to give a general guide for every use.

And maybe it's too complicated and precise for a DIY machine and his purpose.

This machine can be used as a  solar tracker or as a mirror to follow sun and reflect the light inside the house.

I didn’t like to use a SD with ".txt" file with all the coordinates of the sun, but Since I studied at my engineering course a lot about the sun, classic Mechanics and about earth movements I rebuilt the equations with high accuracy 0.1% (it counts the reflection of the light too).

I use the ESP module to get the real time from internet and check the weather conditions.

If it rains I move the mirror to protect the case as much as possible from the water , and if it is too windy (speed and direction download from internet) the software puts the mirror in a “flag” position. 

(a 30km/h of wind can cause almost 50N of forces and high torques (15Nm in the worst case) with 1m^2 of mirror).

I left the original bootloader in the ESP8266, which I use for the real time and weather conditions. I just send command and read the answers from the second Tx Rx of the Arduino Due. Those are the commands I use.

AT+CWMODE=1                                                        %choose the right mode 
AT+CWDHCP=1,1                                                      %set DHCP for the ip                                                                                            assignment
AT+CWLAP                                                              %list of access point

AT+CWJAP="WIFI_NAME","PASSWORD"                     %Insert you wifi and pass
AT+CIPSNTPCFG=1,0,"0.europe.pool.ntp.org"              %this is the server for                                                                                           time information
AT+CIPSNTPTIME?                                                    %returns time

For each of those command you need to send, wait and save the answer for preventing errors and and choose the right behave of the Solar tracker.

If you want weather infomation you need to create an API key on a website.. I advice 

https://openweathermap.org/ 

there are 6 sketches, just save them in more files in the same folder:

 

- az_el_angles.h  here there all the equation and calculus needed for the sun position                                         and mirror position depending on the "mode" choosen;

- data_server.h   function for calling the date time on internet;

- funzioni_varie.h  sorry for the italian name, it just means "other functions" used in the                                        program;

- global_data.h       all the parameters and string needed in the program;

-weather_server.h  This is the function that elaborates the data downloaded from the API key

- homing.ino          The main file in which it is created the general behavior of the mirror  

I don't upload the complete files to let you understand that it is not a "copy and paste" solution, but I insist that you take your time reading it and setting your parameters (working time, time_zone, working mode etc...)

weather_server.h

 

#include "WString.h"

/*Example of one server answer
busy s...

Recv 101 bytes

SEND OK

+IPD,0,469:{"coord":{"lon":10.41,"lat":43.71},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"stations","main":{"temp":298.22,"feels_like":297.28,"temp_min":297.15,"temp_max":299.26,"pressure":1023,"humidity":60},"visibility":10000,"wind":{"speed":4.6,"deg":280},"clouds":{"all":40},"dt":1590322981,"sys":{"type":1,"id":6803,"country":"IT","sunrise":1590291882,"sunset":1590345965},"timezone":7200,"id":6542122,"name":"Pisa","cod":200}0,CLOSED
*/

struct string_find_result
{
 bool found;
 int index; 
};


struct string_find_result find_substring(String s, String sub_s)  //returns the position of the first caracters of the substring
{
  struct string_find_result res;
  res.found=false;
 for(int i=0;i<s.length()-sub_s.length();i++) 
  {
    String temp=s.substring(i,i+sub_s.length());
    if(temp.compareTo(sub_s)==0) {res.index=i; res.found=true; break;} 
    
  }

 return res;
}


bool bad_weather(String s) //in case of "Rain", "Thunderstorm", "Drizzle", "Snow" returns true
{
  bool b_w;

  
  // find substring "weather":[{
    struct string_find_result res1=find_substring(s,"\"weather\":[{");
    

  // find of the substring }], ,"deg": 
    struct string_find_result res2=find_substring(s,",\"deg\"");
 

    if(res1.found==false || res2.found==false) b_w=false; //stringa diversa da quella attesa--> ignorata e bad_weather=false
    else 
    {
       
      String sub_s=s.substring(res1.index,res2.index);
       
      struct string_find_result res3=find_substring(sub_s,"\"Rain\"");
      struct string_find_result res4=find_substring(sub_s,"\"Snow\"");
      struct string_find_result res5=find_substring(sub_s,"\"Drizzle\"");
      struct string_find_result res6=find_substring(sub_s,"\"Thunderstorm\"");

      if(res3.found==true || res4.found==true ||res5.found==true ||res6.found==true) b_w=true;
      else b_w=false;
      
    }

   
  return b_w;
}


bool high_speed_wind(String s, float wind_speed_max) // in case of winds with a speed bigger than the setted parameter, it returns true
{
 bool h_s_w;

// finding the substring "wind":{"speed":
    struct string_find_result res1=find_substring(s,"\"wind\":{\"speed\":");

  // frinding the substring ,"deg"
    struct string_find_result res2=find_substring(s,",\"deg\"");

    if(res1.found==false || res2.found==false) h_s_w=false; //stringa diversa da quella attesa--> ignorata e high_speed_wind=false
    else 
    {
      String sub_s=s.substring(res1.index+16,res2.index);
      //Serial.print(sub_s);
      float wind_speed=sub_s.toFloat();

      if(wind_speed>=wind_speed_max) h_s_w=true;
      else h_s_w=false;
    }

 return h_s_w; 
}
 

 

global_data.h

#include "WString.h"

//door direction (relative position of the door where reflects the sunlight)
double az_door_deg=287;
double el_door_deg=-2;

//working time of the mirror
int orario_utc_operativo[]={6,17};

String reset_impostazioni = "AT+RST\r\n";                                                                              

//RESET SETTINGS ESP8266
String mode_client = "AT+CWMODE=1\r\n";                                                                                //CHOOSE THE MODE (1= client) (2=AP) (3=BOTH)
String mode_DHCP = "AT+CWDHCP=1,1\r\n";                                                                         //mode DHCP for the IP assignment
String scelta_rete_wifi = "AT+CWJAP=\"YOUR_WIFI_NAME\",\"YOUR_PASS\"\r\n";                                

 //pick the right AP and insert the password
String server_timezone = "AT+CIPSNTPCFG=1,0,\"europe.pool.ntp.org\"\r\n";                                              //pick the server (the second number it is the timezone 0=UTC)
String richiesta_tempo = "AT+CIPSNTPTIME?\r\n";                                                                        //take time form server
String set_connection_type="AT+CIPMUX=1\r\n";                                                                          // setconnection number
String connessione_dominio= "AT+CIPSTART=0,\"TCP\",\"api.openweathermap.org\",80\r\n";                                 //CONNECT TO THE RIGHT DOMAIN
String manda_dati= "AT+CIPSEND=0,101\r\n";                                                                             //SEND REQUEST TO THE SERVER WITH RIGHT NUMER OF CARACTERS
String manda_URL="GET /data/2.5/weather?id=YOUR_CITY&appid=YOUR_API_KEY\r\nHost: api.openweathermap.org\r\n"; //SEND THE QUERY WITH THE URL


String risposta = "";
String DATA_TEMPO = "";
String weather_API="";
const int buttonPin_2 = A7;
int value = 0;
const int buttonPin = 9;
bool buttonState = true;
bool buttonState_2=true;
double* posizione=new double[2];      // 0 ELEVATION; 1 AZIMUTH
const int red_led_pin=2;
const int green_led_pin=10;

// geometric value parameters
const double rapporto_assiale = 62.1428;
const double rapporto_riduttore = 45.0;

// constraints
const double elev_min = 0.0; //minimum elevation relative to the home position
const double azim_min = 60.0;   // minimum azimuth relative to the home position
const double azim_max = 360.0;
const double elev_max = 85.0;
const String mode="mirror";// "solar_tracker"     choose the mode you want


const double homing_speed = 10 * 500;   // velocità in step al secondo 2000=2giri/min
 
int freq_blink_led_Hz=10; 
float MAX_WIND=8; //m/s

 


long int DT_setup_s= 10800;         // time frame between two loop cycle
long int DT_loop_s=30;              // minimum duration of the loop cycle

bool  BADWEATHER;   // if it's true the weather is dangerous (wind or rain)

data_server.h

 


#include "WString.h"

struct UTC_time data(String s) //earns datetime UTC from the input string (coming from internet)
{
int pos_spazi[5];

//Fino al primo spazio ignorare la stringa
int k=0;
for(int i=0;i<s.length();i++)if(s[i]==32) {pos_spazi[k]=i;k++;}

String sigla_mese[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

String nome_mese=s.substring(pos_spazi[0]+1,pos_spazi[0]+1+3);
int mm=0;
for (int i=0;i<12;i++) if(nome_mese==sigla_mese[i]) mm=i+1;

int day=0;
String giorno_mese=s.substring(pos_spazi[1]+1,pos_spazi[1]+1+2);
if(giorno_mese[0]=='0') giorno_mese.remove(0,1);
day=giorno_mese.toInt();


String times=s.substring(pos_spazi[2]+1,pos_spazi[2]+1+8);
String hour=times.substring(0,2);
if(hour[0]=='0') hour.remove(0,1);
int h=0;
h=hour.toInt();


String minutes=times.substring(3,3+2);
if(minutes[0]=='0') minutes.remove(0,1);
int minute=0;
minute=minutes.toInt();

String seconds=times.substring(6,6+2);
if(seconds[0]=='0') seconds.remove(0,1);
int sec=0;
sec=seconds.toInt();

String year=s.substring(pos_spazi[3]+1,pos_spazi[3]+1+4);
int yyyy=0;
yyyy=year.toInt();

struct UTC_time data(day, mm, yyyy,h,minute,sec);

return data;
}
 

 

funzioni_varie.h

Not all of these functions are really obligatory (aesthetics and debug functions )

#include <AccelStepper.h>
#include <SPI.h>
#include <Wire.h>
#include "WString.h"
#include <rtc_clock.h>

#define MOTOR_AZI_ENABLE_PIN 5
#define MOTOR_AZI_STEP_PIN 6
#define MOTOR_AZI_DIR_PIN 8

#define MOTOR_ELE_ENABLE_PIN 5
#define MOTOR_ELE_STEP_PIN 3
#define MOTOR_ELE_DIR_PIN 4

 

AccelStepper motorAZI(1, MOTOR_AZI_STEP_PIN, MOTOR_AZI_DIR_PIN);
AccelStepper motorELE(1, MOTOR_ELE_STEP_PIN, MOTOR_ELE_DIR_PIN);

char c;
//---------------------------------------------------------------------------------------------------------------------------------------------------
extern const int buttonPin_2;
extern int value;
extern const int buttonPin;
extern bool buttonState;
extern bool buttonState_2;
extern double* posizione;
extern const double homing_zero_potentiometer;
extern const double rapporto_assiale;
extern const double rapporto_riduttore;
extern const double azim_min;
extern const double azim_max;
extern const double elev_min;
extern const double elev_max;
extern const double homing_speed;
extern const int red_led_pin;
extern const int green_led_pin;

 

void(* resetFunc) (void) = 0;

bool is_admissible_destination(double* destinazione)
{
  bool out=false;
  if (destinazione[0] >= azim_min && destinazione[1] >= elev_min && destinazione[0] <= azim_max && destinazione[1] <= elev_max) {out=true;}
  return out;
}

void red_led_on()
{
  digitalWrite(red_led_pin, HIGH);
}

void red_led_off()
{
  digitalWrite(red_led_pin, LOW);
}

void green_led_on()
{
  digitalWrite(green_led_pin, HIGH);
}

void green_led_off()
{
  digitalWrite(green_led_pin, LOW);
}


void clock_setup(RTC_clock& rtc_clock, struct UTC_time& data)
{
  rtc_clock.init();
  rtc_clock.set_time(data.h,data.minute,data.secs);
  rtc_clock.set_date(data.dd,data.mm,data.yy);
  return;
}

void print_clock_time(RTC_clock& rtc_clock)
{
  Serial.println("RTC clock time: ");
  Serial.print(rtc_clock.get_hours());
  Serial.print(":");
  Serial.print(rtc_clock.get_minutes());
  Serial.print(":");
  Serial.print(rtc_clock.get_seconds());
  Serial.print(" del  ");
  Serial.print(rtc_clock.get_days());
  Serial.print(".");
  Serial.print(rtc_clock.get_months());
  Serial.print(".");
  Serial.println(rtc_clock.get_years());
}

long int time_elap_s(RTC_clock& rtc_clock,struct UTC_time& data)    // duration from the last setup cicle
{
  //Ipotesi: tra le due date ci devono essere meno di 24 ore di differenza
  long int t_elap=0;
  
  //Orario quando è stato eseguito l'ultimo setup
  int h_old=data.h;
  int min_old=data.minute;
  int sec_old=data.secs;
  long int t_old=h_old*3600+min_old*60+sec_old;

  //Ora attuale 
  int h_now=rtc_clock.get_hours();
  int min_now=rtc_clock.get_minutes();
  int sec_now=rtc_clock.get_seconds();
  long int t_now;
  if(h_now<h_old) t_now=(h_now+24)*3600+min_now*60+sec_now;
  else t_now=h_now*3600+min_now*60+sec_now;

  t_elap=t_now-t_old;
  
}

struct UTC_time save_date_time(RTC_clock& rtc_clock)
{
  struct UTC_time data;
  data.h=rtc_clock.get_hours();
  data.minute=rtc_clock.get_minutes();
  data.secs=rtc_clock.get_seconds();
  data.dd=rtc_clock.get_days();
  data.mm=rtc_clock.get_months();
  data.yy=rtc_clock.get_years();

  return data;
}

// blinking in case of adverse weather
void Alternate_blinking(RTC_clock& rtc_clock,long int time_to_wait_s, int freq_Hz)
{
    green_led_on(); //led turn on
    red_led_off();   //led turn off

    struct UTC_time data_inizio_blinking=save_date_time(rtc_clock);
    
    Serial.println("Meteo avverso! Tempo da attendere[s]= ");
    Serial.print(time_to_wait_s);  
    while(time_to_wait_s>time_elap_s(rtc_clock,data_inizio_blinking)) // per tutta la durata del tempo di attesa i led lampeggiano 
    {
     red_led_on();
     green_led_off();
     delay(500/freq_Hz); //durata led accesi: metà durata del ciclo
     red_led_off();
     green_led_off();
     delay(500/freq_Hz); // durata led spento: metà durata del ciclo
    } 
}

//blinking because out of the working time
void blinking_leds_mode(RTC_clock& rtc_clock,int* orario_operativo, int freq_Hz)
{
    green_led_off(); //led turn off
    red_led_off();   //led turn off
    struct UTC_time data_inizio_blinking=save_date_time(rtc_clock);  
    long int time_to_wait_s;

    if(data_inizio_blinking.h>orario_operativo[1]) time_to_wait_s=(orario_operativo[0]+24-data_inizio_blinking.h)*3600-data_inizio_blinking.minute*60+data_inizio_blinking.secs;
    else if(data_inizio_blinking.h<orario_operativo[0])  time_to_wait_s=(orario_operativo[0]-data_inizio_blinking.h)*3600-data_inizio_blinking.minute*60+data_inizio_blinking.secs;

    Serial.println("Orario non operativo! Tempo da attendere[s]= ");
    Serial.print(time_to_wait_s);
    while(time_to_wait_s>time_elap_s(rtc_clock,data_inizio_blinking)) // per tutta la durata del tempo di attesa i led lampeggiano 
    {
     red_led_on();
     green_led_on();
     delay(500/freq_Hz); //durata led accesi: metà durata del ciclo
     red_led_off();
     green_led_off();
     delay(500/freq_Hz); // durata led spento: metà durata del ciclo
    } 
}


bool is_sun_up(struct UTC_time & DATA)// è vero se Sun elevation >0
{
  bool out=false;
  double* sun_pos=output_angle(DATA,"solar_tracker",0, 0); // posizione attuale del Sole
  if(sun_pos[1]>0) out=true;
  return out;
}

void wait_nostop(long int intervallo)
{
  long int tempo_iniziale = millis();
  long int tempo = millis();
  while (tempo - tempo_iniziale < intervallo)
  {
    tempo = millis();
  }
}


String manda_comando(String comando, int timeout ) //comunication with the ESPmodule

{

  int tempo_iniziale = millis();
  int tempo;
  String response = "";
  Serial1.print(comando);
  tempo = millis();
  while (tempo - tempo_iniziale < timeout)
  {
    tempo = millis();

    while (Serial1.available())
    {
      c = Serial1.read(); // read the next character.
      response += c;
    }
  }
  Serial.print(response);
  return response;
}

long int* calcolo_steps(double* destinazione) // knowing the destination  it computes the numbers of the steps    0 ELEVATION;  1 AZIMUTH  
{
  double  steps[2];
  long int* step_intero=new long int[2]; 
  
  steps[0] = (-posizione[0] + destinazione[1]) * (8.8888) * (rapporto_riduttore); // motore elevation
  steps[1] = (-posizione[1] + destinazione[0]) * (8.8888) * (rapporto_assiale);   //motore azimuth    faccio 360°-angolo per il diverso verso di rotazione 
 
  step_intero[0] = (long int)steps[0];
  step_intero[1] = (long int)steps[1];

  return step_intero;
}

void muoviELE_AZI(long int* spo) //it moves the motor as much as indicated in the  array "spo" which it is been returned from the  the function "calcolo_steps"
{

  motorELE.move(spo[0]);
  motorAZI.move(spo[1]);

  while (motorAZI.distanceToGo() != 0 || motorELE.distanceToGo() != 0)
  {
    motorAZI.run();
    motorELE.run();
  }
    motorELE.stop();
    motorAZI.stop();
  }


void safeconfig() // safe config in case of adverse weather: azimuth=az_min and elevazion=elev_max
{
  double destinazione[] ={azim_min,elev_max} ;

  if (is_admissible_destination(destinazione))
  {
    long int* step_int=calcolo_steps(destinazione);
    Serial.println("Raggiungimento posizione di sicurezza!");
    muoviELE_AZI(step_int);   //SE VALIDI MUOVE I MOTORI
    posizione[0] = destinazione[1];  // aggiornamento elevation attuale
    posizione[1] = destinazione[0];   // aggiornamento azimuth attuale
  }
  else Serial.println("Posizione non ammissibile di safeconfig()!");
}


void homing() // going to the homing position: azimuth=0 and elevazion=0
{
  Serial.println("Homing: iniziato");
  motorELE.setSpeed(-homing_speed);    
  buttonState = digitalRead(buttonPin);
  while (buttonState == true)
  {
    motorELE.runSpeed();
    buttonState = digitalRead(buttonPin);
    if (buttonState== false)
    {
    delay(20);
    buttonState = digitalRead(buttonPin);
    }
  }
  motorELE.stop();
  delay(2000);
  motorELE.setSpeed(homing_speed);
  Serial.println("Homing_teta terminato");

  motorAZI.setSpeed(homing_speed);    
  buttonState_2 = digitalRead(buttonPin_2);
  while (buttonState_2 == true)
  {
    motorAZI.runSpeed();
    buttonState_2 = digitalRead(buttonPin_2);
    if (buttonState_2== false)
    {
    delay(20);
    buttonState_2 = digitalRead(buttonPin_2);
    }
  }
  motorAZI.stop();
  delay(2000);
  motorAZI.setSpeed(-homing_speed);
  
  Serial.println("Homing_phi terminato");
  posizione[0] = 0.00;
  posizione[1] = 360.00;
}


void settaggi_motore()
{
    pinMode(buttonPin, INPUT);
    pinMode(buttonPin_2, INPUT);
    motorAZI.setEnablePin(MOTOR_AZI_ENABLE_PIN);
    motorAZI.setPinsInverted(false, false, true);
    motorELE.setEnablePin(MOTOR_ELE_ENABLE_PIN);
    motorELE.setPinsInverted(false, false, true);
    pinMode(MOTOR_AZI_ENABLE_PIN, OUTPUT);
    pinMode(MOTOR_ELE_ENABLE_PIN, OUTPUT);                                 //motor settings 
  
    motorAZI.setMaxSpeed(200 * 8);
    motorAZI.setSpeed(200 * 8);
    motorAZI.setAcceleration(400.0);
  
    motorELE.setMaxSpeed(200 * 4);
    motorELE.setSpeed(-200 * 4);
    motorELE.setAcceleration(400.0);
  
    motorAZI.enableOutputs(); // power on the motors
    motorELE.enableOutputs();
}

void settaggio_led()
{
  pinMode(red_led_pin, OUTPUT);      // sets the digital pin as output
  pinMode(green_led_pin, OUTPUT);    // sets the digital pin as output
}

void connessione_scheda_wifi(String reset_impostazioni, String mode_client, String mode_DHCP, String scelta_rete_wifi, String server_timezone)
{
  Serial1.begin(115200); // comunicazione con il modulo wi-fi
  String risposta;
  risposta = manda_comando(reset_impostazioni, 1500);
  risposta = manda_comando(mode_client, 1500);                                 // WIFI connection
  risposta = manda_comando(mode_DHCP, 1500);
  risposta = manda_comando(scelta_rete_wifi, 20000);
  risposta = manda_comando(server_timezone, 1500);

  return;
}

void print_current_position()
{
  Serial.println("Posizione: Elevation[deg]= ");
  Serial.print(posizione[0]);
  Serial.print(" Azimuth[deg]=");
  Serial.println(posizione[1]);
}

struct UTC_time date_time_acquisition(String richiesta_tempo)
{
  //------ date and time acquisition ----------------------------------------------------------------------------------------------------------------------------------------------------------
  String DATA_TEMPO = manda_comando(richiesta_tempo, 2000);   //RICHIESTA TEMPO AL SERVER
  struct UTC_time DATA = data(DATA_TEMPO);                    //from the output string of the server it saves the date in a array
  while(DATA.correct()==false && DATA.yy!=1970)               // right acquisition from the server
    { 
     Serial.println("Acquisizione data dal server in setup() non riuscita! Nuovo tentativo in corso.");
    
     DATA_TEMPO = manda_comando(richiesta_tempo, 2000);   //time acquisition 
     DATA = data(DATA_TEMPO);                         // from the output string of the server it saves the date in a array
     delay(10000);    
    }  

  return DATA;
}


String weather_acquisition(String set_connection_type,String connessione_dominio, String manda_dati, String manda_URL)
{
   // conditions acquisition of the weather from the server
    Serial.println("Richiesta meteo al server");
    String risposta = manda_comando(set_connection_type,2000);
    risposta = manda_comando(connessione_dominio,2000);                   //WEATHER CONDITION
    risposta = manda_comando(manda_dati,3000);
    String weather_API = manda_comando(manda_URL,3000);
    return weather_API;
}


bool is_safe_config_necessary(String weather_API,float MAX_WIND)
{
  bool RAINY=bad_weather(weather_API);                                       
  bool WINDY=high_speed_wind(weather_API,MAX_WIND); 
  if(RAINY==true || WINDY==true) return true;
  else return false;
}


void led_mode(String mode)
{
  if(mode=="setup")      red_led_on();                     // durante il setup il led rosso è acceso
  else if(mode=="loop") {red_led_off(); green_led_on();}   // durante il loop solo il led verde è acceso
  else if(mode=="not_adm"){red_led_on(); green_led_on();}  // quando la posizione non è ammissibile sia il rosso che il verde sono accesi

}

void print_motor_steps(long int* step_int)
{
  Serial.println("Step motore: Elevation= ");
  Serial.print(step_int[0]);
  Serial.print(" steps; Azimuth= ");
  Serial.print(step_int[1]);
  Serial.println(" steps");
}


double* reach_destination(double* destination)
{
  long int* step_int=calcolo_steps(destination);  // compute the number of the steps
  print_motor_steps(step_int);
    
  // comando ai motori
  muoviELE_AZI(step_int);          //if they are good , the motor will move

  double* pos=new double[2];
  pos[0] = destination[1];  // update the present elevation
  pos[1] = destination[0];  // update the present azimuth

  return pos;
}
 

 

homing.ino

#include <WString.h>
#include <Arduino.h>
#include "global_data.h"
#include "az_el_angles.h"    //FOGLIO FUNZIONI CALCOLO ANGOLI
#include "data_server.h"
#include "weather_server.h"
#include "funzioni_varie.h"

#include <rtc_clock.h>

//RTC_clock rtc_clock(XTAL);
RTC_clock rtc_clock(RC);
//---------------------------------------------------------------------------------------------------------------------------------------------------
struct UTC_time DATA(1,1,1970,0,0,0);

void setup()
{
  Serial.begin(115200); // usb serial
 
  settaggi_motore();                                                // motor settings
  settaggio_led();                                                    // led settings
  led_mode("setup");
  connessione_scheda_wifi(reset_impostazioni, mode_client, mode_DHCP, scelta_rete_wifi, server_timezone); // connection with wifi module 
  homing();                                                                                               // homing
  print_current_position();                                                                               // print the present position
  DATA=date_time_acquisition(richiesta_tempo);                                                            // Acquisizione da internet della data e ora attuali
  clock_setup(rtc_clock,DATA);                                                                            // Inizializzazione orario interno Arduino
  print_clock_time(rtc_clock);                                                                            // print the date and time
  
  weather_API=weather_acquisition(set_connection_type,connessione_dominio,manda_dati,manda_URL);          //  Acquisition of the weather conditions from the server

  BADWEATHER=is_safe_config_necessary(weather_API,MAX_WIND);
  if(BADWEATHER==true)                                                        // if the weather is adverse , it goes to the safe configuration

     {
        // safe config 
        Serial.println("Condizioni meteo avverse!");
        safeconfig();
        Alternate_blinking(rtc_clock,DT_setup_s,2*freq_blink_led_Hz); //led blinking
     }    
 
}

void loop()
{
  
      struct UTC_time data_inizio_loop=save_date_time(rtc_clock);
      led_mode("loop");
     
      if(data_inizio_loop.h<orario_utc_operativo[0] || data_inizio_loop.h>orario_utc_operativo[1]) {Serial.print("Specchio non operativo di notte! Attesa fino a ");Serial.print(orario_utc_operativo[0]);Serial.println(" UTC");safeconfig(); blinking_leds_mode(rtc_clock,orario_utc_operativo, freq_blink_led_Hz);} // di notte (dopo mezzanotte)
      else // di giorno
      {
            if(time_elap_s(rtc_clock,DATA)>=DT_setup_s) setup();
      
            if(BADWEATHER==true) 
            {
              Serial.println("Condizioni meteo avverse!");
              safeconfig();
              Alternate_blinking(rtc_clock,DT_setup_s-time_elap_s(rtc_clock,DATA),2*freq_blink_led_Hz);
              }
            else
            {
              Serial.println("Orario operativo e condizioni meteo favorevoli");
              led_mode("loop");
           
              double* destinazione = output_angle(DATA,mode,az_door_deg,el_door_deg);    //  it computes  the new configuration that depends on the sun position [0]=AZIMUTH;[1] ELEVATION; 

              if (is_admissible_destination(destinazione)) { Serial.println("Posizione ammissibile"); posizione=reach_destination(destinazione); }
              else {Serial.println("Posizione NON ammissibile"); led_mode("not_adm"); }
            
              // wait before calling again the weather condition from the server
              while ( time_elap_s(rtc_clock,data_inizio_loop) < DT_loop_s)  { } 
            }
       }
       
}

az_el_angles.h

 

this section is very difficult, and it required a lot of time to achieve the right knowledge to be written.

Just change the geographic coordinates and it should work 

#include"WString.h"
struct UTC_time   // data UTC
{
   int dd;
   int mm;
   int yy;
   int h;
   int minute;
   int secs;
   double D;  // giorno con ore, minuti e secondi espressi in frazioni di giorno
   bool correct();
   UTC_time(int day, int month, int year, int hour, int minutes, int seconds);
   UTC_time();
   void update(int time_ms); // aggiorna la data sapendo il tempo trascorso in millisecondi
};

UTC_time::UTC_time(int day, int month, int year, int hour, int minutes, int seconds)
{
   dd=day;
   mm=month;
   yy=year;
   h=hour;
   minute=minutes;
   secs=seconds;
   D=dd+((double)h/(double)24)+((double)minute/(double)1440)+((double)secs/(double)86400);
}

UTC_time::UTC_time()
{}

bool UTC_time::correct()
{
  if(dd<=0 || dd>31 || mm<=0 || mm>12 || yy<2020) return false;
  else return true;
}

bool bisestile(int yy)
{
  bool bis=false;
  int v=(yy-2020)%4; // il 2020 è un anno bisestile
  if(v%4==0) bis=true;

  return bis;
}

int giorni_mese(int month, int year)
{
  int arr[]={31,28,31,30,31,30,31,31,30,31,30,31};

  if (bisestile(year)==true && month==2) return (arr[1]+1);
  else return arr[month-1];

}

void UTC_time::update(int time_ms)
{

 int time_s=time_ms/1000;

 int giorni_trascorsi=time_s/86400;

 time_s-=giorni_trascorsi*86400;

 int ore_trascorse=time_s/3600;

 time_s-=ore_trascorse*3600;

 int minuti_trascorsi=time_s/60;

 time_s-=minuti_trascorsi*60;

 int secondi_trascorsi=time_s;

 secs+=secondi_trascorsi;
 minute+=minuti_trascorsi;
 h+=ore_trascorse;
 dd+=giorni_trascorsi;

 minute+=secs/60;
 h+=minute/60;
 dd+=h/24;

 secs=secs%60;
 minute=minute%60;
 h=h%24;

     while(dd>giorni_mese(mm,yy))
     {
       dd-=giorni_mese(mm,yy);

       if(mm==12) {yy++;mm=1;}
       else mm++;
     }

D=dd+((double)h/(double)24)+((double)minute/(double)1440)+((double)secs/(double)86400);
}


struct geographic_coord // Geographic position
{
  double latitude_deg= 43......;                                           //your coordinates
  double longitude_deg=10......;

  double latitude_rad= latitude_deg*M_PI/180;
  double longitude_rad=longitude_deg*M_PI/180;
};


struct Julian_date
{
   double JD; //julian day
   double JC; //julian century
   double JM; //julian millennium


   Julian_date(UTC_time& date); //constructor

};

Julian_date::Julian_date(struct UTC_time& date)
{

  int year=date.yy;
  int month=date.mm;

  if (month<=2) { year-=1; month+=12;}

  int A=year/100;
  int B=2-A+A/4;

  double a=floor(365.25*(year+4716));
  double b=floor(30.6001*(month+1));
  double c=B-1524.5;

  JD=a+b+date.D+c;
  JC=(JD-(double)2451545)/((double)36525);
  JM=(JC/(double)10);

}

double L0(struct Julian_date& J)
{
   unsigned long A[]={175347046, 3341656, 34894,
              3497, 3418, 3136, 2676,
              2343, 1324, 1273, 1199,
               990, 902, 857, 780,753,
               505,492,357,317,284,271,
               243,206,205,202,156,132,
               126,115,103,102,102,99,
               98,86,85,85,80,79,75,74,
               74,70,62,61,57,56,56,52,52,51,49,
              41,41,39,37,37,36,36,
              33,30,30,25};


     double B[]={0,4.6692568,4.6261,2.7441,
              2.8289,3.6277,4.4181,6.1352,
              0.7425,2.0371,1.1096,5.233,
              2.045,3.508,1.179,2.533,
              4.583,4.205,2.92,5.849,
              1.899,0.315,0.345,4.806,
              1.869,2.458,0.833,3.411,
              1.083,0.645,0.636,0.976,
              4.267,6.21,0.68,5.98,
              1.3,3.67,1.81,3.04,1.76,
              3.5,4.68,0.83,3.98,1.82,
              2.78,4.39,3.47,0.19,1.33,
              0.28,0.49,5.37,2.4,6.17,
              6.04,2.57,1.71,1.78,0.59,
              0.44,2.74,3.16};

     double C[]={0,6283.07585,12566.1517,
                  5753.3849,3.5231,77713.7715,
                  7860.4194,3930.2097,11506.7698,
                  529.691,1577.3435,5884.927,26.298,
                  398.149,5223.694,5507.553,18849.228,
                  775.523,0.067,11790.629,796.298,
                  10977.079,5486.778,2544.314,5573.143,
                  6069.777,213.299,2942.463,20.775,0.98,
                  4694.003,15720.839,7.114,2146.17,155.42,
                  161000.69,6275.96,71430.7, 17260.15,12036.46,
                  5088.63,3154.69,801.82,9437.76,8827.39,
                  7084.9,6286.6,14143.5,6279.55,12139.55,
                  1748.02,5856.48,1194.45,8429.24,19651.05,
                  10447.39,10213.29,1059.38,2352.87,6812.77,
                  17789.85,83996.85,1349.87,4690.48};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);

return out;
}

double L1(struct Julian_date& J)
{
   long long A[]={628331966747,206059,4303,
               425,119,109,93,72,68,67,
               59,56,45,36,29,21,19,19,
               17,16,16,15,12,12,12,12,
               11,10,10,9,9,8,6,6};

    double B[]={0,2.678235,2.6351,1.59,
                5.796,2.966,2.59,1.14,1.87,
                4.41,2.89,2.17,0.4,0.47,
                2.65,5.34,1.85,4.97,2.99,
                0.03,1.43,1.21,2.83,3.26,
                5.27,2.08,0.77,1.3,4.24,
                2.7,5.64,5.3,2.65,4.67};

    double C[]={0,6283.07585,12566.1517,
                3.523,26.298,1577.344,
                18849.23,529.69,398.15,
                5507.55,5223.69,155.42,
                796.3,775.52,7.11,0.98,
                5486.78,213.3,6275.96,
                2544.31,2146.17,10977.08,
                1748.02,5088.63,1194.45,
                4694,553.57,6286.6,1349.87,
                242.73,951.72,2352.57,
                9437.76,4690.48};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}

double L2(struct Julian_date& J)
{
   unsigned long A[]={52919,8720,309,
               27,16,16,10,9,7,
               5,4,4,3,3,3,3,3,3,2,2};


    double B[]={0,1.0721,0.867,
                0.05,5.19,3.68,
                0.76,2.06,0.83,
                4.66,1.03,3.44,
                5.14,6.05,1.19,
                6.12,0.31,2.28,
                4.38,3.75};

    double C[]={0,6283.0758,12566.152,
                3.52,26.3,155.42,18849.23,
                77713.77,775.52,1577.34,
                7.11,5573.14,796.3,5507.55,
                242.73,529.69,398.15,
                553.57,5223.69,0.98};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}

double L3(struct Julian_date& J)
{
   double A[]={289,35,17,3,1,1,1};


    double B[]={5.844,0,5.49,5.2,4.72,5.3,5.97};

    double C[]={6283.076,0,12566.15,155.42,3.52,18849.23,242.73};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}

double L4(struct Julian_date& J)
{
   double A[]={114,8,1};


    double B[]={3.142,4.13,3.84};

    double C[]={0,6283.08,12566.15};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}


double L5(struct Julian_date& J)
{
   double A=1;
   double B=3.14;
   double C=0;

double out=A*cos(B+C*J.JM);

return out;
}

double B0_coef(struct Julian_date& J)
{
   double A[]={280,102,80,44,32};


    double B[]={3.199,5.422,3.88,3.7,4};

    double C[]={84334.662,5507.553,5223.69,2352.87,1577.34};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}


double B1_coef(struct Julian_date& J)
{
   double A[]={9,6};


    double B[]={3.9,1.73};

    double C[]={5507.55,5223.69};

double out=0;

for(int i=0;i<sizeof(A)/sizeof(A[0]);i++) out+=A[i]*cos(B[i]+C[i]*J.JM);
//cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(C)<<endl;
return out;
}

 


struct Earth_heliocentric_coordinates
{
    
    //coefficienti per il calcolo della longitudine eliocentrica della Terra
    double L_0;
    double L_1;
    double L_2;
    double L_3;
    double L_4;
    double L_5;

    //coefficienti per il calcolo della latitudine eliocentrica della Terra
    double B_0;
    double B_1;
    double L_rad;  //Earth heliocentric longitude
    double L_deg;
    double B_rad;  //Earth heliocentric latitude
    double B_deg;
    double teta_rad; //earth geocentric longitude
    double teta_deg;
    double beta_rad; //earth geocentric latitude
    double beta_deg;

    double epsilon_deg=23.440465;             //inclinazione asse terrestre (nutazione trascurabile)
    double epsilon_rad=epsilon_deg*M_PI/180;

    double alpha_rad;  //run right ascension
    double ni_rad;     // apparent sidereal time at Greenwich
    double ni_deg;

   Earth_heliocentric_coordinates (Julian_date&);


};

Earth_heliocentric_coordinates::Earth_heliocentric_coordinates (struct Julian_date& J)
{
  
  L_0=L0(J);
  L_1=L1(J);
  L_2=L2(J);
  L_3=L3(J);
  L_4=L4(J);
  L_5=L5(J);
  B_0=B0_coef(J);
  B_1=B1_coef(J);
  
  double L_temp=(L_0+L_1*J.JM+L_2*pow(J.JM,2)+L_3*pow(J.JM,3)+L_4*pow(J.JM,4)+L_5*pow(J.JM,5))/1e8;
  double B_temp=(B_0+B_1*J.JM)/1e8;

  double F=0.5*L_temp/M_PI-floor(0.5*L_temp/M_PI); // fraction of 2*PI

  if (L_temp>0 ) L_rad=2*M_PI*F;
  else L_rad= L_rad=2*M_PI*((double)1-F);

  F=0.5*B_temp/M_PI-floor(0.5*B_temp/M_PI); // fraction of 2*PI

  if (B_temp>0 ) B_rad=2*M_PI*F;
  else B_rad= B_rad=2*M_PI*((double)1-F);


  L_deg=L_rad*180/M_PI;
  B_deg=B_rad*180/M_PI;

  teta_rad=L_rad+M_PI;
  beta_rad=-B_rad;
  teta_deg=teta_rad*180/M_PI;
  beta_deg=beta_rad*180/M_PI;


  alpha_rad=atan2(sin(teta_rad)*cos(epsilon_rad)-tan(beta_rad)*sin(epsilon_rad), cos(teta_rad));

  ni_deg=280.46061837+360.98564736629*(J.JD-2451545)+0.000387933*pow(J.JC,2)-pow(J.JC,3)/((double)38710000);
  F=ni_deg/360-floor(ni_deg/((double)360));

  if (ni_deg>0 ) ni_deg=360*F;
  else ni_deg=360*((double)1-F);

  ni_rad=ni_deg*M_PI/180;


}


struct sun_position
{

double hour_angle_rad; //angolo orario
double hour_angle_deg;

double declination_rad; //declinazione solare
double declination_deg;

double elevation_rad; // elevazione del Sole alle coordinate specificate
double elevation_deg;

double azimuth_rad; //azimuth solare alle coordinate specificate
double azimuth_deg;

sun_position(struct Earth_heliocentric_coordinates&, struct geographic_coord&);

};


sun_position::sun_position(struct Earth_heliocentric_coordinates& helios, struct geographic_coord& geo)
{
  hour_angle_rad=helios.ni_rad+geo.longitude_rad-helios.alpha_rad;
  declination_rad=asin(sin(helios.beta_rad)*cos(helios.epsilon_rad)+cos(helios.beta_rad)*sin(helios.epsilon_rad)*sin(helios.teta_rad));
  elevation_rad=asin(sin(geo.latitude_rad)*sin(declination_rad)+cos(geo.latitude_rad)*cos(declination_rad)*cos(hour_angle_rad));

 azimuth_rad=M_PI+atan2(sin(hour_angle_rad),cos(hour_angle_rad)*sin(geo.latitude_rad)-tan(declination_rad)*cos(geo.latitude_rad));

 hour_angle_deg=hour_angle_rad*180/M_PI;
 declination_deg=declination_rad*180/M_PI;
 elevation_deg=elevation_rad*180/M_PI;
 azimuth_deg=azimuth_rad*180/M_PI;
}

 

double* output_angle(struct UTC_time& DATA, String behaviour,double az_door_deg,double el_door_deg)
{

double *out=new double[2];

struct Julian_date J(DATA);
struct geographic_coord G;

struct Earth_heliocentric_coordinates H(J);
struct sun_position S(H, G);


if(behaviour=="solar_tracker")
{
  out[0]=S.azimuth_deg;
  out[1]=S.elevation_deg;
}

else if(behaviour=="mirror")
{
  double el_door_rad=M_PI*el_door_deg/180;
  double az_door_rad=M_PI*az_door_deg/180;
  double somma_x=cos(el_door_rad)*cos(az_door_rad)+cos(S.elevation_rad)*cos(S.azimuth_rad);
  double somma_y=cos(el_door_rad)*sin(az_door_rad)+cos(S.elevation_rad)*sin(S.azimuth_rad);
  double somma_z=-sin(el_door_rad)-sin(S.elevation_rad);

  double norma=pow( pow(somma_x,2)+pow(somma_y,2)+pow(somma_z,2)   ,0.5);

  somma_x/=norma;
  somma_y/=norma;
  somma_z/=norma;

  out[1]=-asin(somma_z);          //elevation
  out[0]=atan2(somma_y,somma_x);  //azimuth

  if (out[0]<0) out[0]+=2*M_PI;

  out[0]/=M_PI/180;
  out[1]/=M_PI/180;


}
  return out;
}

 

 

void print_date(struct UTC_time& date)
{
  Serial.print(date.dd);
  Serial.print("/");
  Serial.print(date.mm);
  Serial.print("/");
  Serial.print(date.yy);
  Serial.print(" ");
  Serial.print(date.h);
  Serial.print(":");
  Serial.print(date.minute);
  Serial.print(":");
  Serial.print(date.secs);
  Serial.print("\n");
  
}

void print_juliandate(struct Julian_date& J)
{
  Serial.print("J.JD= ");
  Serial.print(J.JD);
  Serial.print("\n");
  
}

void print_solar_pos(struct UTC_time& date, struct sun_position& S)
{
  Serial.print(date.dd);
  Serial.print("/");
  Serial.print(date.mm);
  Serial.print("/");
  Serial.print(date.yy);
  Serial.print(" ");
  Serial.print(date.h);
  Serial.print(":");
  Serial.print(date.minute);
  Serial.print(":");
  Serial.print(date.secs);
  Serial.print("  ");
  Serial.print("Azimuth= ");
  Serial.print(S.azimuth_deg);
  Serial.print(" deg; ");
  Serial.print("Elevation= ");
  Serial.print(S.elevation_deg);
  Serial.print(" deg;");
  Serial.print("\n");
}

 

If you want to be uploaded some of your projects ...just email me or feel free to write on the integrated forum. 

My email is written at the bottom of the page

for information, advice, questions

write to my email or just write in the chat box

not1996@hotmail.it

  • Instagram

If you want to be uploaded some of your projects ...just email me or write in the forum and discuss it

© 2023 by Gamma. Proudly created with Wix.com