Arduino solar tracker

The remark about wear and tear on the LDRs out in the sun's damaging rays, and all weathers set me thinking...

a) In ANYTHING you design that will be outdoors, design it for easy replacement of damaged parts. They WILL need replacing... sooner than you hope!

b) Are LDRs the way to go? Probably... but I wondered if maybe temperature sensors embedded in small blocks of black epoxy might do just as well, and be more robust?

c) Of course, while I would be the first to applaud anyone wanting to have fun creating a solar tracker... how cool! (I've wanted to build one for years).... if I just wanted my solar panels oriented correctly, I would put my faith in Mr Newton. The geometries of the earth's relationship to the sun are entirely predictable, and a program can be written to point the panels in the right direction without any light sensors. Sorry to be a bore. Do remember on BIG advantage of the track-by-light-sensor approach, though: It doesn't need an accurately set RTC (time of day clock) to work.

d) A KISS point: You really only need the expense, complexity, etc, etc needed to "tip" the panels left and right around one axis of rotation day by day.... to track the sun across the sky. You may want to mount everything on a second axis of rotation to compensate for the sun's height above the horizon at noon, to compensate for the seasonal variation in that axis... but this adjustment can be done manually with whatever you want to use during your routine servicing of the panels. The rate of change in this axis is slow.

I too have been thinking of how to track the sun to gather that extra 30% of energy, but the cost of most commercial solutions has exceeded the cost of installing 30% more solar modules...

I'm very new to the world of Arduino, and from the few forum threads on the subject, the popular method seems to track light levels to determine the position of "Ol' Sol" visually.
The downside I see with this methodology is that it is difficult to allow for all the variations and possible scenarios that the solar array may face, and have to algorithmically respond to.
For example, will the tracker (in the examples given) properly handle if a thunderstorm blows in mid-day ? In other words, all of a sudden there is no bright spot in the sky; what will it do ? I suppose it will stay put, because the two or three light sensors will "see" the same light level, am I right ?
Next, what happens at sundown ? How does it know whan to go back towards the east and wait for sunrise ? Without a sense of time (ie, with an RTC) I'm not convinced it will find the sun in the morning, mostly because the sun will literally be behind the light sensors.

With respect to razorbud's and tkbyd's comments about using lat/long information to determine azimuth and elevation of the sun in real time, I think this is a great idea. It solves a lot of problems in terms of where the sun is, regardless of time or weather. It's not the simplest (no KISS here) but

The big question is: has anyone done this, to calculate the sun's azimuth ?

I've read and looked at ve9qrp's qrptracker project, and how he uses keplerian elements and Plan13 formulas. I'm hoping someone has some information to share on how to do this, to save from re-inventing any wheels.

Thanks to all in advance for any assistance...

Dann.

I think the best simplest solution is to utilize a simple RTC/Calendar to drive a stepper motor gear drive at a constant sidereal rate for a single axis right ascension movement.

If you research telescope clock drives using polar mounts you will see that it's pretty simple, accurate and reliable. You may need to manual adjust the azimuth angle manually every month or so to optimize performance. The RTC/Calendar could also handle the skewing command to reposition to each sunrise from look up table in your program.

Mowcius has the answer!

http://www.mowcius.co.uk/suntrackercode.html

Sun tracker code for you :slight_smile:
Attach to a RTC and tell it your co-ordinates and it'll throw out sun position :wink:

Mowcius

Great thread guys! Mowcius -- I read the info on your personal web site for the sun position tracking and was salivating, but only to find the links to the .pde's are broken! Are you still providing this sample code?

Yeah, annoying aint it.

Hopefully moving my website soon and sorting it out.

Anyway, here's some static code

//Sun Position Calculation
//Provides sun position (relative) from static variables

#include <math.h>
#define pi    3.14159265358979323846
#define twopi (2*pi)
#define rad   (pi/180)
#define EarthMeanRadius     6371.01      // In km
#define AstronomicalUnit    149597890      // In km

//Input Variables --------------------- TIME HAS TO BE IN UT (UNIVERSAL TIME)! NO TIME ZONES OR SUMMER TIMES --------
//My last modifications were probably at this time on this date!
      int Year = 2010; //year
      int Month = 7; //month
      int Day = 3; //day
      float Hours = 16; //hour
      float Minutes = 38; //minutes

      float Longitude = 1.2967; //enter longitude here
      float Latitude = 1.5465; //enter latitude here
//--------

