Calculating Sunrise/Sunset

So, now that we have most of the components of this project posted (including this post), I can finally give you the details of what we’re trying to accomplish. My house is situated in a smallish town without a lot of street lighting and the nearest light is too far away to effectively illuminate the house. Even worse than that, I have a driveway that slopes back towards the house creating a small, dark area just in front of the garage door. The problem with this arrangement is that it’s very easy for someone to spend a lot of time trying to steal gas from the one car I have that doesn’t fit in the garage. (funny tip: If you’re trying to steal gas, don’t go after a 4 cylinder, 35 MPG car like the guy in my neighborhood did. There is, on average, less than 4 gallons of fuel in the tank!) To help with this problem, I do have a pair of recessed lights on the house over the garage, but I don’t have a way to effectively turn them on and off at the right time of day. Currently, I have to leave the lights on all the time due to an unpredictable personal schedule of work, family, and friends which isn’t very efficient.

What about a light sensor? I did think of that solution, but that has its own set of problems. Being in the northeast, we get a fair bit of snow in the winter time. That snow can reflect a lot of light and cause the sensor to turn the lights off at the wrong time. Taking it a step further, if someone’s willing to resort to minor criminal activity, nothing would stop them from figuring out some way to fool the sensor since it would have to be mounted on the outside of the house where it would be accessible and visible.

My solution is to use a combination of a RTC, the WWVB time signal, and the TimeLord library to accurately compute sunrise and sunset. The resulting circuit would automatically turn the lights on at sunset and turn them off at sunrise. Not only would this be an energy savings over the course of each day, but with advancements in LED technology, I could reduce my energy use to just pennies per month and get the benefit of having a properly lighted driveway. Furthermore, an addition of some wireless communications could allow me to plot the energy usage over the course of the year. That last feature isn’t planned for this version but it wouldn’t be difficult to do.

So, how do you compute sunrise and sunset? Not being afraid of a little math, I started looking online at the equations necessary to accomplish this. I found this page that had all of the calculations and put together some proof of concept code to see how accurate it would be on the arduino. Being spoiled from working on PC’s I was a little surprised to find that the answers didn’t make much sense. In particular, the trig functions in the avr-libc package tended to give me the most inaccurate values. Furthermore, the calculation of the number of days since the last epoch was giving me a number that was too large to be represented on the microcontroller.

My next thought was to add a floating point processor to the circuit, but the problem with that is 1) added chips take up space, making the project bulky …and… 2) added chips drive up the overall cost of the project. Sometimes the best path forward is the simplest, so I did a search online to see if anyone else had already solved the problem. As it turns out, the TimeLord library does exactly what I needed it to do and it’s very easy to use. My only suggestion for the authors of this library would be to improve the documentation. Sometimes it’s hard to tell if values should be passed as local time or UTC. Beyond that, I would also add functions so that the user can pass either local or UTC without having to do the conversion. However, it did solve my problem without needing any extra circuitry.

I will be posting the full code to this project at a later date. For the time being, though, I’m posting the pieces in the same way I developed the final code which is to say that I put together several small and easy to debug code modules and then later integrated all of them into one large project. The benefit of doing it this way is that it’s much easier and faster to debug small modules than it is to try and put the whole project together in one attempt. Here is the proof of concept code for this week:

#include <TimeLord.h>

// current date - BTW - don't do this if you can avoid it.  Globals are evil.
int curYear = 2010;
int curMonth = 11;
int curDay = 12;

// west longitude and north lattitude - approximate - 
//      I'm not giving you my address in an online code example anyway... :-)
double Lw = -78;
double Ln = 43;

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  
  TimeLord tardis;
  tardis.TimeZone(-5 * 60);
  byte day[] = { 0, 0, 12, curDay, curMonth, curYear }; // noon
  tardis.Position(Ln, Lw);
  if (tardis.SunRise(day))
  {
    Serial.print("Sunrise: ");
    Serial.print((int) day[tl_hour]);
    Serial.print(":");
    Serial.println((int) day[tl_minute]);
  }
  
  if (tardis.SunSet(day))
  {
    Serial.print("Sunset: ");
    Serial.print((int) day[tl_hour]);
    Serial.print(":");
    Serial.println((int) day[tl_minute]);
  }
}

