Home made Pyranometer or Irradiance/Insolation Meter
The idea behind making a Pyranometer is to add another measurement to my weather station. It is basically a light meter but it can also tell me what the cloud cover was on any given day. I'm hoping to combine this data with temperature and rainfall readings to see if I can find correlations between plant growth and fruit yields. Just something to do really!
Today was the first day my prototype was put into action, see previous post, and I recorded, automatically, a reading of how much power from the sun was reaching the earth every minute. Technically if using a PV Solar Panel I should have put it into short circuit but I haven't done this since I have no way of calibrating my device. By loading the panel to the maximum power point, which is close to the short circuit point, I at least have the manufacturers Standard Test Condition ratings to calibrate my readings with.
To cut a long story short the results were graphed from 8:30am to just past 6:30pm and are below. What the readings can tell me is what the cloud cover was and whether the clouds were thick and low or high cirrus clouds. Today was a good day for the trial run...
Click image for larger version
The Y axis is Watts per Metre Squared and the X access is minutes since 8:30am.
The dip just after the 100 minute mark is when the telegraph pole cast a shadow onto the solar panel and 10 minutes later the jagged points rising were the shadows of the power lines. The rest are clouds.
The panel is on a 66 degree roof (I couldn't find a horizontal place to mount the panel near a power point) and is a few degrees off of south. The results are reasonably accurate, perfect for what I need. You can clearly see that first thing there were very few clouds, as is the case around 4pm and from 5pm onwards. The highest peaks are full sun. Today was one that you would class as not a bad summers day, a few too many clouds perhaps but a standard late summers day.
Had there been no clouds the graph would have looked similar to this image I nicked from http://www.mpoweruk.com/solar_power.htm
Anyone interested in the C++ program I knocked up it is here (not brilliant but functional and the formatting has gone wrong when I pasted it here):
// The Raspberry Pi is connected to an MCP3004 Analogue
// to Digital Converter. That is connected to a Solar
// Panel with a load to produce max power. The Voltage
// is divided down to max of 3.3 and then read.
// Calculations are made to work out power being generated
// and Insolation (irradiance) falling onto the panel
// It is calibrated by the manufacturers STC values on
// the panel, ie, 5 Watts at 17.9 volts, .280A when
// Irradiance is 1000 W/m2
/////////////
// OUTPUTS //
/////////////
//
// date time,interval in minutes,power in watts from test panel
// ,watts per sq metre,watts generated with 16 standard panels
// ,calculated insolation,voltage of test panel,current in test panel,status (0=ok,1=no reading)
//
// yyyy-mm-dd hh:mm:ss,int,float,float,float,float,float,float,int
// Used to access GPIO
#include <wiringPi.h>
// Used to read ADC
#include <mcp3004.h>
// Used by setprecision
#include <iomanip>
// Used for reading the time
#include <ctime>
// Used for making strings
#include <string>
// Used by number to string conversion
#include <sstream>
// Used by file writing
#include <fstream>
// Used by mkdir()
#include <sys/stat.h>
// Used by shutdown
#include <stdlib.h>
// Used by WiringPi to access ADC MCP3004
#define BASE 100
#define SPI_CHAN 0
using namespace std;
// Test last write time
int check_clock_update()
{
long int solartime = 0;
ifstream in("solartime.txt", ios::in);
if(!in)
{
// no file
return(1);
}
in >> solartime;
in.close();
time_t t = time(NULL);
// Give the time 5 minutes because Pi may take some while to shutedown and record time
// This may be lowered to what ever time it takes Pi to shutdown properly
// If Pi power is cut then fake clock time will be upto 1 hr behind
if (t > (solartime+300)) return(solartime); else return(0);
};
// save last write time
void record_time(long int t)
{
ofstream out("solartime.txt", ios::out);
if(out)
{
out << t;
out.flush();
out.close();
}
};
// convert any number to string
template <typename T>
string NumberToString ( T Number )
{
ostringstream ss;
ss << Number;
return ss.str();
};
// Make time string YYYY-MM-DD HH:MM:SS for recording the date time of reading
string time_str()
{
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
string s;
string year = NumberToString(now->tm_year + 1900);
string month = NumberToString(now->tm_mon + 1);
if (now->tm_mon+1 < 10) month = "0" + month;
string day = NumberToString(now->tm_mday);
if (now->tm_mday < 10) day = "0" + day;
string hour = NumberToString(now->tm_hour);
if (now->tm_hour < 10) hour = "0" + hour;
string min = NumberToString(now->tm_min);
if (now->tm_min < 10) min = "0" + min;
string sec = NumberToString(now->tm_sec);
if (now->tm_sec < 10) sec = "0" + sec;
s = year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
return(s);
};
// For 1st folder name
string time_year_str()
{
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
string year;
year = NumberToString(now->tm_year + 1900);
return(year);
};
// For 2nd folder name
string time_year_month_str()
{
// Get time
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
// Build up string YYYY-MM
string s;
string year = NumberToString(now->tm_year + 1900);
string month = NumberToString(now->tm_mon + 1);
if (now->tm_mon+1 < 10) month = "0" + month;
s = year + "-" + month;
return(s);
};
// For filename
string time_year_month_day_str()
{
// Get time
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
// Build up string in a formatted way
// YYYY-MM-DD
string s;
string year = NumberToString(now->tm_year + 1900);
string month = NumberToString(now->tm_mon + 1);
if (now->tm_mon+1 < 10) month = "0" + month;
string day = NumberToString(now->tm_mday);
if (now->tm_mday < 10) day = "0" + day;
s = year + "-" + month + "-" + day;
return(s);
};
int main()
{
// General Variables
float x, chan, irradiance, panels ;
float voltage, current, power, metre, resistor;
long int time_target = 0;
int read = 0, status, interval, fail_count = 0, start = 0;
int writes = 0;
// Interval between samples
interval = 60;
string ts;
// Don't start unless the time is greater than our last reading
// plus 5 minutes. Since the Pi doesn't have RTC we may have to wait
// for the clock to be updated.The Pi fake clock gets written on shutdown
// so we may power up with the last known good time.
// If no file continue, it may be the first run
// If last write time is before now, wait
// if last time time is after now continue
long int check_time = 0;
while (check_time == 0)
{
check_time = check_clock_update();
}
// Handy little library to access various chips via Raspberry Pi
wiringPiSetup() ;
// This is the ADC chip on the LinkSprite.com Shield.
mcp3004Setup (BASE, SPI_CHAN) ; // 3004 and 3008 are the same 4/8 channels
chan = 0;
// Just keep repeating until power fail or cntl-c etc
while(1)
{
time_t t = time(NULL);
// Do we take a reading?
if (time_target <= t) read = 1;
if (read)
{
// read ADC on linksprite.com rpi shield
x = analogRead (BASE + chan);
// If we got a value then do calculations
if (x > 0)
{
// We have loaded the solar panel with 8 Watt resistor
resistor = 63.93;
// Voltage from panel is divided using resistors to produce
// Max 3.3v - this is 6.79 times smaller than the real voltage
// combine this with the ADC output to find correct voltage
// that the panel is generating
voltage = (3.3/1023)*(x*6.79);
// When we know Voltage we use Ohms Law to find current
current = voltage / resistor;
// Now we can work out power of our little panel
power = voltage * current;
// Scale it up dimension wise to 1 sq metre
// Our panel is 27.135 times smaller than 1 sq metre
metre = 27.135*power;
// 16 panels of roughly 1.5 sq metres each
panels = (24 * metre);
// Our little panel is 5.012 Watts when irradiance = 1000 Watts
// So we scale up
irradiance = 199.52*power;
// What's the time of our reading?
ts = time_str();
// Good status
status = 0;
start = 1;
fail_count = 0;
}
// If we didn't get a value then zero everything, set status and time
else
{
// Don't know if we ever get here but if we do
// We still want to record the fact it failed
voltage = 0;
current = 0;
power = 0;
metre = 0;
ts = time_str();
panels = 0;
irradiance = 0;
// Bad status
status = 1;
fail_count++;
}
}
// If we attempted a reading save it providing we have had
// at least one good reading. Only start recording once we
// know there is enough data. Saves SD Card Writes.
if (read && start == 1)
{
// Create pathname in same format as our other weather station
string top = "/home/pi/SolarPanelPower/irradiance";
// If directory isn't there make it otherwise silently fail
mkdir(top.c_str(), 0777);
string pathname, path;
pathname = time_year_str();
pathname = top + "/" + pathname;
// write 2nd directory of year
mkdir(pathname.c_str(), 0777);
path = time_year_month_str();
path = pathname + "/" + path;
// 3rd directory is year + month
mkdir(path.c_str(), 0777);
// Filename is year-month-day.txt
string filename = path + "/" + time_year_month_day_str() + ".txt";
ofstream ofile(filename.c_str(), ios::app);
// Interval between readings in seconds needs converting to minutes
// to match weather station
int temp = interval / 60;
if ( ofile )
{
// write to file
ofile << ts << setprecision(2) << fixed << "," << temp << "," << power << "," << metre << "," << panels << "," << irradiance << "," << voltage << "," << current << "," << status << endl;;
ofile.flush();
ofile.close();
}
// We have finished this reading
read = 0;
// increase write count
writes++;
// Reset counter interval
time_target = t + interval;
// write last sample time every 10 samples
if (writes == 10)
{
record_time(t);
writes = 0;
}
// Sleep for 90% of interval time to save CPU
// CPU on Raspberry PI is flat out with this program which uses
// a lot of power. Well over 90% of time is spent running around
// in a loop checking the time to see if the interval time has
// been reached. Not necessary so do nothing for most of the time.
// We could do nothing for 99% but play it safe!
sleep((int)interval*0.9);
// If we fail to get 10 readings in a row shutdown - fault or too dark
// for a reading. Save SD Card Writes.
if (fail_count >= 10 && start == 1)
{
record_time(t);
// wait for any flushing to finish - maybe not needed
sleep(60);
system("sudo shutdown -h now");
}
}
}
// All done, return to OS
return 0;
}