Here comes the sun

Our intelligence and our technology have given us the power to affect the climate. How will we use this power? Are we willing to tolerate ignorance and complacency in matters that affect the entire human family? Do we value short-term advantages above the welfare of the Earth? Or will we think on longer time scales, with concern for our children and our grandchildren, to understand and protect the complex life-support systems of our planet? The Earth is a tiny and fragile world. It needs to be cherished.

– Carl Sagan (1980)

By spending ~$25 and about 15 hours of research all issues that were previously blocking me from being able to calculate exactly where the sun would be on a given day and time have been completely resolved.

So let’s take a look at the mess that this triple-sized breadboard has become:

You’ll see a few new things on this board.  To the left, attached to the Arduino Mega 2560 is a memory expansion breakout board.  This ties directly in to what is unfortunately 40 pins of the board.  The good news is that 16 of them can be disabled and are accessible via headers on the expansion board.  Doing so disables half of the additional memory the board provides, so it must be used cautiously.  More on that later.  I haven’t soldered female headers into this yet as I don’t suspect I’ll need them.  Any additional sensor requirements can be driven from the available ADC pins.  Anything else can be handled through shift registers if necessary.

On the center right of the board, you’ll notice a button battery on a breakout PCB.  This is a real time clock module.  The battery serves as a method by which the 32.768MHz crystal can oscillate and maintain time when power is not supplied by the microcontroller (MCU).  It streams the time to the MCU and allows time accurate to +/- 1 sec/year to be used in all calculations without any external time source.

To the right of the LCD screen there are two other breakouts.  The right one is simply a 5-direction switch which will be used to navigate settings menus on the LCD screen once those are coded, so it isn’t being utilized at all.  The left one is a 3-axis accelerometer, which provides angle information relative to the earth.  This is also not wired at the moment, but will be shortly.  It will be used to measure the angle of the solar panel as it is actuated based upon the calculations the MCU provides.  Again, more on this in a bit.

MegaRAM Breakout

This is the breakout board purchased from [Rugged Circuits].  There are two chips on this circuit.  The right chip is a controller that manages communication between the RAM and the MCU.  The left chip is the actual RAM. It contains 128Kb of additional memory that is fully integrated into the MCU’s address tables.  Mostly.  Because the ATMega2560 uses 16 bit addresses, only the first 64Kb of memory is immediately addressable.  This includes the 8K of onboard memory as well.  By doing nothing but plugging this board in and setting a pin high to power it, 56Kb of additional memory is gained.  This leaves 72K unused.  Through the onboard controller and some manipulation of timing as well as very careful programming, the 72Kb can be accessed separately, broken out into several separate memory banks.  It’s a pain in the ass and unnecessary at the moment.  It also eliminates the possibility of using the 16 breakout pins.  Because I don’t need it at the moment and I would have to rework a great amount of code I’ve left the total memory at 64Kb (including the base 8K).  If I need more it’s there but for now it’s just hanging out laughing at the other RAM as it’s working its ass off.  What a dick.

RTC Module

This is the RTC module that’s been touched on a bit already.  It has a two wire I2C serial interface with the MCU.  I2C is an interesting architecture because it allows 128 separate modules to be strung together through a 6-bit addressing scheme using only a data wire and a clock wire.  Well worth the sacrifice of two pins.  Also, these are two-way communication pins.  The time is settable and readable with some very simple wiring and commands.  This module, including shipping, cost a little over $5.  Why did I spend so much?  Because one excellent feature of this unit is that in addition to being able to manage accurate time through the year 2100, it also has 32Kb of EEPROM that can be read/written.  This will be very useful for saving state information relevant to battery charge cycles, sun tracking progress, etc should the unit lose power or be put into a low power standby during its cycle.  It can also help save some basic logging/debugging information as the system is built.  Before I’m done I’m sure I’ll put an SD card into the mix for some more interesting logging, but for now this will be a very nice to have.