//Program Variables
      float ZenithAngle;
      float Azimuth;
        float RightAscension;
      float Declination;
        float Parallax;
        float ElevationAngle;

      float ElapsedJulianDays;
      float DecimalHours;
      float EclipticLongitude;
      float EclipticObliquity;
//--------

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

void sunPos(){


      // Auxiliary variables
      float dY;
      float dX;

      // Calculate difference in days between the current Julian Day
      // and JD 2451545.0, which is noon 1 January 2000 Universal Time

            float JulianDate;
            long int liAux1;
            long int liAux2;
            // Calculate time of the day in UT decimal hours
            DecimalHours = Hours + (Minutes / 60.0);
            // Calculate current Julian Day
            liAux1 =(Month-14)/12;
            liAux2=(1461*(Year + 4800 + liAux1))/4 + (367*(Month
                  - 2-12*liAux1))/12- (3*((Year + 4900
            + liAux1)/100))/4+Day-32075;
            JulianDate=(float)(liAux2)-0.5+DecimalHours/24.0;
            // Calculate difference between current Julian Day and JD 2451545.0
            ElapsedJulianDays = JulianDate-2451545.0;

      // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
      // ecliptic in radians but without limiting the angle to be less than 2*Pi
      // (i.e., the result may be greater than 2*Pi)

            float MeanLongitude;
            float MeanAnomaly;
            float Omega;
            Omega=2.1429-0.0010394594*ElapsedJulianDays;
            MeanLongitude = 4.8950630+ 0.017202791698*ElapsedJulianDays; // Radians
            MeanAnomaly = 6.2400600+ 0.0172019699*ElapsedJulianDays;
            EclipticLongitude = MeanLongitude + 0.03341607*sin( MeanAnomaly )
                  + 0.00034894*sin( 2*MeanAnomaly )-0.0001134
                  -0.0000203*sin(Omega);
            EclipticObliquity = 0.4090928 - 6.2140e-9*ElapsedJulianDays
                  +0.0000396*cos(Omega);

      // Calculate celestial coordinates ( right ascension and declination ) in radians
      // but without limiting the angle to be less than 2*Pi (i.e., the result may be
      // greater than 2*Pi)

            float Sin_EclipticLongitude;
            Sin_EclipticLongitude= sin( EclipticLongitude );
            dY = cos( EclipticObliquity ) * Sin_EclipticLongitude;
            dX = cos( EclipticLongitude );
            RightAscension = atan2( dY,dX );
            if( RightAscension < 0.0 ) RightAscension = RightAscension + twopi;
            Declination = asin( sin( EclipticObliquity )*Sin_EclipticLongitude );

      // Calculate local coordinates ( azimuth and zenith angle ) in degrees

            float GreenwichMeanSiderealTime;
            float LocalMeanSiderealTime;
            float LatitudeInRadians;
            float HourAngle;
            float Cos_Latitude;
            float Sin_Latitude;
            float Cos_HourAngle;
            GreenwichMeanSiderealTime = 6.6974243242 +
                  0.0657098283*ElapsedJulianDays
                  + DecimalHours;
            LocalMeanSiderealTime = (GreenwichMeanSiderealTime*15
                  + Longitude)*rad;
            HourAngle = LocalMeanSiderealTime - RightAscension;
            LatitudeInRadians = Latitude*rad;
            Cos_Latitude = cos( LatitudeInRadians );
            Sin_Latitude = sin( LatitudeInRadians );
            Cos_HourAngle= cos( HourAngle );
            ZenithAngle = (acos( Cos_Latitude*Cos_HourAngle
                  *cos(Declination) + sin( Declination )*Sin_Latitude));
            dY = -sin( HourAngle );
            dX = tan( Declination )*Cos_Latitude - Sin_Latitude*Cos_HourAngle;
            Azimuth = atan2( dY, dX );
            if ( Azimuth < 0.0 )
              Azimuth = Azimuth + twopi;
            Azimuth = Azimuth/rad;
            // Parallax Correction
            Parallax=(EarthMeanRadius/AstronomicalUnit)
                  *sin(ZenithAngle);
            ZenithAngle=(ZenithAngle //Zenith angle is from the top of the visible sky (thanks breaksbassbleeps)
                  + Parallax)/rad;
                ElevationAngle = (90-ZenithAngle); //Retrieve useful elevation angle from Zenith angle
}

