 # Automated Chicken Coop Door

I see most of my coding has been done before, saving me a lot of time!

In this article: http://arduino.cc/forum/index.php/topic,8705.0.html

There are the lines:

``````sunrise = int(350 + (90*cos(((((month-1)*30.5)+dayOfMonth)+8)/58.1)));
sunset = int(1075 + (90*sin(((((month-1)*30.5)+dayOfMonth)-83)/58.1)));
``````

Where did this come from, and how do I know if it will work for my location (Hopkinsville, KY)

Any equation to find sunrise sunset is based on location. In order to rewrite it for your location, take a look at this: take a look at this: http://williams.best.vwh.net/sunrise_sunset_algorithm.htm . One question: why not use a light sensor to figure out sunrise/sunset instead of an expensive RTC and lots of math? IMHO, that makes a lot more sense.

Analysis: The assumption here is that the sunrise/set timetable is one sinus wave, the rest is scaling and translating a bit

_sunrise = int (350 + ( 90*cos( ((((month-1)*30.5)+dayOfMonth)+8) /58.1) )); _

The part: (month-1)*30.5 + dayOfMonth + 8 This is a formula for dayoftheyear with a small offset 8 days.

If you fill in the days and months of the year, you will get approx. 23 march = 90; 23 june = 180; 23 sept = 270 and 23 dec = 360 (=0) . These are the solstice and equinoxes. Note the +8 is the offset between 1 jan and 23 dec (shortest day in NH = Northern Hemisphere)

so now we can bring back the formula to: _sunrise = 350 + 90*cos( dayofyear/58.1) ; _

As the cos() needs a value between 0..2PI the dayof year must be divided by a value so that 0 is mapped on 0 and 365 is mapped upon 2PI. simple math: 365/2PI = 58.0915542... which is rounded to 58.1. This is a very small error but one better uses 58.091554 (the approx limit of Arduino float) to keep error to a minimum

This brings the formula to: _sunrise = 350 + 90 * cos(); _ where cos() varies between from [-1, 1];

90 is the half of the difference between the sunrise on 23 dec and 23 june. 350 is the middle between the two sunrises, approx the sunrise @23 march/sept

OK, now take some time to reread the analysis above.

Back to the question : Where did this come from, and how do I know if it will work for my location (Hopkinsville, KY) We now know where it comes from - see above - and we have the means to adapt the formulas.

OK, to change this formula to the lattitude of Hopkinsville, KY

step 1: Google EARTH => 36.51 north 87.29 E step 2: http://www.srrb.noaa.gov/highlights/sunrise/sunrise.html

-- update -- the numbers below are to be checked, this are just how its done

Sunrise @23 june = 4:32 = 60 * 4 + 32 = 272 Sunrise @23 dec = 7:21 = 60 * 7 + 21 = 441 delta = 441- 272 = 169 => divide by 2 makes 85 average = (441+272)/2 = 356

So the sunrise formula for Hopkinsville, KY will be: _sunrise = 356 + 85 * cos( ((month-1)*30.5+dayOfMonth+8) /58.1); _ // removed some unneeded ()

The sunset formula can be done in same way, left as an exercise for the reader.

Some programming/optimizing hints:

As sin() and cos() are quite equal I would change the formulas a bit so it only uses cos().

as : sin(x) = -cos(x+90);

one could convert the formulas to :