Triple-Axis Accelerometer

The module on the left is a triple-axis accelerometer (created by Virtuabotix [awesome people to deal with]).

This project only really requires two axis, so the X and Y will be the only ones wired.  But a bonus feature of this board is that it has a low-power mode that puts it to sleep when the MCU doesn’t need it.  Another interesting feature is free-fall detection.  If the board ever experiences zero G, it can set a free-fall pin to high in ~2ms.  Again, not needed for this project but probably pretty handy as a “hey by the way this whole thing you spent a lot of time on is hurtling towards the Earth at the exact speed Newton said it would…” alert.

Now to the maths…

Here are three shots which are simply values cycled through the LCD:

These are the 12 output values currently being calculated.  You’ll note the voltage, cell temperature, and power loss due to heat, which have been here for a while now.  Below those is the current date and time provided by the RTC.  This is what the rest of the calculations mean:

Solar T: This is the solar time.  It is the time that the photons currently hitting the earth left the sun, adjusted for the earth’s position in orbit, angle relative to its orbit around the sun and the sun’s wobble as it orbits the galaxy.  It’s also off by an additional hour because the sun doesn’t care about daylight savings time.

Local T: Local time, the current time at the MCU’s current place.  This is the same as the time provided by the RTC.

Sunrise/Sunset:  The sunrise and sunset times for the current day (adjusted for altitude, DST)

Declination: This is a value that fluctuates annually between 23.45°N at the Summer Solstice and 23.45°S at the Winter Solstice.  It is displayed here only as information as this value is already encapsulated in other values that are the ones we’re most interested in.

Altitude (Angle): This is how high in the sky the sun appears to be relative to the southern azimuth.  This will be a critical value in the final positioning but is also encompassed in another measurement.

Azimuth (Angle): This is how far from the southern azimuth the sun would appear to be if it were at ground level.  This is also critical, but also encompassed in other calculations.

Hour Angle: This is the angle of the sun from -90° to 90° as it moves across the sky throughout the day.  It is at -90° at sunrise and 90° at sunset.  Also a very critical value.

The position of the sun in the sky can be reflected with the hour angle serving as the X axis and the azimuth serving as the Y axis, inverted 90° out of phase.

The accelerometer will provide feedback on the panel’s position regardless of whether or not the panel’s mount is level on the ground.  This saves having to worry about the panel’s X-axis being perfectly aligned.  The Y-axis, however, can be an issue in that the panel’s mount must be perfectly aligned with the azimuth.  Also, the current latitude, longitude and altitude (relative to sea level) values are hard coded.  By adding GPS and compass modules, these values could also be computed, making the system entirely autonomous when it comes to seeking and harvesting light.

Time to build the mount.

And to throw in another Carl Sagan quote to complete the posting sandwich.

The neurochemistry of the brain is astonishingly busy, the circuitry of a machine more wonderful than any devised by humans. But there is no evidence that its functioning is due to anything more than the 1014 neural connections that build an elegant architecture of consciousness.

– Carl Sagan (1980)

Shave and a haircut (and native floating point support): 32 bits

Since the voting regarding which angle of attack to use on tackling the out of memory problem resulted in a tie I decided to take a half-of-each approach.  The code would be optimized as much as possible, but instead of dumping more RAM into the mix, I would set up a second microcontroller as a math co-processor.