void loop(){
  sunPos(); //Run sun position calculations
  Serial.print("Elevation Angle:  ");
  Serial.println(ElevationAngle, 0); //Print Elevation (Vertical) with no decimal places as accuracy is not really great enough
  Serial.print("Azimuth:  ");
  Serial.println(Azimuth, 0); //Print Azimuth (Horizontal) with no decimal places
  if(ElevationAngle < 0)
  Serial.println("The sun has set. Get some sleep!");
  while(1){} //Stop - Values aren't going to have changed anyway as they are currently static variables!
}

Should work fine.

Mowcius

Thanks Mowcius, this is great!
I have what is probably a really uneducated, lame question: It seems to get this to work in my area, all I need is to swap in my lat/long, is that correct? If so, I tried that, and I must be doing this wrong:

In my area, (Redmond, OR, USA), my decimal lat/long comes up as:
Latitude: 44.2727778
Longitude: -121.1727778

When plugging that in, I see the serial port reads a negative elevation angle, and azimuth 239, and says 'The sun has set...."

Can you point me in the right direction?
I feel guilty asking someone who as written such detailed complex code, for something like this :frowning:

EDIT:: Sorry!! Disregard. I had lat/long switched in the code. I guess I was right that it was a lame question :slight_smile: Thanks again for this fantastic code!

I feel guilty asking someone who as written such detailed complex code, for something like this

EDIT:: Sorry!! Disregard. I had lat/long switched in the code. I guess I was right that it was a lame question Thanks again for this fantastic code!

I feel slightly guilty every time someone writes something like that as I merely took it and modified it slightly for the arduino.

Yeah I think it'd confusing how it is written with the lat and long. People always seem to stick them in the wrong way round :stuck_out_tongue:

Mowcius

Have you done any further work with integrating this with a GPS for the clock and lat/long? (I saw you talking about this in another thread by chance, and figured the two projects were related)

I integrated it with a RTC (DS1307) for the time. I don't have a GPS at the moment so I can't do it with that.

It's easy enough :slight_smile:

I'd love to use a compass and a GPS some time to make an automatic system that will track the sun anywhere without any values being programmed in.

Something I am going to using it for in the next month is for part of a lamp I am doing. Because I know when the sun has set, I can adjust lighting accordingly in the evening. Then there's no chance of sticking something on top of an LDR by accident and making the light turn on in the middle of the day when it's still light :slight_smile:

Mowcius

Hi Mowcius

It seems that a lot of people using your sun tracker code.
Your code is great! Many thanks!!

I am working still on my solar concentrating power system.
The full code for it is done, at the moment I am waiting for some mechanical parts from italy.
With these parts I would be able to finish my project till spring.

I tested your code with a libelium gps module and it works everywhere!

Please visit my website under http://www.andres.li/
The website is written in german...under "Steuerung" you can see how arduino is working with the gps module.

schwizer

I tested your code with a libelium gps module and it works everywhere!

Great!

Cool project, glad it works OK with the GPS. I presume you are converting the output from the GPS to decimal or is it outputting in decimal?

Hey Mowcius,
I ordered a RTC kit, and I'm wondering if you wouldn't mind posting your updated tracker code that includes your RTC?

I also ordered and Ethernet shield, and I'll be working on doing HTTP post to SQL via PHP scripts, so my data is logged properly, so I will make sure and contribute my code once I get to that point.

Last night I welded up a solar panel 2-axis jig, so I can begin a sanity check of the hardware/software once my RTC arrives.

Umm, if I knew where I put it then I would :smiley:

It's simple enough, I presume your RTC will be a DS1307, there are a few libraries around, easiest is probably DS1307.h then add some of this to your sketch:

#include <Wprogram.h>
#include <Wire.h>
#include <DS1307.h>

int rtc[7];

void setup()
{
  RTC.start();
}

void loop()
{
  RTC.get(rtc,true);

  for(int i=0; i<7; i++)
  {
    if (rtc[0] < 10 && i==0){ Serial.print("0");}
    Serial.print(rtc[i]);
    Serial.print(" ");
  }

  Serial.println();

  delay(1000);

}

From the output of that sketch you will be able to work out what rtc[0] to rtc[6] are then you can assign them to variables.

You will also need a sketch to set the time on the RTC, there are a few of them around too.

Call RTC.get(rtc, true) at the start of the sun position calculation, then use rtc as the variables.
I'll see if I can find my code again, if not then I'll whip this back up into a sketch tonight - might add in the time set over serial code too.
Mowcius