void getRiseSet() { int doy = (month-1)*30.5+dayOfMonth;

sunrise = 350 + 90*cos(( doy + 8 )/58.1); sunset = 1075 - 90*cos(( doy + 8 )/58.1); // OK, I smuggled 1 day :) (83+8 = 91 not 90 but as there are more rounding errors allready... }

or max optimized to

void getRiseSet() { float common = 90 * cos( ((month-1)*30.5+dayOfMonth + 8 ) / 58.091554) ; sunrise = 350 + common; sunset = 1075 - common; }

update 2012-10-27 : void getRiseSet() { float common = 90 * cos( ((month-1)*30.5+dayOfMonth + 8 ) * 0.017214206) ; // replace division with faster multiplication sunrise = 350 + common; sunset = 1075 - common; } 90 * 0.017214206 can be precalculated too but they have a different semantic meaning and the compiler might optimize it anyway.

Some interesting link I found during this exercise: - http://www.astro.uu.nl/~strous/AA/en/antwoorden/zonpositie.html - and of course wikipedia - http://en.wikipedia.org/wiki/Sunrise_equation -

Note 1: beware that this formulas are approximations (quite good ones I think)

Note 2: these formulas don't take DST into account.

fun!!!

One question: why not use a light sensor to figure out sunrise/sunset instead of an expensive RTC and lots of math? IMHO, that makes a lot more sense.

Overcast days, fogged in mornings or evenings, full moon Vs new moon, stormy weather? I would think ambient light level would be a very unreliable indications of exact sunrise and sunset times?

Lefty

BTW an automated chicken door could also be based upon a microphone checking the morning call of the rooster ;) IT’LL TAKE ME 2 WEEKS TO GET OVER THE ANSWER TO THIS QUESTION!

OK,

Sunrise @23 jun = 4:32= 60 * 4 + 32= 272 Sunrise @23 dec = 7:00 = 60 * 7 + 00 = 420 delta = 420- 272 = 148 => divide by 2 makes 74 average = (420+272)/2 = 346

Sunset @23 jun = 19:13 = 60 * 19 + 23= 1163 Sunset @23 dec = 16:38 = 60 * 16 + 38= 998 delta = 1163 - 998 = 165 => divide by 2 makes 82 average = (1163 + 998)/2 = 1080

So the formulas for Hopkinsville, KY will be:

``````void getRiseSet()
{
float common = cos( ((month-1)*30.5+dayOfMonth + 8 ) / 58.091554) ;
sunrise = 346 + 74 * common;
sunset =  1080 - 82 * common;
}
``````

I hope I saved you two weeks, but please spent an hour or so to understand the story, maybe I have bugged up somewhere and you should be able to fix it ;)

@ SouthernAtHeart, don't read below this line ;)

What amazed me is that the sunset has a bigger spread (delta is 8 minutes larger) than the sunrise. can't explain this yet ...????

