This program uses the "PSA" solar positioning algorithm calculating the sun position, based on longitude, latitude, and time zone. Then Mbed chip controls the two digital servos to rotate the solar panel to the correct azimuth and zenith angle.

Dependencies:   4DGL-uLCD-SE AX12 NetServices mbed spxml

Fork of AX12-HelloWorld by Chris Styles

Files at this revision

API Documentation at this revision

Comitter:
conantina
Date:
Mon Apr 25 17:28:13 2016 +0000
Parent:
1:b12b06e2fc2d
Commit message:
n

Changed in this revision

4DGL-uLCD-SE.lib Show annotated file Show diff for this revision Revisions of this file
Helios.cpp Show annotated file Show diff for this revision Revisions of this file
Helios.h Show annotated file Show diff for this revision Revisions of this file
NetServices.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
spxml.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/4DGL-uLCD-SE.lib	Mon Apr 25 17:28:13 2016 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/4180_1/code/4DGL-uLCD-SE/#2cb1845d7681
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helios.cpp	Mon Apr 25 17:28:13 2016 +0000
@@ -0,0 +1,160 @@
+/*
+  Helios.cpp- 
+  Copyright (c) 2011 Hannes Hassler.  All rights reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  
+  
+  This library can be used for calculating the solar position on Arduino.  
+  
+  The algorithm is an adaption from
+  the "PSA" solar positioning algorithm, as documented in:
+  
+  Blanco-Muriel et al.: Computing the Solar Vector. Solar Energy Vol 70 No 5 pp 431-441.
+  http://dx.doi.org/10.1016/S0038-092X(00)00156-0
+  
+  According to the paper, "The algorithm allows .. the true solar vector 
+  to be determined with an accuracy of 0.5
+  minutes of arc for the period 1999–2015.
+  The original code has been downloaded from
+  http://www.psa.es/sdg/sunpos.htm
+  
+  Adaptions:
+  Modified calculation of number of Days since 1.Jan 2000 (dJulianDate-2451545.0)
+  Neccessary because of the limited double precision on Arduino
+  (double has the same precision as float on the current Arduino (2011))
+  It should be used only for dates between 1.1.2000 and 31.12.2100
+  (PSA itself has garantueed accuracy only until 2015)
+  
+*/
+
+#include "Helios.h"
+#include <math.h>
+
+
+Helios::Helios(){}
+
+
+void Helios::calcSunPos(
+        int iYear,
+	int iMonth,
+	int iDay,
+	double dHours,
+	double dMinutes,
+	double dSeconds,
+	double dLongitude,
+	double dLatitude)
+{
+	
+	
+	// Calculate difference in days between the current Julian Day 
+	// and JD 2451545.0, which is noon 1 January 2000 Universal Time
+	{
+		  
+	        //double dJulianDate;
+		//long int liAux1;
+		//long int liAux2;
+		// Calculate time of the day in UT decimal hours
+		dDecimalHours = dHours + (dMinutes 
+			+ dSeconds / 60.0 ) / 60.0;
+			
+		// Calculate current Julian Day
+		/* original calculation; does not work
+		   for Arduino, because double precision is the same 
+		   same as float.
+		liAux1 =(iMonth-14)/12;
+		liAux2=(1461*(iYear + 4800 + liAux1))/4 + (367*(iMonth 
+			- 2-12*liAux1))/12- (3*((iYear + 4900 
+		+ liAux1)/100))/4+iDay-32075;
+		dJulianDate=(double)(liAux2)-0.5+dDecimalHours/24.0;
+		// Calculate difference between current Julian Day and JD 2451545.0 
+		dElapsedJulianDays = dJulianDate-2451545.0;
+		*/
+		
+		long int iYfrom2000=iYear-2000;
+		long int iA=(14-(iMonth))/12;
+		long int iM=(iMonth)+12*iA-3;
+		
+		long int liAux3=(153*iM+2)/5;
+		long int liAux4=365*(iYfrom2000-iA);
+		long int liAux5=(iYfrom2000-iA)/4;
+		
+		
+		dElapsedJulianDays=(double)(iDay+liAux3+liAux4+liAux5+59)+
+		                    -0.5+dDecimalHours/24.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)
+	{
+		
+	        /*double dMeanLongitude;
+		double dMeanAnomaly;
+		double dOmega;*/
+		
+		dOmega=2.1429-0.0010394594*dElapsedJulianDays;
+		dMeanLongitude = 4.8950630+ 0.017202791698*dElapsedJulianDays; // Radians
+		dMeanAnomaly = 6.2400600+ 0.0172019699*dElapsedJulianDays;
+		dEclipticLongitude = dMeanLongitude + 0.03341607*sin( dMeanAnomaly ) 
+			+ 0.00034894*sin( 2*dMeanAnomaly )-0.0001134
+			-0.0000203*sin(dOmega);
+		dEclipticObliquity = 0.4090928 - 6.2140e-9*dElapsedJulianDays
+			+0.0000396*cos(dOmega);
+	}
+
+	// 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)
+	{
+		
+		dSin_EclipticLongitude= sin( dEclipticLongitude );
+		double dY1 = cos( dEclipticObliquity ) * dSin_EclipticLongitude;
+		double dX1 = cos( dEclipticLongitude );
+		dRightAscension = atan2( dY1,dX1 );
+		if( dRightAscension < 0.0 ) dRightAscension = dRightAscension + twopi;
+		dDeclination = asin( sin( dEclipticObliquity )*dSin_EclipticLongitude );
+	}
+
+	// Calculate local coordinates ( azimuth and zenith angle ) in degrees
+	{
+		
+		dGreenwichMeanSiderealTime = 6.6974243242 + 
+			0.0657098283*dElapsedJulianDays 
+			+ dDecimalHours;
+						
+		dLocalMeanSiderealTime = (dGreenwichMeanSiderealTime*15 
+			+ dLongitude)*rad;
+		dHourAngle = dLocalMeanSiderealTime - dRightAscension;
+		dLatitudeInRadians = dLatitude*rad;
+		dCos_Latitude = cos( dLatitudeInRadians );
+		dSin_Latitude = sin( dLatitudeInRadians );
+		dCos_HourAngle= cos( dHourAngle );
+		dZenithAngle = (acos( dCos_Latitude*dCos_HourAngle
+		*cos(dDeclination) + sin( dDeclination )*dSin_Latitude));
+		double dY = -sin( dHourAngle );
+		double dX = tan( dDeclination )*dCos_Latitude - dSin_Latitude*dCos_HourAngle;
+		dAzimuth=atan2( dY, dX );
+		if ( dAzimuth < 0.0 ) 
+			dAzimuth = dAzimuth + twopi;
+		dAzimuth = dAzimuth/rad;		
+		// Parallax Correction		
+		dParallax=(dEarthMeanRadius/dAstronomicalUnit)
+			*sin(dZenithAngle);
+		dZenithAngle=(dZenithAngle 
+			+ dParallax)/rad;
+		dElevation=90-dZenithAngle;
+	}	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helios.h	Mon Apr 25 17:28:13 2016 +0000
@@ -0,0 +1,84 @@
+/*
+  Helios.h - Library for calculating the solar
+  position.
+  Copyright (c) 2011 Hannes Hassler.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  
+*/
+#ifndef Helios_h
+#define Helios_h
+
+
+#include <math.h>
+
+// Declaration of some constants 
+#define pi    3.14159265358979323846
+#define twopi (2*pi)
+#define rad   (pi/180)
+#define dEarthMeanRadius     6371.01	// In km
+#define dAstronomicalUnit    149597890	// In km
+
+
+class Helios
+{
+  public:
+    Helios();        
+    void calcSunPos(
+        int iYear,
+	int iMonth,
+	int iDay,
+	double dHours,
+	double dMinutes,
+	double dSeconds,
+	double dLongitude,
+	double dLatitude);
+        
+	// Main variables
+	double dElapsedJulianDays;
+	double dDecimalHours;
+	double dEclipticLongitude;
+	double dEclipticObliquity;
+	double dRightAscension;
+	double dDeclination;
+
+	// Auxiliary variables
+	double dSin_EclipticLongitude;
+	
+	
+	double dMeanLongitude;
+	double dMeanAnomaly;
+	double dOmega;
+	
+	double dGreenwichMeanSiderealTime;
+	double dLocalMeanSiderealTime;
+	double dLongitude;
+	double dLatitudeInRadians;
+	double dHourAngle;
+	double dCos_Latitude;
+	double dSin_Latitude;
+	double dCos_HourAngle;
+	double dParallax;
+	double dAzimuth;
+        double dZenithAngle;
+        double dElevation;
+	
+ 
+   
+};
+
+
+
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NetServices.lib	Mon Apr 25 17:28:13 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/segundo/code/NetServices/#966a0265edfc
--- a/main.cpp	Thu Mar 31 12:03:04 2011 +0000
+++ b/main.cpp	Mon Apr 25 17:28:13 2016 +0000
@@ -1,14 +1,198 @@
+/*
+This Mbed code is a demo code of a DIY dual-axis solar tracker
+Since the solar position is different from place to place, the variables: YourLongitude and YourLatitude needs to be changed based on
+user's location
+The PSA solar positioning algorithm use UTC. There may be a time difference. The variable time_difference should be calibrated as well.
+The calibration can be done by calculated the longitute difference between the user's loaction and Greenwich. 30 degree is 0.25 hours.
+The futher calibration can be done by compare the PSA results with the solar position GUI results, for example:
+http://www.esrl.noaa.gov/gmd/grad/solcalc/azel.html
+The real time needs to be set manually at the beginning of main function
+*/
+
 #include "mbed.h"
 #include "AX12.h"
+#include "Helios.h"
+#include "EthernetNetIf.h"
+#include "HTTPClient.h"
+#include "spdomparser.hpp"
+#include "spxmlnode.hpp"
+#include "spxmlhandle.hpp"
+#include <string>
+#include "uLCD_4DGL.h"
+
+uLCD_4DGL lcd(p9,p10,p11); // serial tx, serial rx, reset pin;
+EthernetNetIf eth; 
+HTTPClient http;
+ 
+HTTPResult result;
+bool completed = false;
+void request_callback(HTTPResult r)
+{
+  result = r;
+  completed = true;
+}
+ 
+
+AX12 Hax12 (p13, p14, 1);
+AX12 Vax12 (p28, p27, 1);
+//uLCD_4DGL lcd(p28, p27, p29);
+Serial pc(USBTX,USBRX);
+
+Helios helios;
+
+/////////// TEMPORARY TEST VARIABLES //////////////////////
+int TheYear  = 2016;
+int TheMonth = 4;
+int TheDay   = 9;
+double TheHour = 12;            /*  UTC TIME!  */
+double TheMinute = 0.00;
+double TheSeconds = 0.00;
+double YourLongitude = 84.39;     // your longitude [e.g 151.857964];
+double YourLatitude  = 33.4;     // your latitude  [e.g -33.579265];
+////// LIVE VARIABLES SHOULD BE USED FROM A GPS //////////
+
+
+//real_time variable
+struct tm *t;
+float time_difference = 6.1;       //calibrated time difference of Atlanta
+
+//update the solar position and roate the panel accordingly with the time interval of update_period
+float update_period = 0.5*60;    //10 minutes by default
+
+//HTTP variables
+int n = 0;
+string delimiter = "weather";
+string delimiter2 = ",";
+string delimiter3 = ":";
+string place;
+string weather;
+string condition;
+
+void get_sun_position(){
+        helios.calcSunPos(TheYear, TheMonth, TheDay, TheHour, TheMinute, TheSeconds, YourLongitude, YourLatitude);
+        pc.printf("Sun Zenith Angle: %f\n",helios.dZenithAngle);    // Degrees down from vertical
+        pc.printf("Sun Azimuth Angle: %f\n",helios.dAzimuth);       // Degrees from north
+        pc.printf("Sun Elevation Angle: %f\n",helios.dElevation);   // Degrees up from horizontal
+}
+
+
+//rotate the horizontal angle to get the proper azimuth angle
+void rotate_horizontal(float angle){
+    Hax12.SetGoal(angle);
+}
+
+//rotate the vertical angle to get the proper zenith angle, the zero of the servo points vertical
+void rotate_vertical(float angle){
+    Vax12.SetGoal(angle+60); 
+}
+
 
 int main() {
-
-    AX12 myax12 (p9, p10, 1);
+    // setup time structure for Wed, 28 Oct 2016 3:12:00
+    struct tm mytime;
+    mytime.tm_sec = 00;    // 0-59
+    mytime.tm_min = 50;    // 0-59
+    mytime.tm_hour = 16;   // 0-23
+    mytime.tm_mday = 28;   // 1-31
+    mytime.tm_mon = 3;     // 0-11
+    mytime.tm_year = 116;  // year since 1900
 
-    while (1) {
-        myax12.SetGoal(0);    // go to 0 degrees
-        wait (2.0);
-        myax12.SetGoal(300);  // go to 300 degrees
-        wait (2.0);
+    time_t seconds = mktime(&mytime);
+    set_time(seconds);
+    
+    lcd.cls();
+    lcd.printf("Start\n");
+ 
+    lcd.printf("Setting up...\n");
+    EthernetErr ethErr = eth.setup(100000);
+    if(ethErr)
+    {
+     lcd.printf("Error %d in setup.\n", ethErr);
+     return -1;
     }
-}
\ No newline at end of file
+    lcd.printf("Setup OK\n");
+  
+    HTTPStream stream;
+    SP_XmlDomParser parser;
+   
+    char BigBuf[512 + 1] = {0};
+    stream.readNext((byte*)BigBuf, 512); //Point to buffer for the first read
+    int i = 0;
+    char buffer [512*5+1];
+    while(true){
+         HTTPResult r = http.get("http://openweathermap.org/data/2.1/find/city?lat=33.75&lon=-84.39&cnt=1&type=XML", &stream, request_callback); //Load a very large page, such as the hackaday RSS feed
+         //http://openweathermap.org/data/2.1/find/city?lat=33.75&lon=-84.39&cnt=1&type=XML
+         //http://wxdata.weather.com/wxdata/weather/local/USGA0028:1:US?cc=*&unit=m&dayf=1
+         //HTTP://hackaday.com/feed/
+         //http://openweathermap.org/data/2.1/find/city?lat=40.71&lon=-74.00&cnt=1&type=XML
+         i = 0;
+    
+        while(!completed)
+        {
+            Net::poll(); //Polls the Networking stack
+            if(stream.readable())
+            {
+                i++;
+                BigBuf[stream.readLen()] = 0; //Transform this buffer in a zero-terminated char* string
+                parser.append( BigBuf, strlen(BigBuf)); // stream current buffer data to the XML parser
+                if (i == 1){  
+                sprintf(buffer,"%s",BigBuf);
+                 } else{
+                printf("%s",BigBuf); //Display it while loading
+                }
+             //Note: some servers do not like if you throttle them too much, so printf'ing during a request is generally bad practice
+                stream.readNext((byte*)BigBuf, 512); //Buffer has been read, now we can put more data in it
+            }
+        }
+        //lcd.printf("\n--------------\n");
+        n = sizeof(buffer);
+        string ret(buffer, n);
+  
+        place = ret.substr(ret.find("name"),ret.length());
+        place = place.substr(place.find(delimiter3),place.find(delimiter2)-4);
+        //lcd.printf("Location:%s\n", place);
+  
+        weather = ret.substr(ret.find(delimiter), ret.length());
+        weather.erase(0,weather.find(delimiter3)+1);
+        condition = weather.substr(weather.find("main"),weather.length());
+        condition = condition.substr(condition.find(delimiter3),place.find(delimiter2)-2);
+        //lcd.printf("weather condition%s\n",condition);
+        
+        while (true) {
+            time_t local_time = time(NULL); 
+            lcd.cls();           
+            lcd.printf("The Current Time is: %s\n", ctime(&local_time)); 
+            pc.printf("The Current Time is: %s\n", ctime(&local_time)); 
+            lcd.printf("\n--------------\n"); 
+            lcd.printf("Location:%s\n", place);
+            lcd.printf("weather condition%s\n",condition);
+            pc.printf("weather condition%s\n",condition);
+            t = localtime(&local_time);
+            TheYear  = t->tm_year + 1900;
+            TheMonth = t->tm_mon;
+            //change the local time to UTC TIME according to longitude
+            if(t->tm_hour - time_difference < 0){
+                TheDay   = t->tm_mday -1;
+                TheHour = t->tm_hour + 24 - time_difference; 
+            } else{
+                TheDay   = t->tm_mday;
+                TheHour = t->tm_hour - time_difference; 
+            }           
+            TheMinute = t->tm_min;
+            TheSeconds = t->tm_sec; 
+            get_sun_position();
+            pc.printf("%d\n",condition.find("Clear"));
+            if( helios.dZenithAngle<90 && (condition.find("Clear")!=-1)){  
+                rotate_horizontal(helios.dAzimuth);
+                rotate_vertical(helios.dZenithAngle);
+                lcd.printf("Sun Zenith Angle: %f\n",helios.dZenithAngle);    // Degrees down from vertical
+                lcd.printf("Sun Azimuth Angle: %f\n",helios.dAzimuth);       // Degrees from north
+            }else{
+                lcd.printf("The solar tracker is not tracking the sun...");
+            }            
+            wait(update_period); 
+            if(t->tm_hour == 6) break;                 
+        }
+    }
+     
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml.lib	Mon Apr 25 17:28:13 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/hlipka/code/spxml/#3fa97f2c0505