Intermittent bug driving me crazy

The following code opens and closes a linear actuator based on the current day’s sunrise and sunset. The problem I’ve encountered is that it will work seamlessly for 3-4 days and then just stop working, I’ve left a laptop plugged into it with the serial monitor up so I can see my debug statements, they all display as expected for the 3-4 days and then just stop, no errors or anything suspicious. If I then re-publish it, it goes back to normal functioning.

I can provide much more detail but I’ll wait and see what you’re curious about first. But I have 2 queries:

1)Does anything in this code snippet look off or problematic? (C++ is not my native language so I definitely could have screwed something up)
2)If not #1, how can I go about gaining more info on what is failing? (i.e. getting a stack trace on error, attaching a live debugger, etc)?

#include <Time.h>
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <TimeLord.h>
#include <Timezone.h>
#include <DS3232RTC.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

//constants
const int PROGMEM forwardsPIN = 7;
const int PROGMEM backwardsPIN = 6;
const bool OPEN = false;
const bool CLOSE = true;
const TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  //UTC - 4 hours
const TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   //UTC - 5 hours
const Timezone usEastern(usEDT, usEST);


//lcd module
LiquidCrystal_I2C lcd(0x3F, 20, 4);

//sunrise and sunset by location
const float LONGITUDE = -78.6815;
const float LATITUDE = 40.442021;
const int TZ_OFFSET = 0;

TimeLord sunManager;

bool isDailySet = false;

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

  //relay pins
  pinMode(forwardsPIN, OUTPUT);
  pinMode(backwardsPIN, OUTPUT);

  //module init
  lcd.begin();
  //turn lcd backlight on
  lcd.backlight();

  //default resync interval is 300 secs
  setSyncProvider(RTC.get);   // the function to get the time from the RTC


  if (timeStatus() != timeSet){
     Serial.println("INIT - Unable to sync with the RTC");
  }
  else{

     Serial.print("INIT - RTC has set the system time to: ");
     time_t utc = now();
     String dtStr = "";
     currentDT(dtStr);
     Serial.println(dtStr + " UTC");
  }

  //setup sunset/sunrise
  sunManager.TimeZone(TZ_OFFSET);
  sunManager.Position(LATITUDE, LONGITUDE);

  //always run once upon init, then schedule it to run daily from within
  DailySetup();
}

void loop() {
  //timers require this?
  Alarm.delay(1000);
}


void DailySetup(){
  Serial.println("Starting daily setup");

  //set the current time display
  LCDTime();

  //set up the time display to update every minute on a repeating trigger
  Alarm.timerRepeat(60, LCDTime);

  time_t utc = now();
  tmElements_t tm;
  tm.Day = day();
  tm.Month = month();
  tm.Year = year()-1970;

  byte today[] = {0,0,12,day(),month(),year()}; //today 12 noon
  String msg = "Sunrise: ";
  if (sunManager.SunRise(today)){
    tm.Hour = today[tl_hour];
    tm.Minute = today[tl_minute];

    time_t ss =  usEastern.toLocal(makeTime(tm));
    tmElements_t tmss;
    breakTime(ss, tmss);
    char res[14];
    sprintf(res, "%02d:%02d", tmss.Hour, tmss.Minute);
    msg = msg + res;
    lcd.setCursor(3,2);
    lcd.print(msg);
    Alarm.alarmOnce(tm.Hour, tm.Minute, 0, TodaySunrise);
  }

  if (sunManager.SunSet(today)){
    tm.Hour = today[tl_hour];
    tm.Minute = today[tl_minute];
    time_t sr =  usEastern.toLocal(makeTime(tm));
    tmElements_t tmsr;
    breakTime(sr, tmsr);
    char res[14];
    sprintf(res, "%02d:%02d", tmsr.Hour, tmsr.Minute);
    msg = "Sunset:  ";
    msg = msg + res;
    lcd.setCursor(3,3);
    lcd.print(msg);
    Alarm.alarmOnce(tm.Hour, tm.Minute, 0, TodaySunset);
  }

  if(!isDailySet){
    Serial.println("Scheduling DailySetup on timer");
    Alarm.alarmRepeat(0,30,0, DailySetup);  // 12:30am every day
    isDailySet = true;
  }
  else{
    Serial.println("DailySetup already set");
  }
}



void LCDTime(){
  String str = "";
  currentDT(str);
  lcd.setCursor(2,0);
  lcd.print(str);
}

void TodaySunrise(){
  String dt ="";
  currentDT(dt);
  Serial.println("");
  Serial.println("-->SUNRISE TRIGGER FIRED: ");
  Serial.print(dt);
  //open actuator
  actuate(OPEN);
}