void loop() {}

The next post in this series will have the fully integrated and tested code. I still have some modifications to make to the electrical in my garage to enable this to work, so it may be a while before I can post pictures of the final project. My goal is to have this done and installed sometime before the end of April.

6 Comments

  1. Tag says:

    Hi!

    Great piece of code!.
    Think the timelord lib needs proper documentation,
    anyway i tested the code and found out that if you want a proper print on the serial port, the serial print line must print the day[1] as a decimal value..

    So the complete line should look like this:

    Serial.println(day[1],DEC);

    If there should be a training zero for the minutes, put this in front:

    if (day[1] <= 9){ Serial.print("0"); }

    Think it is a nice piece of code!.
    thanks and good luck!

    Tag,

  2. Jerry says:

    I have been trying to get TimeLord working with no luck.
    I got a new copy of TimeLord.h, copied your exact code to a pde.
    Compiled it and got blank : : sunrise and : :4 for sunset.
    Any idea what could be the problem?
    My hardware is working fine with some complex programs.
    I took out the globals, with no change.

    • signal7 says:

      Ok. I’ve been looking over my code, and I have to admit, I’ve got some mistakes in there. One of them is that I used absolute indexes to get the time from the returned byte array. Under normal circumstances, this is a bad practice because if the original author changes which byte corresponds to a particular piece of information, it causes a bug in my code. So, the better way to get at the data is to use day[tl_hour] and day[tl_minute]. The tl_xxxx constants are part of timelord.h and this was fixed before going to the final code.

      Lastly, the time returned is in UTC and the coordinates you supply to the library have to be as accurate as possible. I’m in the GMT-5 timezone, so I have to subtract 5 hours from the result to get the real sunset/sunrise for my timezone. update: Not true. Just re-ran the sketch and got 16:48 for sunset, which is about right after correcting the coordinates…

      Finally (and I think this is the real issue), is that I forgot to cast the byte values to (int) before passing them to Serial.print(). The line should read Serial.print((int) day[tl_hour]); for instance. This tells the compiler which version of the print() function to call. By default, it apparently assumes the input is an ascii character. I’ll correct the post accordingly.

  3. signal7 says:

    Been getting really odd comments from fans of Dr Who about this article. I’m a fan of the show too (hence, my interpretation of how they came up with the name of the TimeLord library), but I don’t think those comments contribute to the discussion in a constructive way.

    I dunno. Do all bloggers get odd comments like this?

    Please, if you have a comment, make it about the actual content. I kinda feel like I’m being hit by a comment bot…

    • Habib says:

      Если не трудно ответьте пожалуйста на вопрос. В данной программеpublic class VarArgs { public stiatc void vaTest (int v) { System.out.print( Количество аргументов: + v.length+ Содержимое: ); for (int x:v) System.out.print(x+ ; ); System.out.println(); } public stiatc void main (String args []) { vaTest();При вызове метода vaTest с отсутствующим параметром компилятор не ругается. Но в следующей программе в строке vaTest (); выдает ошибку: reference to vaTest is ambiguous, both method vaTest(int ) in Array. Почему он так поступает не могу понять .public class VarArgs3 { stiatc void vaTest (int v){ System.out.print( Кол-во агрументов: +v.length+ ); for (int x:v) System.out.print(x+ ); System.out.println(); } stiatc void vaTest (boolean v){ System.out.print( Кол-во агрументов: +v.length+ ); for (boolean x:v) System.out.print(x+ ); System.out.println();} stiatc void vaTest (String msg, int v){ System.out.print(msg+ Кол-во агрументов: +v.length+ ); for (int x:v) System.out.print(x+ ); System.out.println();} public stiatc void main (String args []){ vaTest ( Кря-кря , 2, 70, 0); vaTest (true,false,true); vaTest (); }}

      • signal7 says:

        I’m afraid I don’t speak or know Russian, so I’m only going on a Google translation of your message.

        Unfortunately, I’m not writing C++ in this article and the arduino does not support the varargs interfaces. I’m afraid I can’t really help you with your question. You may want to ask your question on stackoverflow or some other site where there are people with more experience in these areas.