Search

Working with Time, Sunset, Sunrise , C++ and Arduino

Today I was working on calculating the sunset and sunrise times on a device that is not necessarily going to be connected to the internet, but uses a real-time clock to keep track of time, a MAX31328. This device the Micro Retic Max, is a reticulation controller that we have designed, it work's well with Home Assistant, which handles when and for how long to water the garden, but I would like it to work when not connected for a number of reasons. First WIFI drop out(the controller lives in my shed), second, Home Assistant is shut down for maintenance, or maybe someone else wants to use it but doesn't want Home Assistant... yet. Or maybe I just want to be able to use them at jobs where people need a plain, reliable controller. I also want to apply this knowledge to a time-clock module/controller that is low cost so I can just keep a few in my van on hand, easy to program, easy to repair (no such thing currently exists). To achieve all of these goals I need to understand how to use time functions, get sunrise, sunset, and sort and recall alarms plus more in the Arduino environment.

Micro Retic Max
Upgraded design in testing

I started with the ESPDateTime Library (https://github.com/mcxiaoke/ESPDateTime) because I have an ESP32 and I need time on it. This library has one simple example to show a connection to wifi, obtaining the time from an NTP server, and setting your timezone which is all pretty straightforward. I'm using an ESP32 PICO D4 module from M5 Stack it's very small and it is called the Stamp. I load in the demo and send it to my device. It loads and starts running, after a bit of fiddling around I realize the serial monitor needs to be set to 9600, and boom, it's all go everything is printing out on my screen as expected. I see the current date and time in many different formats, what a surprise. Just looking over the example it's clear to me I can now display the time and/or current date, on the serial monitor anyway I desire, but what about just the hours and minutes as integers so I can work with them and compare them. And what about formatting time that is not the current time.

Serial.printf("TimeZone:      %s\n", DateTime.getTimeZone());
Serial.printf("Up     Time:   %lu seconds\n", millis() / 1000);
Serial.printf("Boot   Time:   %ld seconds\n", 
                DateTime.getBootTime());
Serial.printf("Cur    Time:   %ld seconds\n",
                DateTime.getBootTime() + millis() / 1000);
Serial.printf("Now    Time:   %ld\n", DateTime.now());
Serial.printf("OS     Time:   %ld\n", DateTime.osTime());
Serial.printf("NTP    Time:   %ld\n", DateTime.ntpTime(2 * 1000L));

So now I move on to the Time Library (https://github.com/PaulStoffregen/Time) this library has enabled me to take a UNIX timestamp and get the hour, minute, day from it as an integer, great but digging through the library trying to figure out how to format the time like this: 06:04 from a past timestamp, I discovered that there were a few things I didn't. Such as what it this time_t type, it's not declared anywhere and how can they use a struct called tm that I could not find declared anywhere. I looked in every file, till I foiind the reference that answered my question. (http://www.cplusplus.com/reference/ctime/tm/) It turned out that tm is a c++ struct that is for the time and date and even includes a flag for daylight saving, so you don't need to define it, it's part of the Arduino environment to use, I also worked out that localtime() along with mktime() and gmtime() are not functions of Time but of c++ ctime standard library, and they are unsurprisingly super useful. localtime(time_t) accepts a time type time_t which is basically a long unsigned integer, which is what a UNIX timestamp is which is the number of seconds from a random date in 1970 till now counting up. Of course, you will somewhere in your code need to have the local time zone set which it happens I did with the first library I used.


I have come this far now, and I find a function that is part of ESPDateTime and it will convert the time into all sorts of formats using strftime, it seems easy to use as well, but still only the current time. I find the function in the cpp file, it only accepts the parameters that define the output, I can't send it a time_t variable. But I can modify/copy the function so that that iyt will accept also a time_t in my case it's the sunrise time for today in Perth, Western Australia, but it could be any time, such as the next time the sprinklers will turn on. It will give me a string in return, fully formatted.

//This is part of the library
String DateTimeParts::format(const char* fmt) const {
  char buf[64];
  struct tm* t = localtime(&_ts);
  strftime(buf, sizeof(buf), fmt, t);
  return String(buf);
}

I need to add one more argument to this function but pointers and de-referencing & and * hurts my head, I'm amazed anyone can remember whats what, but where the &_ts is, is where I want my timestamp for sunrise to go. I decide to make a random effort, it is educated though, I have read and watched about pointer and I get what they are for.

String formatTimestamp(const char* fmt, time_t ctm ){
  char buf[64];
  struct tm* t = localtime(&ctm);
  strftime(buf, sizeof(buf), fmt, t);
  return String(buf);
}

And it actually work the first time, weird.

Serial.printf("SunriseTime:   %s\n",                                   formatTimestamp("%H:%M", trise));

And printed out: SunriseTime: 06:04

Ok, so that is correct, I also got the sunset time from a Library called Arduino-sun (https://github.com/nplan/Arduino-Sun) it is very easy to use and calculates the sunset, sunrise, night length in seconds, day length in second using the latitude and longitude.

It is initialized by Sun sun(lat, long);

So the related code in my example for my location looked like this :

#include <Sun.h>
.
.
Sun sun(-31.81, 115.86);
time_t trise = (sun.getRise(DateTime.now()));
time_t tset = (sun.getSet(DateTime.now()));
.
.

You give the sun the time now, or any time today and it tells you today's sunset and sunrise times as time_t types or unsigned long, they are interchangeable. You pass them to the formatTimestamp() function just up there and I am done for today. Except to tell you one more interesting discovery, a lot of programs that use time, work with time including this project use the UNIX timestamp, it's nice and easy to work with. Only it will run out of space in 2038 which is 16 years away, which is not really that long away, and it seems to me that, it will be similar Y2K which ended up being nothing, it sort of fizzled out, but it might not fizz out. We'll have to wait and see.

120 views0 comments

Recent Posts

See All