In the original code (what I posted in #1)

``````sunrise = int(350 + (90*cos(((((month-1)*30.5)+dayOfMonth)+8)/58.1)));
sunset = int(1075 + (90*sin(((((month-1)*30.5)+dayOfMonth)-83)/58.1)));
``````

it has 350 + .... and 1075 + ....

but yours has

346 + ... 1080 - .... should that be 1080 + ... ??? I haven't spent an hour on it, but 20 minutes or so. I understand the jist of it, but doubt I could troubleshoot your formula. If you made a mistake, you're going to here from my chickens! (if they can get out of the pen)

but yours has

346 + ... 1080 - .... should that be 1080 + ...

The change of sign is introduced as I rewrote the sin(x) with a -cos(x + 90) ; (and yes I smuggled 1 day )

If I apply the above rewriteing on the original formula the sign will change.

``````sunrise = int(350 + (90*cos(((((month-1)*30.5)+dayOfMonth)+8)/58.1)));
sunset = int(1075 - (90*cos(((((month-1)*30.5)+dayOfMonth)+8)/58.1)));
``````

To explain it otherwise, first note that the whole long part (90*....) is the same now due to the rewrite

If it becomes summer that long part will go to -1 sunrise = 350 + 90 * -1 => sinrise is earlier sunset = 1075 - 90 * -1 => sunset is later days are becoming longer.

If it becomes winter that long part will go to 1 sunrise = 350 + 90 * 1 => sunrise is later and later sunset = 1075 - 90 * -1 => sunset is earlier and earlier days are becoming shorter

The formula is in essence : sunrise = average sunrise + Func(dayOfTheYear); // idem for sunset

more clear now?

It's VERY clear that you understand it! I follow the jist of it, but it's a little gray yet. I'll move on now to something simpler like watching the little 1-wire temperature sensor, and turning an AC relay on if it gets too hot or too cold (for a fan, or a drinking water heater) I'll be back when I run into trouble!

write a small sketch that uses the formula and let it generate a list of sunrises and sunsets. You see that it will be reasonable correct (+_ 5 minutes)

I'll be back when I run into trouble!

If you have problems with this one, PM me. Add a link to this discussion.

I'll move on now to something simpler like watching the little 1-wire temperature sensor, and turning an AC relay on if it gets too hot or too cold

Also a fun project, familiar with - http://www.milesburton.com/Dallas_Temperature_Control_Library - ??

Thanks, robtillaart, I’ve gone thru this code a few more times, and have understood it enough to redo it for another GPS location! It’s not too complicated once you understand it, and I DO much prefer to understand the code I use, rather than use it and not know how it does what it does…

My next question:
This one has me stumped. I’m using an RTC to get my date info. Each time I get the date, I want to check to see if it is:

1. after the second Sunday in March, &
2. before the first Sunday in November

I thought I use a boolean function that returns TRUE if it is DST, or FALSE if it’s not DST

``````boolean CheckDST(){
/*
Starts: Second Sunday in March
Ends: First Sunday in November
*/
if (month >= 3 && dayOfWeek <= 11) {    //It's a DST month
...not sure here...

}

month, dayOfWeek, dayOfMonth, etc are all global variables that are updated to the current date
``````

SouthernAtHeart: I see most of my coding has been done before, saving me a lot of time!

Having previously written a sunrise/sunset algorithm (+-1 minute, based on current GPS location) before, whereby it took an embarrassingly long time to write/debug, I tip my hat to you for a dramatically simplified approximation. Nicely done.

If anyone cares, my implementation was used to automatically brighten and dim the display of an android device while providing considerable power savings over that of the common auto adjust logic. If you have an android device and struggle with battery life, turning off auto brightness typically saves a fair amount.

Just curious - why does it matter whether it's DST? I'd assumed that you're opening the coop at astronomical sunrise - are there other functions that are affected by what time people think it is?

…The sunrise isn’t affected by DST? Well, I reckon it wouldn’t matter, other than the LCD display will be an hour off most of the year. But since the chickens are the only ones that will be seeing it most of the time…

I did find some code that I tweaked to work (I think it will work)

``````boolean CheckDST(){
/*
Starts: Second Sunday in March
Ends: First Sunday in November
*/
//January, february, and december are out.
if (month < 3 || month > 11) { return false; }
//April to October are in
if (month > 3 && month < 11) { return true; }
int previousSunday = dayOfMonth - dayOfWeek;
//In march, we are DST if our previous sunday was on or after the 8th.
if (month == 3) { return previousSunday >= 8; }
//In november we must be before the first sunday to be dst.
//That means the previous sunday must be before the 1st.
return previousSunday <= 0;
}
``````

They don’t make anything like an ‘atomic RTC’ ??? My \$25.00 watch has atomic time keeping, and I paid \$15 for a DS1307 from Sparkfun that looses a minute every month. How’d I miss that? I thought I scoured their website!
Thanks.

retrolefty: Overcast days, fogged in mornings or evenings, full moon Vs new moon, stormy weather? I would think ambient light level would be a very unreliable indications of exact sunrise and sunset times?

more than a few hours of pitch black should be enough to tell you it's night time :)

After that it's just a case of accepting more than fifteen minutes of light as "dawn", and defining "dusk" as the first sufficiently dark fifteen minute period (perhaps with some weighting based on expected daylight hours). Perhaps use the built in timer to estimate about six hours of daylight, and adjust the estimate of daylight hours based on the last few days worth of recordings.

No worries about RTC drift. No worries about RTC battery replacement. Your controller would then be operating on roughly the same system as the chooks!

That sounds like a lot of computations, but I see how it could work... I do the final install this weekend. I've ended up with an LCD display and a keypad, so the user can update the time (changing the clock from DST to EST will be often enough, so I made the processor work of EST/DST rather than Astronomical time) The DS1307 RTC will lose enough time to require being updated 2x a year, anyway.