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
main.cpp@2:2a3493799f03, 2016-04-25 (annotated)
- Committer:
- conantina
- Date:
- Mon Apr 25 17:28:13 2016 +0000
- Revision:
- 2:2a3493799f03
- Parent:
- 1:b12b06e2fc2d
n
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
conantina | 2:2a3493799f03 | 1 | /* |
conantina | 2:2a3493799f03 | 2 | This Mbed code is a demo code of a DIY dual-axis solar tracker |
conantina | 2:2a3493799f03 | 3 | Since the solar position is different from place to place, the variables: YourLongitude and YourLatitude needs to be changed based on |
conantina | 2:2a3493799f03 | 4 | user's location |
conantina | 2:2a3493799f03 | 5 | The PSA solar positioning algorithm use UTC. There may be a time difference. The variable time_difference should be calibrated as well. |
conantina | 2:2a3493799f03 | 6 | The calibration can be done by calculated the longitute difference between the user's loaction and Greenwich. 30 degree is 0.25 hours. |
conantina | 2:2a3493799f03 | 7 | The futher calibration can be done by compare the PSA results with the solar position GUI results, for example: |
conantina | 2:2a3493799f03 | 8 | http://www.esrl.noaa.gov/gmd/grad/solcalc/azel.html |
conantina | 2:2a3493799f03 | 9 | The real time needs to be set manually at the beginning of main function |
conantina | 2:2a3493799f03 | 10 | */ |
conantina | 2:2a3493799f03 | 11 | |
chris | 0:f6f8cf11779f | 12 | #include "mbed.h" |
chris | 0:f6f8cf11779f | 13 | #include "AX12.h" |
conantina | 2:2a3493799f03 | 14 | #include "Helios.h" |
conantina | 2:2a3493799f03 | 15 | #include "EthernetNetIf.h" |
conantina | 2:2a3493799f03 | 16 | #include "HTTPClient.h" |
conantina | 2:2a3493799f03 | 17 | #include "spdomparser.hpp" |
conantina | 2:2a3493799f03 | 18 | #include "spxmlnode.hpp" |
conantina | 2:2a3493799f03 | 19 | #include "spxmlhandle.hpp" |
conantina | 2:2a3493799f03 | 20 | #include <string> |
conantina | 2:2a3493799f03 | 21 | #include "uLCD_4DGL.h" |
conantina | 2:2a3493799f03 | 22 | |
conantina | 2:2a3493799f03 | 23 | uLCD_4DGL lcd(p9,p10,p11); // serial tx, serial rx, reset pin; |
conantina | 2:2a3493799f03 | 24 | EthernetNetIf eth; |
conantina | 2:2a3493799f03 | 25 | HTTPClient http; |
conantina | 2:2a3493799f03 | 26 | |
conantina | 2:2a3493799f03 | 27 | HTTPResult result; |
conantina | 2:2a3493799f03 | 28 | bool completed = false; |
conantina | 2:2a3493799f03 | 29 | void request_callback(HTTPResult r) |
conantina | 2:2a3493799f03 | 30 | { |
conantina | 2:2a3493799f03 | 31 | result = r; |
conantina | 2:2a3493799f03 | 32 | completed = true; |
conantina | 2:2a3493799f03 | 33 | } |
conantina | 2:2a3493799f03 | 34 | |
conantina | 2:2a3493799f03 | 35 | |
conantina | 2:2a3493799f03 | 36 | AX12 Hax12 (p13, p14, 1); |
conantina | 2:2a3493799f03 | 37 | AX12 Vax12 (p28, p27, 1); |
conantina | 2:2a3493799f03 | 38 | //uLCD_4DGL lcd(p28, p27, p29); |
conantina | 2:2a3493799f03 | 39 | Serial pc(USBTX,USBRX); |
conantina | 2:2a3493799f03 | 40 | |
conantina | 2:2a3493799f03 | 41 | Helios helios; |
conantina | 2:2a3493799f03 | 42 | |
conantina | 2:2a3493799f03 | 43 | /////////// TEMPORARY TEST VARIABLES ////////////////////// |
conantina | 2:2a3493799f03 | 44 | int TheYear = 2016; |
conantina | 2:2a3493799f03 | 45 | int TheMonth = 4; |
conantina | 2:2a3493799f03 | 46 | int TheDay = 9; |
conantina | 2:2a3493799f03 | 47 | double TheHour = 12; /* UTC TIME! */ |
conantina | 2:2a3493799f03 | 48 | double TheMinute = 0.00; |
conantina | 2:2a3493799f03 | 49 | double TheSeconds = 0.00; |
conantina | 2:2a3493799f03 | 50 | double YourLongitude = 84.39; // your longitude [e.g 151.857964]; |
conantina | 2:2a3493799f03 | 51 | double YourLatitude = 33.4; // your latitude [e.g -33.579265]; |
conantina | 2:2a3493799f03 | 52 | ////// LIVE VARIABLES SHOULD BE USED FROM A GPS ////////// |
conantina | 2:2a3493799f03 | 53 | |
conantina | 2:2a3493799f03 | 54 | |
conantina | 2:2a3493799f03 | 55 | //real_time variable |
conantina | 2:2a3493799f03 | 56 | struct tm *t; |
conantina | 2:2a3493799f03 | 57 | float time_difference = 6.1; //calibrated time difference of Atlanta |
conantina | 2:2a3493799f03 | 58 | |
conantina | 2:2a3493799f03 | 59 | //update the solar position and roate the panel accordingly with the time interval of update_period |
conantina | 2:2a3493799f03 | 60 | float update_period = 0.5*60; //10 minutes by default |
conantina | 2:2a3493799f03 | 61 | |
conantina | 2:2a3493799f03 | 62 | //HTTP variables |
conantina | 2:2a3493799f03 | 63 | int n = 0; |
conantina | 2:2a3493799f03 | 64 | string delimiter = "weather"; |
conantina | 2:2a3493799f03 | 65 | string delimiter2 = ","; |
conantina | 2:2a3493799f03 | 66 | string delimiter3 = ":"; |
conantina | 2:2a3493799f03 | 67 | string place; |
conantina | 2:2a3493799f03 | 68 | string weather; |
conantina | 2:2a3493799f03 | 69 | string condition; |
conantina | 2:2a3493799f03 | 70 | |
conantina | 2:2a3493799f03 | 71 | void get_sun_position(){ |
conantina | 2:2a3493799f03 | 72 | helios.calcSunPos(TheYear, TheMonth, TheDay, TheHour, TheMinute, TheSeconds, YourLongitude, YourLatitude); |
conantina | 2:2a3493799f03 | 73 | pc.printf("Sun Zenith Angle: %f\n",helios.dZenithAngle); // Degrees down from vertical |
conantina | 2:2a3493799f03 | 74 | pc.printf("Sun Azimuth Angle: %f\n",helios.dAzimuth); // Degrees from north |
conantina | 2:2a3493799f03 | 75 | pc.printf("Sun Elevation Angle: %f\n",helios.dElevation); // Degrees up from horizontal |
conantina | 2:2a3493799f03 | 76 | } |
conantina | 2:2a3493799f03 | 77 | |
conantina | 2:2a3493799f03 | 78 | |
conantina | 2:2a3493799f03 | 79 | //rotate the horizontal angle to get the proper azimuth angle |
conantina | 2:2a3493799f03 | 80 | void rotate_horizontal(float angle){ |
conantina | 2:2a3493799f03 | 81 | Hax12.SetGoal(angle); |
conantina | 2:2a3493799f03 | 82 | } |
conantina | 2:2a3493799f03 | 83 | |
conantina | 2:2a3493799f03 | 84 | //rotate the vertical angle to get the proper zenith angle, the zero of the servo points vertical |
conantina | 2:2a3493799f03 | 85 | void rotate_vertical(float angle){ |
conantina | 2:2a3493799f03 | 86 | Vax12.SetGoal(angle+60); |
conantina | 2:2a3493799f03 | 87 | } |
conantina | 2:2a3493799f03 | 88 | |
chris | 0:f6f8cf11779f | 89 | |
chris | 0:f6f8cf11779f | 90 | int main() { |
conantina | 2:2a3493799f03 | 91 | // setup time structure for Wed, 28 Oct 2016 3:12:00 |
conantina | 2:2a3493799f03 | 92 | struct tm mytime; |
conantina | 2:2a3493799f03 | 93 | mytime.tm_sec = 00; // 0-59 |
conantina | 2:2a3493799f03 | 94 | mytime.tm_min = 50; // 0-59 |
conantina | 2:2a3493799f03 | 95 | mytime.tm_hour = 16; // 0-23 |
conantina | 2:2a3493799f03 | 96 | mytime.tm_mday = 28; // 1-31 |
conantina | 2:2a3493799f03 | 97 | mytime.tm_mon = 3; // 0-11 |
conantina | 2:2a3493799f03 | 98 | mytime.tm_year = 116; // year since 1900 |
chris | 1:b12b06e2fc2d | 99 | |
conantina | 2:2a3493799f03 | 100 | time_t seconds = mktime(&mytime); |
conantina | 2:2a3493799f03 | 101 | set_time(seconds); |
conantina | 2:2a3493799f03 | 102 | |
conantina | 2:2a3493799f03 | 103 | lcd.cls(); |
conantina | 2:2a3493799f03 | 104 | lcd.printf("Start\n"); |
conantina | 2:2a3493799f03 | 105 | |
conantina | 2:2a3493799f03 | 106 | lcd.printf("Setting up...\n"); |
conantina | 2:2a3493799f03 | 107 | EthernetErr ethErr = eth.setup(100000); |
conantina | 2:2a3493799f03 | 108 | if(ethErr) |
conantina | 2:2a3493799f03 | 109 | { |
conantina | 2:2a3493799f03 | 110 | lcd.printf("Error %d in setup.\n", ethErr); |
conantina | 2:2a3493799f03 | 111 | return -1; |
chris | 0:f6f8cf11779f | 112 | } |
conantina | 2:2a3493799f03 | 113 | lcd.printf("Setup OK\n"); |
conantina | 2:2a3493799f03 | 114 | |
conantina | 2:2a3493799f03 | 115 | HTTPStream stream; |
conantina | 2:2a3493799f03 | 116 | SP_XmlDomParser parser; |
conantina | 2:2a3493799f03 | 117 | |
conantina | 2:2a3493799f03 | 118 | char BigBuf[512 + 1] = {0}; |
conantina | 2:2a3493799f03 | 119 | stream.readNext((byte*)BigBuf, 512); //Point to buffer for the first read |
conantina | 2:2a3493799f03 | 120 | int i = 0; |
conantina | 2:2a3493799f03 | 121 | char buffer [512*5+1]; |
conantina | 2:2a3493799f03 | 122 | while(true){ |
conantina | 2:2a3493799f03 | 123 | 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 |
conantina | 2:2a3493799f03 | 124 | //http://openweathermap.org/data/2.1/find/city?lat=33.75&lon=-84.39&cnt=1&type=XML |
conantina | 2:2a3493799f03 | 125 | //http://wxdata.weather.com/wxdata/weather/local/USGA0028:1:US?cc=*&unit=m&dayf=1 |
conantina | 2:2a3493799f03 | 126 | //HTTP://hackaday.com/feed/ |
conantina | 2:2a3493799f03 | 127 | //http://openweathermap.org/data/2.1/find/city?lat=40.71&lon=-74.00&cnt=1&type=XML |
conantina | 2:2a3493799f03 | 128 | i = 0; |
conantina | 2:2a3493799f03 | 129 | |
conantina | 2:2a3493799f03 | 130 | while(!completed) |
conantina | 2:2a3493799f03 | 131 | { |
conantina | 2:2a3493799f03 | 132 | Net::poll(); //Polls the Networking stack |
conantina | 2:2a3493799f03 | 133 | if(stream.readable()) |
conantina | 2:2a3493799f03 | 134 | { |
conantina | 2:2a3493799f03 | 135 | i++; |
conantina | 2:2a3493799f03 | 136 | BigBuf[stream.readLen()] = 0; //Transform this buffer in a zero-terminated char* string |
conantina | 2:2a3493799f03 | 137 | parser.append( BigBuf, strlen(BigBuf)); // stream current buffer data to the XML parser |
conantina | 2:2a3493799f03 | 138 | if (i == 1){ |
conantina | 2:2a3493799f03 | 139 | sprintf(buffer,"%s",BigBuf); |
conantina | 2:2a3493799f03 | 140 | } else{ |
conantina | 2:2a3493799f03 | 141 | printf("%s",BigBuf); //Display it while loading |
conantina | 2:2a3493799f03 | 142 | } |
conantina | 2:2a3493799f03 | 143 | //Note: some servers do not like if you throttle them too much, so printf'ing during a request is generally bad practice |
conantina | 2:2a3493799f03 | 144 | stream.readNext((byte*)BigBuf, 512); //Buffer has been read, now we can put more data in it |
conantina | 2:2a3493799f03 | 145 | } |
conantina | 2:2a3493799f03 | 146 | } |
conantina | 2:2a3493799f03 | 147 | //lcd.printf("\n--------------\n"); |
conantina | 2:2a3493799f03 | 148 | n = sizeof(buffer); |
conantina | 2:2a3493799f03 | 149 | string ret(buffer, n); |
conantina | 2:2a3493799f03 | 150 | |
conantina | 2:2a3493799f03 | 151 | place = ret.substr(ret.find("name"),ret.length()); |
conantina | 2:2a3493799f03 | 152 | place = place.substr(place.find(delimiter3),place.find(delimiter2)-4); |
conantina | 2:2a3493799f03 | 153 | //lcd.printf("Location:%s\n", place); |
conantina | 2:2a3493799f03 | 154 | |
conantina | 2:2a3493799f03 | 155 | weather = ret.substr(ret.find(delimiter), ret.length()); |
conantina | 2:2a3493799f03 | 156 | weather.erase(0,weather.find(delimiter3)+1); |
conantina | 2:2a3493799f03 | 157 | condition = weather.substr(weather.find("main"),weather.length()); |
conantina | 2:2a3493799f03 | 158 | condition = condition.substr(condition.find(delimiter3),place.find(delimiter2)-2); |
conantina | 2:2a3493799f03 | 159 | //lcd.printf("weather condition%s\n",condition); |
conantina | 2:2a3493799f03 | 160 | |
conantina | 2:2a3493799f03 | 161 | while (true) { |
conantina | 2:2a3493799f03 | 162 | time_t local_time = time(NULL); |
conantina | 2:2a3493799f03 | 163 | lcd.cls(); |
conantina | 2:2a3493799f03 | 164 | lcd.printf("The Current Time is: %s\n", ctime(&local_time)); |
conantina | 2:2a3493799f03 | 165 | pc.printf("The Current Time is: %s\n", ctime(&local_time)); |
conantina | 2:2a3493799f03 | 166 | lcd.printf("\n--------------\n"); |
conantina | 2:2a3493799f03 | 167 | lcd.printf("Location:%s\n", place); |
conantina | 2:2a3493799f03 | 168 | lcd.printf("weather condition%s\n",condition); |
conantina | 2:2a3493799f03 | 169 | pc.printf("weather condition%s\n",condition); |
conantina | 2:2a3493799f03 | 170 | t = localtime(&local_time); |
conantina | 2:2a3493799f03 | 171 | TheYear = t->tm_year + 1900; |
conantina | 2:2a3493799f03 | 172 | TheMonth = t->tm_mon; |
conantina | 2:2a3493799f03 | 173 | //change the local time to UTC TIME according to longitude |
conantina | 2:2a3493799f03 | 174 | if(t->tm_hour - time_difference < 0){ |
conantina | 2:2a3493799f03 | 175 | TheDay = t->tm_mday -1; |
conantina | 2:2a3493799f03 | 176 | TheHour = t->tm_hour + 24 - time_difference; |
conantina | 2:2a3493799f03 | 177 | } else{ |
conantina | 2:2a3493799f03 | 178 | TheDay = t->tm_mday; |
conantina | 2:2a3493799f03 | 179 | TheHour = t->tm_hour - time_difference; |
conantina | 2:2a3493799f03 | 180 | } |
conantina | 2:2a3493799f03 | 181 | TheMinute = t->tm_min; |
conantina | 2:2a3493799f03 | 182 | TheSeconds = t->tm_sec; |
conantina | 2:2a3493799f03 | 183 | get_sun_position(); |
conantina | 2:2a3493799f03 | 184 | pc.printf("%d\n",condition.find("Clear")); |
conantina | 2:2a3493799f03 | 185 | if( helios.dZenithAngle<90 && (condition.find("Clear")!=-1)){ |
conantina | 2:2a3493799f03 | 186 | rotate_horizontal(helios.dAzimuth); |
conantina | 2:2a3493799f03 | 187 | rotate_vertical(helios.dZenithAngle); |
conantina | 2:2a3493799f03 | 188 | lcd.printf("Sun Zenith Angle: %f\n",helios.dZenithAngle); // Degrees down from vertical |
conantina | 2:2a3493799f03 | 189 | lcd.printf("Sun Azimuth Angle: %f\n",helios.dAzimuth); // Degrees from north |
conantina | 2:2a3493799f03 | 190 | }else{ |
conantina | 2:2a3493799f03 | 191 | lcd.printf("The solar tracker is not tracking the sun..."); |
conantina | 2:2a3493799f03 | 192 | } |
conantina | 2:2a3493799f03 | 193 | wait(update_period); |
conantina | 2:2a3493799f03 | 194 | if(t->tm_hour == 6) break; |
conantina | 2:2a3493799f03 | 195 | } |
conantina | 2:2a3493799f03 | 196 | } |
conantina | 2:2a3493799f03 | 197 | |
conantina | 2:2a3493799f03 | 198 | } |