I built a simple little light tracker for my controls class, I made a video of it at the request of the professor. Its not nearly as complex as what you guys are planning, but it shows what you can do with a couple of photo-resistors.

It doesn't actually record the lights position, though this could be remedied by attaching a pot. A 2-axis system would just be everything here except duplicated and turned 90 degrees.

I have been working hard on my solar tracking system this week, and have made great progress. All of the sun position calculations are based on Mowcius' code.
This uses a 20x4 HD44780 LCD for the display, and the AdaFruit motor driver shield (AFMotor.h library) to drive the two stepper motors. I built in logic to park the solar panels at night as well. I am a total hack programmer on my best day, so I greatly value any feedback or ways to tighten up the code. The last two remaining steps, once I receive the hardware (should be here this weekend) are to integrate the DS1307 RTC so I do not have to manually plug in UTC time each time I reset the Arduino. The next big step once my Ethernet shield arrives, will be writing the Ele and Azimuth to SQL, via HTTP send and PHP scripts, which I haven't even touched yet, but seen others have done it.

Here's my full code:
http://bishoptechnology.com/pub/SunTracker.pde

My AdaFruit DS1307RTC arrived this weekend, so I built that and got it integrated into my code. Since I'm not using any of my analog ports for this particular project, I am able to use their suggestion of plugging it straight in to the Arduino's analog ports, and just set pin 17 HIGH and pin 16 LOW to power it. Below is a link to my current code, and I also have a second sketch that syncs the RTC to your PC's current time (of course you must set your PC to UTC, and in my case to offset the delay of compile time and upload time, I advance my PC clock 23 seconds, so it lands exactly on official UTC time when it writes to the RTC). Hope this helps! I should have my AdaFruit motor shield and my two stepper motors next week, so I can begin doing a sanity check on all of this madness :slight_smile:

http://www.bishoptechnology.com/pub/SunTracker.pde

http://www.bishoptechnology.com/pub/RTC_Clock_Sync_Util.pde

Eventually I will eliminate the separate clock sync sketch and just have a line in the main sketch that you can uncomment to sync the RTC. I just did this because the AdaFruit library in the example on her site drove me nuts (not because it's bad, but because I'm not a developer), so I ended up using the DS1307RTC.h and Time.h libs instead.

Hi guys, I am continuing to work hard on the Solar Tracker, and I need some senior level programming help at this point. I have been working with some guys in another thread to get my ENC28J60-based ethernet shield working, and that is all good now, but now I am at a cross-roads with the sunPos() Function donated by Mowcius. I am writing data to a php script via the "unofficial" ethernet shield, in order to log to a SQL database. The problem is, I can write data to php all day long, but as soon as I compile the sketch to tell it to call to the sunPos() function, and do the sun position calculations, it breaks. My feeling is this is some sort of memory overflow, due to the heavy duty calculations being performed by that function. What can I do to troubleshoot and fix this?
http://bishoptechnology.com/pub/SunTrackerSQL.pde is my sketch for reference.

Ok - Not completely sure of what the issue will be but I also suspect it is a RAM issue.

Some things you can make smaller:

Do all of these need to be ints? Can they be bytes?

        int ElevInt = 0;
        int AziInt = 0;
        int ElevFrac = 0;
        int AziFrac = 0;
        int AziStep = 0;
        int ElevStep = 0;
        int AziPrevious = 0;
        int ElevPrevious = 0;
        int AziOneDegree = 10;   // Enter number of steps to physically move your rig 1 degree Azimuth.
        int ElevOneDegree = 20;   // Enter number of steps to physically move your rig 1 degree Elevation.

I would also move a load of these into the sunPos() function as they are only needed in that function:

      float ZenithAngle;
      float Azimuth;
      float RightAscension;
      float Declination;
      float Parallax;
      float ElevationAngle;
      float ElapsedJulianDays;
      float DecimalHours;
      float EclipticLongitude;
      float EclipticObliquity;

See how that goes to start with.

Mowcius

I actually sorted this out just a few minutes ago: sunPos() is actually not the problem, the issue was any reference to delay() was breaking my ethernet shield's /GET to my php script. I'm not sure what the deal is with that. I also noticed if I had anything in the loop() after the php script, it would fail, so I either moved everything before it, or in some cases broke them out into another Function. At this point everything is working 100% normal.

My last step before getting this thing functional and outside, is to complete the brackets for the two stepper motors on the tracking rig, and also when I get home tonight, verify that my stepper motors are actually running as the code says it is :slight_smile: