Solar Tracking

To get to the point, I want to control a linear actuator with an Arduino+RTC.
This tracker has been up for 6 years. The orginal photo eye failed after a year or so, so I made one from an uno, mosfet board, and automotive relays. This worked well but the photo resistors don't last long, died in 18 months. Next version I used photo diodes, this worked about the same and died in the same amount of time. Clouds would constantly make it move away from where is should be and when the clouds would pass it was often in a position the photo eye couldn't see it.

I want to build something based on time or gps.
It can be as crude as moved to position 1 at sunrise (but this changes), move position 2 at 8am and proceed incrementally through the day finishing at position 10 at sunset.

I don't code well. I can eventually figure it out using examples others have created. This is my current approach, its been 7 days and I am on page 1. Now I am here asking for your help.

GPS gives you time + position. And any search engine will give you the maths if you search for "calculate sun position".

Instead of trying to code, try pseudocoding. Do not worry about the syntax. Since you are outdoors and time is your approach, GPS makes complete sense. I am assuming you have a range of motion you want to sweep across based on the sun's sweep across the same anchor. That requires a start time and an end time. Do you want those times to vary based on the reported date? Do you want to track the same time regardless of season? My first stab at the pseudocode follows

// include libraries
const int longestDayStart;
const int longestDayEnd;
const int shortestDayStart;
const int shortestDayEnd;
const int sweepStart;
const int sweepEnd;
int startTime() {
//calculate time to start sweep based on date, longestDayStart and shortestDayStart
}
int endTime() {
//calculate time to end based on date, longestDayEnd and shortestDayEnd
}
int sweepDuration() {
//calculate total time based on startTime and endTime
}
int sweepPerMinute() {
//calculate each step given the range of motion, and sweepDuration() {
}
//GPS Object
//Time Objects
void setup(){
//setup timers
//setup I/O
//setupSerial
}
void loop(){
  pollSerial();
  syncTime();
  motionControl();
}
void pollSerial(){
// encode gps object
}
void syncTime(){
// synchronize time/date with gps
}
void motionControl() {
//make it happen
}

TurboMiles:
Clouds would constantly make it move away from where is should be and when the clouds would pass it was often in a position the photo eye couldn't see it.

That suggests the system is too reactive. There is probably no need to correct the position more often than once every 5 minutes and even then only by a small amount. I can't imagine how the system could move so far that it would lose sight of the sun. One of the sensors should be able to see it even if it is facing 80° out of position.

...R

try this example from the Ephemeris library. it has all the other astronomical info removed. In its original form it is . too big for anything less than a mega:

/*
 * derived from ephemeris_full.ino
 * 
 * Copyright (c) 2017 by Sebastien MARCHAND (Web:www.marscaper.com, Email:sebastien@marscaper.com)
 */
    
#include <Ephemeris.h>  // https://github.com/MarScaper/ephemeris
#include <TimeLib.h>
#include <DS1307RTC.h>

void printDateAndTime(int day, int month, int year, int hour, int minute, int second ) ////////////////////////////////////////////////////
{
  Serial.print(day);    Serial.print("/"); Serial.print(month); Serial.print("/");
  Serial.print(year);   Serial.print(" "); Serial.print(hour);  Serial.print(":");
  Serial.print(minute); Serial.print(":"); Serial.print(second);
} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void equatorialCoordinatesToString(EquatorialCoordinates coord, char raCoord[14] , char decCoord[14])
{
  int raHour,raMinute;
  float raSecond;
  Ephemeris::floatingHoursToHoursMinutesSeconds(coord.ra, &raHour, &raMinute, &raSecond);
    
  sprintf(raCoord," %02dh%02dm%02ds.%02d",raHour,raMinute,(int)raSecond,(int)round(((float)(raSecond-(int)raSecond)*pow(10,2))));
    
  int decDegree,decMinute;
  float decSecond;
  Ephemeris::floatingDegreesToDegreesMinutesSeconds(coord.dec, &decDegree, &decMinute, &decSecond);
    
  if(decDegree<0)
  {
    sprintf(decCoord,"%02dd%02d'%02d\".%02d",(int)decDegree,decMinute,(int)decSecond,(int)round(((float)(decSecond-(int)decSecond)*pow(10,2))));
  }
  else
  {
    sprintf(decCoord," %02dd%02d'%02d\".%02d",(int)decDegree,decMinute,(int)decSecond,(int)round(((float)(decSecond-(int)decSecond)*pow(10,2))));
  }
}

void printEquatorialCoordinates(EquatorialCoordinates coord) ////////////////////////////////////////////////////////////////////////////
{
  if( isnan(coord.ra) ||  isnan(coord.dec))
  {
    // Do not work for Earth of course...
    Serial.println("R.A: -");
    Serial.println("Dec: -");
        
    return;
  } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
  char raCoord[14];
  char decCoord[14];
  equatorialCoordinatesToString(coord,raCoord,decCoord);

  Serial.print(" R.A: ");
  Serial.println(raCoord);

  Serial.print(" Dec: ");
  Serial.println(decCoord);

  return;
} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void printHorizontalCoordinates(HorizontalCoordinates coord) /////////////////////////////////////////////////////////////////////////////
{
  if( isnan(coord.azi) ||  isnan(coord.alt))
  {
    // Do not work for Earth of course...
    Serial.println("  Az: -");
    Serial.println("  El: -");
        
    return;
  }

  Serial.print("  Az:  ");
  Serial.print(coord.azi,2);
  Serial.println("d");

  Serial.print("  El:  ");
  Serial.print(coord.alt,2);
  Serial.println("d");
} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void printSolarSystemObjects(int day, int month, int year, int hour, int minute, int second) ///////////////////////////////////////////////
{
  Serial.println("-----------------------------");
  printPlanet("Sun",          Sun,     day, month, year, hour, minute, second); // do not remove the spaces
}

void printPlanet( const char *solarSystemObjectName, SolarSystemObjectIndex index, int day, int month, int year, int hour, int minute, int second )
{
  SolarSystemObject solarSystemObject = Ephemeris::solarSystemObjectAtDateAndTime(index, day, month, year, hour, minute, second);
  
  Serial.println(solarSystemObjectName);
  printEquatorialCoordinates(solarSystemObject.equaCoordinates);
  printHorizontalCoordinates(solarSystemObject.horiCoordinates);

  if( solarSystemObject.riseAndSetState == RiseAndSetOk )
  {
    int hour,minute;
    float second;
    
    Ephemeris::floatingHoursToHoursMinutesSeconds(solarSystemObject.rise, &hour, &minute, &second);
    Serial.print("Rise: ");
    Serial.print(hour);
    Serial.print(":");
    Serial.print(minute);
    Serial.print(":");
    Serial.print(second);
    Serial.println(" UTC");

    Ephemeris::floatingHoursToHoursMinutesSeconds(solarSystemObject.set, &hour, &minute, &second);
    Serial.print(" Set:  ");
    Serial.print(hour);
    Serial.print(":");
    Serial.print(minute);
    Serial.print(":");
    Serial.print(second);
    Serial.println(" UTC");
  }
}

void setup() 
{
  Serial.begin(9600);
  // Set location on earth for horizontal coordinates transformations
  Ephemeris::setLocationOnEarth( 45,06,12,  // Lat: 45°06'12"
                               -96,34,06); // Lon: -96°34'06"

  // West is negative and East is positive
  Ephemeris::flipLongitude(false);
    
  // Set altitude to improve rise and set precision
  Ephemeris::setAltitude(1395); //elevation in meters
                                
  // Choose a date and time UTC Time not local /////////////////////////////////////////////////////////////////////////////////////////
  int day=6,month=13,year=2019,hour=15,minute=45,second=0;
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  // Compute and print solar system objects
  Serial.print("SolarTracker: (");
  printDateAndTime(day,month,year,hour,minute,second);
  Serial.println(")");
  printSolarSystemObjects(day, month, year, hour, minute, second);
}

void loop() {}

I have a version that uses a W8BH clock that compiles, but I can't get Time library time elements into the Ephemeris code. That is about 18K

If I were building a tracker, I would make an equatorial mount so only one axis (East / West) would have to be rotated 15 degrees per hour during the day and the declination axis (North / South) would need adjusting about every two weeks.

