How to control 3 LED's using SunriseSunset data from SolarCalculator library

Very first Arduino project so I need a lot of help.

Hardware

  • Arduino Uno R4 Minima
  • DS3231 (RTC)
  • 270 Ohm resistors
  • 3-3.2V, 20mA LED

Goal
I'm trying to turn on 1 LED during the day, another during golden hour (sunrise~20min after sunrise & 20min before sunset~sunset) for a traffic light type art piece I'm making.

So Far
I've uploaded the "ds3231" example code from RTClib...

// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  Serial.begin(57600);

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop () {
    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();

    Serial.print(" since midnight 1/1/1970 = ");
    Serial.print(now.unixtime());
    Serial.print("s = ");
    Serial.print(now.unixtime() / 86400L);
    Serial.println("d");

and gotten the correct time output.

I plan
on using the "SunriseSunset" example code from the SolarCalculator library to get the times for sunrise and sunset.

#include <SolarCalculator.h>

void setup()
{
  Serial.begin(9600);

  // Date
  int year = 2000;
  int month = 1;
  int day = 1;

  // Location
  double latitude = 42.36;
  double longitude = -71.058;
  int utc_offset = -5;

  double transit, sunrise, sunset;

  // Calculate the times of sunrise, transit, and sunset, in hours (UTC)
  calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset);

  // Get the approximate times (minimum program size) (iterations = 0)
  //calcSunriseSunset(year, month, day, latitude, longitude, transit, sunrise, sunset, SUNRISESET_STD_ALTITUDE, 0);

  // Print results
  char str[6];
  Serial.println(hoursToString(sunrise + utc_offset, str));
  Serial.println(hoursToString(transit + utc_offset, str));
  Serial.println(hoursToString(sunset + utc_offset, str));
}

void loop()
{
}

// Rounded HH:mm format
char* hoursToString(double h, char* str)
{
  int m = int(round(h * 60));
  int hr = (m / 60) % 24;
  int mn = m % 60;

  str[0] = (hr / 10) % 10 + '0';
  str[1] = (hr % 10) + '0';
  str[2] = ':';
  str[3] = (mn / 10) % 10 + '0';
  str[4] = (mn % 10) + '0';
  str[5] = '\0';
  return str;
}

My question's are...

  1. How do I use the date I'm getting from my RTC for the SunriseSunset code? I see places in the code for manually inputting date but I'm not sure how to automatically use the date I have on my RTC for this part of the code.

  2. I'm thinking from there the code would be something like

void loop(){
  if(current time == sunrise + 20min){ // time from RTC = sunrise calculated by sunrisesunset code + 20 minutes
    digitalWrite(13,HIGH); // first LED for daytime on
   
  }
  else{ 
    digitalWrite(13,LOW);// turn the LED off
  }

but I'm unsure of the specifics, like how to keep the LED on from the point it is sunrise+20min till it is Sunset-20min and not just turn on the LED for that 1 minute of time where sunrise+20 is true.

Any amount of guidance would be greatly appreciated. I know only how to carve wood.

Welcome! You will want to learn about state machines, I suggest you start here:

Think of your program as having four states: Night, Sunrise, Day, Sunset. Your code need simply decide which state it is in based on the time information you’re already aware of, and then execute one of four states.

I suggest you read up, then make an attempt, and come back to us when you have questions.

Making your LEDs turn on and off with the RTC is like setting an alarm on a clock. Have a look at the "alarm" example from the library you are using: RTClib/examples/DS3231_alarm/DS3231_alarm.ino at master · adafruit/RTClib · GitHub

look this over

  • it shows how the time in hours:mins is more conveniently handled in terms of mins for an entire day
  • it shows how the current, sunrise and sunset can be compared in terms of minute to recognize the various "windows" of time throughout the day
  • it uses a simple function to simulate the use of an RTC to get the time, hour:mins. You posted a similar function to get the sunrise/sunset times
  • a print to indicates which LED is turned on.
char s [90];

// -----------------------------------------------------------------------------
struct DateTime {
    unsigned hour;
    unsigned mins;
};

DateTime getTime ()
{
    DateTime time;

    unsigned long mins5 = 5 *millis () /200;   // 25 mins /sec
    time.mins =     mins5 %60;
    time.hour = 1 +(mins5 /60) %24;

    return time;
}

// -----------------------------------------------------------------------------
enum { LedDay, LedNight, LedGolden };

const char *LedStr [] = { "Day", "Night", "Golden" };

int ledIdxLst;

void turnOnLed (
    int ledIdx )
{
    if (ledIdxLst != ledIdx)  {
        ledIdxLst  = ledIdx;

        sprintf (s, "    turnOnLed: %d %s", ledIdx, LedStr [ledIdx]);
        Serial.println (s);
    }
}

// -----------------------------------------------------------------------------
DateTime dtSunrise = {  7, 30 };
DateTime dtSunset  = { 16, 10 };

void loop ()
{
    unsigned sunriseMins = 60*dtSunrise.hour +dtSunrise.mins;
    unsigned sunsetMins  = 60*dtSunset .hour +dtSunset .mins;

    DateTime now = getTime ();
    unsigned mins = 60*now.hour + now.mins;

    sprintf (s, " %2d:%02d %2d:%02d %2d:%02d", now.hour, now.mins,
        dtSunrise.hour, dtSunrise.mins, dtSunset.hour, dtSunset.mins );
    Serial.println (s);

    if (mins < sunriseMins -20)
        turnOnLed (LedNight);
    else if (mins < sunriseMins)
        turnOnLed (LedGolden);
    else if (mins < sunsetMins -20)
        turnOnLed (LedDay);
    else if (mins < sunsetMins)
        turnOnLed (LedGolden);
    else
        turnOnLed (LedNight);

    delay (250);
}


// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
}

When your "alarm time" arrives, call the Sunrise/Sunset function.

#include <RTClib.h> // the library
RTC_DS1307 rtc; // create an object/instance to control
int secOld, secNew; // variables to store "alarms"

void setup () {
  Serial.begin(115200); // start serial comms
  rtc.begin(); // start rtc
  pinMode(LED_BUILTIN, OUTPUT); // confiure led pin for output
}

void loop () {
  DateTime now = rtc.now(); // read time
  secNew = now.second(); // store "seconds"

  if (secOld != secNew) { // compare old "alarm" (seconds) with new "alarm" (seconds)
    secOld = secNew; // store new "alarm" start
    sunRiseSet(); // call a function
  }
}

void sunRiseSet() {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // flip the LED_BUILTIN state
  Serial.print(digitalRead(LED_BUILTIN)); // see it in the Serial Monitor
}

In my clocks I gently dim the LEDs around dusk/dawn.
So not time related, but solar angle related.
Which of course happens slower in winter than in summer.
Solar angle can be found this way.
calcHorizontalCoordinates(now, latitude, longitude, az, el);
Then I map elevation to PWM values for the LED.
I get 'now' from NTP, not from a RTC.
Maybe this works for a RTC.
calcHorizontalCoordinates(rtc.now(), latitude, longitude, az, el);
Leo..

Hi @Wawa !

I assume rtc.unixtime() should do it. It calculates and returns the RTC’s time data converted to UTC

 /* 32-bit times as seconds since 1970-01-01. */

uint32_t unixtime(void) const;

which is one of the formats accepted by the SolarCalculator library

void calcHorizontalCoordinates(unsigned long utc, double latitude, double longitude, double& azimuth, double& elevation);

void calcHorizontalCoordinates(int year, int month, int day, int hour, int minute, int second, double latitude, double longitude, double& azimuth, double& elevation);