I had a few right here to chose from.  Two PIC chips, two ATMega644P’s, a Sanguinololu (which another project already had dibs on unless I was guaranteed it could do the job, and two Parallax Propeller P8X32A QuickStart boards.

The Parallax Propeller is an interesting board.  It’s a 32-bit 8-core processor that supports development in C, C++, SPIN, and ASM.  Each core operates at a user-defined speed ranging from 20kHz to 80MHz (the k in kHz wasn’t a typo).  The 32-bit architecture also lent itself nicely to the floating-point arithmetic needed by the solar position calculations.  It also has 32kb of program memory and 32kb of RAM.

Here’s a picture of it as if that helps at all:

So I programmed it up and TADA! It was out of memory.  Because the fucker only allocates 4k per core and there’s nothing you can do to get at the rest.

Fuck.  So I bought a RAM upgrade for the original microcontroller.

$20 lighter.

This is the pre-optimized code in C++ for a 32 bit compiler if anyone has any ideas:

#include <Arduino.h>
#include "SolarPosition.h"
#include <Time.h>
#include <HardwareSerial.h>
int inputMonth;
 int inputDate;
 int inputYear;
 int inputHour;
 int inputMinute;
 int inputAMPM;
 double degreesToRadians = 3.1416 / 180.0;
 double radiansToDegrees = 180.0 / 3.1416;
 double feetToMeters = 1.0 / 3.28;
 double solarMinutesAfterMidnight;
SolarPosition::SolarPosition(){
 degreesToRadians = 3.1416 / 180.0000;
 radiansToDegrees = 180.0000 / 3.1416;
 feetToMeters = 1.0000 / 3.2800;
}
 double SolarPosition::getOutputEOT(){
 return outputEOT;
 }
 double SolarPosition::getSolarMinutesAfterMidnight(){
 return solarMinutesAfterMidnight;
 }
String SolarPosition::getOutputLSOT(){
 return outputLSOT;
 }
 String SolarPosition::getOutputClockTime(){
 return outputClockTime;
 }
 double SolarPosition::getOutputHourAngle(){
 return outputHourAngle;
 }
 double SolarPosition::getOutputAltitude(){
 return outputAltitude;
 }
 double SolarPosition::getOutputDeclination(){
 return outputDeclination;
 }
 String SolarPosition::getOutputSunrise(){
 return outputSunrise;
 }
 String SolarPosition::getOutputSunset(){
 return outputSunset;
 }
 double SolarPosition::getOutputAzimuth(){
 return outputAzimuth;
 }
 void SolarPosition::setTime(int Year, int Month, int Date, int Hour, int Minute, int AMPM){
 inputYear = Year;
 inputDate = Date;
 inputMonth = Month;
 inputHour = Hour;
 inputMinute = Minute;
 inputAMPM = AMPM;
 }
void SolarPosition::Calculate(int Year, int Month, int Date, int Hour, int Minute, int AMPM) {
 inputYear = Year;
 inputDate = Date;
 inputMonth = Month;
 inputHour = Hour;
 inputMinute = Minute;
 inputAMPM = AMPM;
 
 inputLongitude = 77.45;
 inputEastWest = 0; // West = 0, East = 1
 inputLatitude = 43.22;
 inputNorthSouth = 0; // North = 0, South = 1;
 inputElevation = 465;
 inputFeetMeters = 0; // Feet = 0, Meters = 1
 inputTimeZone = -5;
 inputDaylight = 1;
 inputZeroAzimuth = 0;// 0 = North, 1 = South

 if (inputEastWest == 1) inputLongitude *= -1; // [0] = east, [1] = west
 if (inputNorthSouth == 1) inputLatitude *= -1; // [0] = north, [1] = south

 if (inputMonth > 2) {
 inputMonth = inputMonth - 3;
 }
 else {
 inputYear = inputYear - 1;
 inputMonth = inputMonth + 9;
 }
 if (inputLongitude > 180) inputLongitude = inputLongitude - 360;
 if (inputLongitude < -180) inputLongitude = inputLongitude + 360;
 if (inputFeetMeters == 0) inputElevation *= feetToMeters;
int meridian = inputTimeZone * -15;
if (inputAMPM == 0 && inputHour == 12) inputHour = 0;
 if (inputAMPM == 1) { if (inputHour != 12) inputHour += 12; }
double inputHoursAfterMidnight = inputHour + (float)inputMinute / 60;
 double inputMinutesAfterMidnight = inputHour * 60 + inputMinute;
 double UT = inputHoursAfterMidnight - inputTimeZone - inputDaylight;
 double t = ((UT / 24) + inputDate + floor(30.6 * double(inputMonth) + 0.5) + floor(365.25 * double(inputYear - 1976)) - 8707.5) / 36525;
 double G = NormalizeTo360(357.528 + 35999.05 * t);
 double C = (1.915 * sin(G * degreesToRadians)) + (0.020 * sin(2.0 * G * degreesToRadians));
 double L = NormalizeTo360(280.460 + (36000.77 * t) + C);
 double alpha = L - 2.466 * sin(2.0 * L * degreesToRadians) + 0.053 * sin(4.0 * L * degreesToRadians);
 double GHA = NormalizeTo360(UT * 15 - 180 - C + L - alpha);
outputDeclination = atan(tan((23.4393 - 0.013 * t) * degreesToRadians) * sin(alpha * degreesToRadians)) * radiansToDegrees;
outputEOT = (L - C - alpha) / 15;
double clockTimeToLSOTAdjustment = ((inputLongitude - double(meridian)) / 15.0) - outputEOT + double(inputDaylight); // in hours
double solarHourAngle = NormalizeTo180(GHA - inputLongitude);
double apparentSolarTime = NormalizeTo24(12 + solarHourAngle / 15.0);
 
 solarMinutesAfterMidnight = inputMinutesAfterMidnight - (clockTimeToLSOTAdjustment * 60.0);
double whichDay = 0;
 
 if (solarMinutesAfterMidnight < 0)
 { 
 solarMinutesAfterMidnight += 24 * 60;
 whichDay = -1;
 }
if (solarMinutesAfterMidnight >= 24 * 60)
 { 
 solarMinutesAfterMidnight -= 24 * 60;
 whichDay = 1;
 }
 
 String solarTime = MinutesToClockTime(int(solarMinutesAfterMidnight));
if (whichDay == -1) outputLSOT = solarTime + "-";
 if (whichDay == 0) outputLSOT = solarTime;
 if (whichDay == 1) outputLSOT = solarTime + "+";
whichDay = 0;
if (inputMinutesAfterMidnight < 0)
 { // it's the day before
 inputMinutesAfterMidnight += 24 * 60;
 whichDay = -1;
 }
if (inputMinutesAfterMidnight >= 24 * 60)
 { // it's the next day
 inputMinutesAfterMidnight -= 24 * 60;
 whichDay = 1;
 }
String clockTime = MinutesToClockTime((int)inputMinutesAfterMidnight);
 
 if (whichDay == -1) outputClockTime = clockTime + "-";
 if (whichDay == 0) outputClockTime = clockTime;
 if (whichDay == 1) outputClockTime = clockTime + "+";*/
 
 outputHourAngle = (solarMinutesAfterMidnight - (12 * 60)) / 4;
 
 double altitudeAngle = radiansToDegrees * asin(
 (sin(inputLatitude * degreesToRadians) *
 sin(outputDeclination * degreesToRadians)) -
 (cos(inputLatitude * degreesToRadians) *
 cos(outputDeclination * degreesToRadians) *
 cos((solarHourAngle + 180) * degreesToRadians)));
outputAltitude = altitudeAngle;
 
 double preAzimuthAngle = radiansToDegrees * acos(
 (cos(outputDeclination * degreesToRadians) *
 ((cos(inputLatitude * degreesToRadians) *
 tan(outputDeclination * degreesToRadians)) +
 (sin(inputLatitude * degreesToRadians) *
 cos((solarHourAngle + 180) * degreesToRadians)))) /
 cos(altitudeAngle * degreesToRadians));
 
 outputAzimuth = preAzimuthAngle + (inputZeroAzimuth - 180);
 if (inputZeroAzimuth == 0)
 outputAzimuth = ChangeSign(outputAzimuth, 0, outputHourAngle);
 else
 outputAzimuth = ChangeSign(outputAzimuth, 1, outputHourAngle);
double sunRiseSetLSoTMinutes = radiansToDegrees * acos(-1 *
 (sin(inputLatitude * degreesToRadians) *
 sin(outputDeclination * degreesToRadians) -
 sin((-0.8333 - 0.0347 * sqrt(inputElevation)) * degreesToRadians)) /
 cos(inputLongitude * degreesToRadians) /
 cos(outputDeclination * degreesToRadians)) * 4;
 
 outputSunrise = MinutesToClockTime((int)(12 * 60 - sunRiseSetLSoTMinutes + (clockTimeToLSOTAdjustment * 60)));
 outputSunset = MinutesToClockTime((int)(12 * 60 + sunRiseSetLSoTMinutes + (clockTimeToLSOTAdjustment * 60)));
 
 }
double SolarPosition::ChangeSign (double input, int mode, double basis) {
if (mode == 0) {
 if ((input * basis) < 0) {
 input *= -1;
 }
 }
 
 else {
 if ((input * basis) > 0) {
 input *= -1;
 }
 }
 
 return input;
 }
String SolarPosition::doubleToString(double input,int decimalPlaces){
 if(decimalPlaces!=0){
 String string = String((int)(input*pow(10,decimalPlaces)));
 if(abs(input)<1){
 if(input>0)
 string = "0"+string;
 else if(input<0)
 string = string.substring(0,1)+"0"+string.substring(1);
 }
 return string.substring(0,string.length()-decimalPlaces)+"."+string.substring(string.length()-decimalPlaces);
 } else {
 return String((int)input);
 }
 }
String SolarPosition::MinutesToClockTime (int totalMinutes) {
 String output;
 int theHours = floor(totalMinutes / 60);
 int theMinutes = (totalMinutes % 60);
if (theMinutes < 10) output = "0" + String(theMinutes); // add leading "0" if necessary
 else output = String(theMinutes);
if (theHours < 12) {
 if (theHours == 0) theHours = 12;
 output = String(theHours) + ":" + output + "am";
 }
 else {
 if (theHours == 12) theHours = 24;
 output = (String(theHours - 12)) + ":" + output + "pm";
 }
 
 return output;
 }

 double SolarPosition::NormalizeTo360 (double theThing) {
 return (theThing - floor(theThing / 360.0) * 360);
 }

 double SolarPosition::NormalizeTo180 (double theThing) {
 theThing = NormalizeTo360(theThing);
 if (theThing > 180) { theThing = theThing - 360; }
return (theThing);
 }

 double SolarPosition::NormalizeTo24 (double theThing) {
 return (theThing - floor (theThing / 24.0) * 24);
 }

Green machine

One of the nicest things about not having a woman around when you’re trying to get stuff done is not having a woman around when you’re trying to get stuff done.

With that in mind I started tossing together a CNC machine that will be able to mill wood, plastics, and PCBs using the RepStrap idea of tossing something together to make the parts for a more permanent machine.  This will be used mainly to create parts for a 3D printer and to properly mill a replica of itself that will be perfectly machined.  Once complete it will be dismantled and its parts will be recycled to make something that runs much more efficiently and looks a fuck of a lot prettier.

Also I’m doing it entirely with hand tools.  Just a hand saw, power drill, and battery-powered Dremel.  With batteries charged off of the solar panel.  Hence the title.  Fuck it.

Here’s a quick pic of what I have so far.  More later, maybe with plans or something:

Yes, that’s a first aid kit in the background. Yes, I cut my thumb open. Yes, it was a hand saw. Yep, that’s pretty sad.

Things I never finished

Before I tuck into three projects this weekend I thought I’d throw up some pictures of things I started and never quite worked out completely from either becoming too bored or moving on to something else that seemed harder or more interesting (she said).

Don’t judge me as a person for any of this.

 

Bluetooth-enabled rotary phone

 

Image

The idea here was to take this 60’s era Carlson-Stromberg rotary phone and rewire it to work with a bluetooth phone, sending the rotary dialed numbers to the phone simulating keypad strokes (or touchscreen clicks or whatever).  Basically it’d use your mobile to become a fully functional desk phone.

Why I didn’t finish it:

  • Couldn’t figure out how to ramp up a 5v power supply to a high enough voltage to ring the bell quickly enough. (I’ve figured it out since then)
  • Got sick of dealing with the bluetooth stack
  • Was well out of my element at the time

 

1940 Philco tube radio restoration and electronification (made up word alert)

Image

 

The idea was to take this radio, fully restore it’s electronics, then add external interfaces that would allow remote tuning and volume adjustment and add input for internet radio, satellite radio, a phonograph, and whatever else seemed interesting. Also this had to be done without augmenting it’s current functionality, thus everything must be strapped on.  This way it could easily be restored to its original condition without leaving a trace that anything had been done to it.

So far all existing electronics have been restored, and it has a phono pre-amp with an audio-in port on it supplying sound via RCA and/or 3.5mm jacks.  Power is controlled via relay and the pre-amp automatically adjusts to the input volume to avoid any awkward pops or chopping of the frequency.

Why I didn’t finish it:

  • Lack of decision on which way to go with some things
  • I dunno, I’ll definitely finish this soon

 

Weird biped-looking robot thing

Image

 

Uhh… yeah.  It was never meant to really hold itself up.  The legs look much shorter than they are in this picture.  It was more to try to learn about biped motion and play with the cool 32 channel servo controller I got.

Why I never finished it:

  • Uhh… look at it.  That shit haunts your dreams.

 

Object avoiding edge avoiding light seeking color detecting family eating tracker bot

Image

 

This one was a bit interesting for me because I made it run on only 6v instead of 9-12v like most other bots I’ve done.  The challenge was to make it move and run the electronics without the startup draw from the motors browning everything out.  It has an ultrasonic device for checking out its surroundings, an IR detector for making sure it doesn’t fall off of anything stupidly, tracks for climbing over shit, LDRs for reading light levels in stereo, and everything is done on a breadboard mounted to the chassis so everything can be modified on the go.

Why I never finished it

  • Ran out of nice jumper wires
  • Stopped being interested
  • Meh, who knows, maybe I will finish it

 

College

Image

 

Despite attending Ithaca College, a school that the year prior to my enrollment had ranked #4 in Playboy’s best-looking-girls-on-campus survey jobber, I still dropped out.

Why I never finished it

  • Yeah… umm…
  • I have no idea
  • Apathy maybe?

Too much to remember

An important part of the circuitry driving the solar charger (at least to me) was granting it the ability to calculate the position of the sun for a given date/lat/long/etc…

Well apparently that’s too much to ask of my little ATMega2560 controller.  He’s only loaded with 8K of onboard memory.  That means that while he’s doing things like updating the display on the LCD screen, multiplexing the 7 segment output, and monitoring voltages and temperatures of the panels he shits himself when trying to recalculate the position of the sun.  The controller freezes up and makes my boner sad.

Now for the choose your own adventure portion of our meganerd saga.

To overcome this memory limit I have a few options:

  1. Try to optimize the code using shorthand integer mathematics to get roughly-right solar position calculations.
  2. Add an external 128K RAM chip to the controller, which boosts my total available memory to 136K but robs me of 24 output pins.
  3. Re-implement the software in C or Assembly on a Parallax Propeller microcontroller board, which supports multithreading but would require more external circuitry as it uses 3.3V logic levels instead of 5V.
  4. Offload the heavy duty calculations to a PC and add a network interface to the controller to ping the PC for this data on a set interval (weak).
  5. Ditch the calculations and do analog sun-hunting with light sensors (lame).

Since this post doesn’t really have any possible images and pictures are nice here’s Burt Bacharach:

Image

Comments welcome, I’ll be making my decision today.