void TodaySunset(){
  String dt ="";
  currentDT(dt);
  Serial.println("");
  Serial.println("-->SUNSET TRIGGER FIRED: ");
  Serial.print(dt);
  //close actuator
  actuate(CLOSE);
}

void actuate(bool closeIt){
  Serial.println("Actuator starting,..." + (closeIt? String("closing") : String("opening")));
  //Activate the relay one direction, they must be set opposite LOW/HIGH to move the motor
  digitalWrite(forwardsPIN, closeIt ? HIGH : LOW);
  digitalWrite(backwardsPIN, closeIt ? LOW : HIGH);

  //wait until fully extended
  Alarm.delay(80000);

  //Deactivate both relays to brake the motor
  //NOTE: built in limit switches should stop it when fully extended/retracted but this is a failsafe.
  digitalWrite(forwardsPIN, HIGH);
  digitalWrite(backwardsPIN, HIGH);

  Serial.print("Done!");
}


void formatDateTime(tmElements_t &tm, String &dateTime){
  char res[14];
  sprintf(res, "%02d-%02d-%02d %02d:%02d", tm.Month, tm.Day, (tm.Year+1970), tm.Hour, tm.Minute, tm.Second);
  dateTime = res;
}

void currentDT(String &dateTime){
  time_t utc, est;
  utc = now();
  est = usEastern.toLocal(utc);
  tmElements_t tm;
  breakTime(est, tm);
  formatDateTime(tm, dateTime);
}

Have you tried eliminating Strings (capital S) from the program and using strings (lowercase s) instead ?

UKHeliBob: Have you tried eliminating Strings (capital S) from the program and using strings (lowercase s) instead ?

No, can you explain for me why that could possibly matter?

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0

The use of the String class is often the cause of errors that occur after a period of normal operation.

...R

Robin2: It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0

The use of the String class is often the cause of errors that occur after a period of normal operation.

...R

Hmmm OK, based on what you said I did some reading, found a pretty good article on it: https://hackingmajenkoblog.wordpress.com/2016/02/04/the-evils-of-arduino-strings/ So given that is the case, it would seem better to work with char[ ] rather than strings no? Now I tried rewriting that formatDateTime function to take a char[ ] instead of a String but the compiler doesn't like that. How would you optimize this method to be memory conscious yet functional? (basically just want to be able to pass a time in and get it neatly formatted as a series of characters: 01-31-2017 15:45 thanks

I tried rewriting that formatDateTime function to take a char[ ] instead of a String but the compiler doesn't like that.

Are you keeping the code and error messages a secret ?

It's practically done for you in the code you have:

void formatDateTime(tmElements_t &tm, char *res)
{
  sprintf(res, "%02d-%02d-%02d %02d:%02d", tm.Month, tm.Day, (tm.Year+1970), tm.Hour, tm.Minute, tm.Second);
}

UKHeliBob: Are you keeping the code and error messages a secret ?

Not sure what you mean. The whole sketch was posted in the introductory post, there is no other code I'm hiding.

wildbill: It's practically done for you in the code you have:

void formatDateTime(tmElements_t &tm, char *res)
{
  sprintf(res, "%02d-%02d-%02d %02d:%02d", tm.Month, tm.Day, (tm.Year+1970), tm.Hour, tm.Minute, tm.Second);
}

thanks, I'll give that a whirl and see if it fixes my bug

shralp: Not sure what you mean. The whole sketch was posted in the introductory post, there is no other code

But you said in reply #4

shralp: Now I tried rewriting that formatDateTime function to take a char[ ] instead of a String but the compiler doesn't like that.

And I think that's what he is talking about. You have some new code that produced some error messages. If the compiler didn't like it then you did it wrong.

I suggest as a second change to use the F macro for the fixed text in your Serial.print; e.g.

Serial.println(F("Hello"));
// instead of
Serial.println("Hello");

This will reduce the RAM usage leaving more for variables.

Hello shralp,

You can assume that there is a problem with Memory allocation or corruption, but you cannot be sure. I hope for You, you are on the right track.

What you could do if you are not, is first try to know on wich line your code is blocking. That way you have an indication what to look for. You could do this by sending, on various locations of your code, digital values to 8 digital outputs. You can then put 255 flags in your code. Once the arduino blocks, you measure (or look at leds attached to the digital outputs), the tension and with this technique you can narrow down the area's where problems can be present.

See also http://forum.arduino.cc/index.php?topic=43244.0. For writing a byte value to a number of pins.

Best Regards, Johi.