The solar position (altitude and azimuth) can be accurately calculated anywhere on the Earth, for any time of day and date, using this simple Arduino library: GitHub - KenWillmott/SolarPosition: Arduino Library to calculate the position of the sun relative to geographic coordinates.

This method is never confused by bad weather.

the problem is, a wise maker wants to pre position the array at sunset, for the next day. this requires a sunrise calculator. that is why I bypassed the Solar Position calculator and tried Ephemeris, which led me to a brick wall too.

the problem with using an equatorial mount is all that weight at an angle, increasing friction. with an Az El mount, you just turn in a circle with the weight vertical. a linear actuator can handle the elevation.

Minimal Arduino sunrise and sunset calculator: GitHub - dmkishi/Dusk2Dawn: Minimal Arduino library for time of sunrise and sunset.

It has a minor bug, though. Replace this:

  timeUTC = sunriseSetUTC(isRise, jday, _latitude, _longitude);

  // Advance the calculated time by a fraction of itself. I've no idea what the
  // purpose of this is.
  newJday    = jday + timeUTC / (60 * 24);
  newTimeUTC = sunriseSetUTC(isRise, newJday, _latitude, _longitude);

with this:

  newTimeUTC = sunriseSetUTC(isRise, jday, _latitude, _longitude);

Why?

Can't you just program it to go to the sunrise position about an hour after it gets really dark? I'm only suggesting the hour wait to avoid false moves.

And I can't see why an accurate sunrise position would be needed if the system was programmed to move more quickly to point at the sun when it first appears after the long dark period that marks a night. The sunrise position would just need to be sufficient to be sure the sensors would detect the early morning sun.

...R

my philosophy is to move it while the batteries are fresh. if the system gets drawn down, it's in the right place when the sun comes up again.

It won't be good for the batteries if they are discharged so low that they cannot move the solar panel.

The electricity is free but the batteries are expensive. Treat them kindly.

...R

Took a bit of trial and error but I got this one GitHub - KenWillmott/SolarPosition: Arduino Library to calculate the position of the sun relative to geographic coordinates working with an RTC. Well I say working, I doesn't track yet but the begining of the code it working, keeping time and update elevation and az. I will work a little more on it tomorrow to see if I can get the data to pull a pin LOW.

Thanks everyone for writing something. This will be the best control system this tracker has ever had on it. I know it will work beyond the time of all the photo eyes.

I think that your photo eyes needed filters (sunglasses). I wonder if pyro-electric sensors would be workable?

What latitude are you on?

GoForSmoke:
I think that your photo eyes needed filters (sunglasses). I wonder if pyro-electric sensors would be workable?

Another thought - use a servo (or anything else suitable) to move a blind across the sensors except for the short periods when sensing must take place.

...R

Solar trackers using light sensors only work on sunny days with no cloud cover.
Clouds cause diffuse light which will completely confuse a tracker which works by using sensors.
Jremingtons idea of of simply using time of day and latitude is by far the best option.

mauried:
Clouds cause diffuse light which will completely confuse a tracker which works by using sensors.

For best results the solar panels should be aimed at the brightest part of the sky - even if that is just where the brightest clouds are.

But it's not too difficult to program the motion so it does not go a long distance (or any distance) backwards and that should mean it is ready for when the clouds clear.

...R

mauried:
Solar trackers using light sensors only work on sunny days with no cloud cover.
Clouds cause diffuse light which will completely confuse a tracker which works by using sensors.
Jremingtons idea of of simply using time of day and latitude is by far the best option.

I have a Mars Confectionery factory in my town, they have 2 x 4Kw solar arrays that are sun tracking.
They are not confused when cloudy, they just point to the area of the sky that is the brightest, which is where the most solar energy is.

For best results the solar panels should be aimed at the brightest part of the sky - even if that is just where the brightest clouds are.

Exactly.
Tom... :slight_smile:

i done this project...

the circuit has following components like

  1. ldr

  2. solar pannnel

  3. arduino

  4. stepper motor

depending upon light intensity ldr resistance changes.. due to that values write the code to rotate the

motor in sun direction

Please note that "brightst spot in the sky" is not necessarily "spot where most solar energy can be harvested"