State Machine Question

I am working on a sequence of light strings that blink in sequence on the quarter hour and in unison on the hour like the bells in a clock tower.

I have it working in a sketch using the Alarm.delay but that is holding up the rest of the program. I have been trying for the past two days to get a state machine working and I just can't figure it out.

The code below has a simulated clock so I can to testing without waiting a full 15 min. I am using a RTC in the project. This code does not work and I am getting confused.

unsigned long TaskWait;
unsigned long HourTime;
unsigned long MinuteTime;
unsigned long FlashTime;
unsigned long PrintTime;
unsigned long LEDTime;
unsigned long currentmillis;
int lightString;
int Blink;
int mincount;
int min;
int hour = 19;
int minute = 0;

enum LightStates {
  AllOn, AllOff, OneOn, TwoOn, ThreeOn, FourOn, BlinkSequence, Blink15, Blink30, Blink45, Blink0
};

LightStates lState = AllOff;             //we start out in this machine state

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  Serial.println("Start New Program");
  FlashTime = millis();
  TaskWait = millis();
  HourTime = millis();
  PrintTime = millis();
  // start from first step
  lightString = OneOn;
  Blink = 5;
  min = 15;
  lState = AllOff;   //we start out in this machine state
}

//Delay time expired function
boolean CheckTime(unsigned long &lastMillis, unsigned long wait)
{
  //is the time up for this task?
  if (currentmillis - lastMillis >= wait)
  {
    lastMillis += wait;  //get ready for the next iteration
    return true;
  }
  return false;
}
//END of CheckTime()

void loop() {
  currentmillis  = millis(); //leave at top

  //just some code to see if the sketch is blocking
  if (CheckTime(LEDTime, 300UL))
  {
    digitalWrite(13, !digitalRead(13));
    Serial.print("Time");
    Serial.print(hour);
    Serial.print(":");
    Serial.println(minute);
  }
  // END blocking check
  clockCount();
  blinkClock();
  //nightLights();
  // Blink15Min();
}

// perform the expected action(s) for the specified step number
void Blink4Times() {

}

void blinkClock() {
  //Check machine state and do things that must be done accordingly
  switch (lState)
  {
    //***************************
    case AllOff:
        {
          if (CheckTime(PrintTime, 300UL)) {
            PrintTime = millis();
            Serial.print(F("ALL OFF"));
            Serial.println();
          }
        }
      

      break; //end case stateStart:

    //***************************
    case AllOn:    
        {
          if (CheckTime(PrintTime, 300UL))
          {
            PrintTime = millis();
            Serial.print(F("ALL ON"));
            Serial.println();
          }
        }
      
      break; //end case AllOn:

    //***************************

    case blink15:
      if (CheckTime(TaskWait, 30000UL))
      {
        Serial.println(F("Blink 15 min"));
        lstate = BlinkSequence;
        Blink = 4;
      }
    case blink30:
      if (CheckTime(TaskWait, 30000UL))
      {
        Serial.println(F("Blink 30 min"));
        lstate = BlinkSequence;
        Blink = 3;
      }
    case blink45:
      if (CheckTime(TaskWait, 30000UL))
      {
        Serial.println(F("Blink 30 min"));
        lstate = BlinkSequence;
        Blink = 2;
      }
    case blink0:
      if (CheckTime(TaskWait, 30000UL))
      {
        Serial.println(F("Blink 00 min"));
        lstate = BlinkSequence;
        Blink = 1;
      }
  }

case BlinkSequence:
  {
    if (Blink <=4 ) {
    //We will stay in this mState until some things are done
    switch (lightString) {
      case OneOn:
        if (CheckTime(FlashTime, 300UL))
        {
          Serial.println("Blink String 1");
          FlashTime = millis(); //initialize the next wait time
          lightString = TwoOn;
        }
        break;

      case TwoOn:
        if (CheckTime(FlashTime, 300UL))
        {
          Serial.println("Blink String 2");
          FlashTime = millis(); //initialize the next wait time
          lightString = ThreeOn;
        }
        break;

      case ThreeOn:
        if (CheckTime(FlashTime, 300UL))
        {
          Serial.println("Blink String 3");
          FlashTime = millis(); //initialize the next wait time
          lightString = FourOn;
        }
        break;

      case FourOn:
        if (CheckTime(FlashTime, 300UL))
        {
          Serial.println("Blink String 4");
          FlashTime = millis(); //initialize the next wait time
          lightString = OneOn;
          //Blink++;
          nightLights();
        }
        break;

      default:
        // this is just to help debugging
        Serial.println("Unexpected step number!");
        break;
    }
    break;
  } //end case BlinkSequence:
  }
}

}

void nightLights() {
  if ( hour >= 19 && hour <= 24) {
    lState = AllOn;
  }
  else if ( hour >= 0 && hour < 5) {
    lState = AllOn;
  }
  else {
    lState = AllOff;
  }
}


void clockCount ()
{
  if (hour > 24) {
    hour = 0;
  }
  if (minute > 60) {
    minute = 0;
  }
  if (CheckTime(HourTime, 60000UL))
  {
    HourTime = millis(); //initialize the next wait time
    hour++;

  }
  if (CheckTime(MinuteTime, 1000UL))
  {
    MinuteTime = millis(); //initialize the next wait time
    minute++;
  }

}

void checkif15min ()
{
  if ( (minute % 15) == 0) {
    lState = BlinkSequence;
    else {
      nightlights();
    }
  }
}

Here is the series of if statements that currently work in the project with a RTC with the Alarm.delay. I can't figure out how to translate this code into a series of cases.

void blinkClock() {
    if ( (minute() % 15) == 0) {
    Blink15Min();
    }
}

void Blink15Min() {
  if (minute() == 15 && has15run == 0) { 
  n = 1;
  blinkCount();
  has15run = 1;
  has00run = 0;
  }
  else if (minute() == 30 && has30run == 0) {
  n = 2;
  blinkCount();
  has30run = 1;
  } 
  else if (minute() == 45 && has45run == 0) {
  n = 3;
  blinkCount();
  has45run = 1;
  has30run = 0;
  }  
  else if (minute() == 0 && has00run == 0) {
  n = 4;
  blinkCount();
  blinkHour();
  has00run = 1;
  has45run = 0;
  } 
}

void blinkCount() {
for (i = 0; i < n; i++) {
  Blink4Times();
  }
}

void Blink4Times() {
  // Serial.println("Alarm: - turn lights on and off in sequence at 15 minutes");
  turnOffLights ();
  Alarm.delay(1000); // wait one second 
  digitalWrite(lightPin1, LOW);
  Alarm.delay(1000); // wait one second 
  digitalWrite(lightPin1, HIGH);
  digitalWrite(lightPin2, LOW);
  Alarm.delay(1000); // wait one second 
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, LOW);
  Alarm.delay(1000); // wait one second 
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, LOW);
  Alarm.delay(1000); // wait one second 
  digitalWrite(lightPin4, HIGH);
  Alarm.delay(2000); // wait two seconds 
  turnOffLights ();
}

void blinkHour() {
  hr = (hour());
  if (hr > 12) {
    hr == hr - 12;
  }  
  for (i = 0; i < hr; i++) {
    blinkAll();
  }
}

void blinkAll () {
  turnOnLights ();
  Alarm.delay(2000); // wait one second between clock display
  turnOffLights ();
  Alarm.delay(1000); // wait one second between clock display
}

void turnOffLights () {
  digitalWrite(lightPin1, HIGH); // Make sure all lights are off
  digitalWrite(lightPin2, HIGH); // Make sure all lights are off
  digitalWrite(lightPin3, HIGH); // Make sure all lights are off
  digitalWrite(lightPin4, HIGH); // Make sure all lights are off
}

void turnOnLights () {
  digitalWrite(lightPin1, LOW); // Make sure all lights are on
  digitalWrite(lightPin2, LOW); // Make sure all lights are on
  digitalWrite(lightPin3, LOW); // Make sure all lights are on
  digitalWrite(lightPin4, LOW); // Make sure all lights are on
}
void nightLights() {
  if ( hour() > 19 && hour() <= 24) {
      turnOnLights ();
  }
  else if ( hour() >= 0 && hour() < 5) {
      turnOnLights ();
  }  
  else {
      turnOffLights ();
  }  
}

Thank you.

Have a look at how timing is managed using millis() in several things at a time. You could easily use an RTC instead of millis().

...R

You have multiple layers tangled up in each other.

The state machine should be the main program code in the sketch. The state machine can start all kind of things, like a blinking sequency. Those things that are started by the state machine can be in seperate functions and can have an own software timing with millis(). Those function should work on their own, that makes it easier to test them.

The Blink15Min() is typical such a function. That function might be more complex than the whole state machine.
In my opinion, the state machine should issue what Blink15Min() should do (which quarter) and the state machine should decide if it will be started. The Blink15Min() should just perform the action.
The sequence of the Blink15Min() could be in a table, or in a state machine. I think it needs its own software timing with millis(). You might even put Blink15Min() in a seperate file.

Since sequences are running on their own (started by the state machine), you probably need a function to update the sequency. I would use a seperate function for that, for example "Blink15MinUpdate()". So the loop() would contain the state machine and one or more update functions.

Keep the different functionality seperated as much as possible.

Robin2:
Have a look at how timing is managed using millis() in several things at a time.

Thanks, I have looked at that numerous times and it helped a lot. Now I need to get rid of the Alarm.delay in the working code I have posted in the second code box above. It seems that a switch case would be the way to do that but I am having trouble.

Psuedo Case List below: Is this how to structure the possible cases or am I missing the boat entirely?

Case System Idle (Case AllOff)
- possible events, Nighttime, Quarter Hour, or Top of Hour

Case NightTime
 - possible events, Daytime, Quarter Hour, or Top of Hour

Case Quarter Hour
 - possible events  Top of Hour, 15 Min, 30 Min, 45 Min

Case Top of Hour
- Possible events Blink4Times

Case Blink4Times
- Possible Events BlinkAll#Hour

Case BlinkAll#Hour
- Possible Events NightTime, DayTime

Case 15 min
- Possible Events Blink1Time

Case Blink1Time
- Possible Events Daytime, Nighttime

Case 30 Min
- Possible Events Blink2Times

Case Blink2Times
- Possible Events Daytime, Nighttime

Case 45 Min
- Possible Events Blink3Times

Case Blink3Times
- Possible Events Daytime, Nighttime

I don't know, that depends on how the state machine is used.
I see in your sketch the different states, but Blink30 is more like an action than a state.
If something needs to be done every 15 minutes, then perhaps you don't need a state machine after all.

What if you check for the 15 minute boundery, and determine after that which quarter (0,1,2 or 3). After that run the appropriate function.

Peter_n:
... but Blink30 is more like an action than a state.
If something needs to be done every 15 minutes, then perhaps you don't need a state machine after all.

If I do not need one that is fine, I just want it to work.

Peter_n:
What if you check for the 15 minute boundary, and determine after that which quarter (0,1,2 or 3). After that run the appropriate function.

I think that is what I am doing with the revised code below. What do you think?

Since the 2nd code block in the original post works and is physically installed in a project now with blinking lights and lights on at night, I decided to revisit the code to see if I could remove the Alarm.delay. I did so using code from a post by LarryD.

If it will work but is just ugly and won't delay other actions I am ok with that. Then I can leave the blinking behind and move on to finalizing the motor and fan controls.

Is this revised code below wrong?

/*-----( Import needed libraries )-----*/
#include <Wire.h>
#include "RTClib.h"
#include <Time.h>

/*-----( Declare Constants and Pin Numbers )-----*/
const int lightPin1 = 30;    // Light #1
const int lightPin2 = 31;    // Light #2
const int lightPin3 = 32;    // Light #3
const int lightPin4 = 33;    // Light #4
//const int deflateFanPin = 2;     // Fan #1 Deflate
//const int inflateFanPin = 3;     // Fan #2 Inflate
//const int deflateDamperPin = 4;     // Damper Motor #1
//const int inflateDamperPin = 5;     // Damper Motor #2
//const int winchUpPin = 6;     // winch up
//const int winchDownPin = 7;    // winch down

/*-----( Declare objects )-----*/
RTC_DS1307 rtc;    // Create a RealTimeClock object

uint32_t syncProvider() //function which sets up the RTC as the source of external time
{
  return rtc.now().unixtime();
}

unsigned long currentMillis;
unsigned long FlashTime;
int has15run = 0;
int has30run = 0;
int has45run = 0;
int has00run = 0;
int BlinkString;
int n;
int i;
int hr;

enum LightStates {
  AllOn, AllOff, OneOn, TwoOn, ThreeOn, FourOn, FourOff
};

LightStates lState = AllOff;  //we start out in this machine state

void setup() {
  pinMode(lightPin1, OUTPUT);      // sets the digital pins as output
  pinMode(lightPin2, OUTPUT);
  pinMode(lightPin3, OUTPUT);
  pinMode(lightPin4, OUTPUT);
  pinMode(deflateFanPin, OUTPUT);
  pinMode(inflateFanPin, OUTPUT);
  pinMode(deflateDamperPin, OUTPUT);
  pinMode(inflateDamperPin, OUTPUT);
  pinMode(winchUpPin, OUTPUT);
  pinMode(winchDownPin, OUTPUT);

  digitalWrite(lightPin1, HIGH);        // Prevents relays from starting up engaged
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, HIGH);
  digitalWrite(deflateFanPin, HIGH);
  digitalWrite(inflateFanPin, HIGH);
  digitalWrite(deflateDamperPin, HIGH);
  digitalWrite(inflateDamperPin, HIGH);
  digitalWrite(winchUpPin, HIGH);
  digitalWrite(winchDownPin, HIGH);

  Serial.begin(57600); // Set up for Serial Monitor to be able to see this work
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(__DATE__, __TIME__)); //comment this out when the RTC has been set
  setSyncProvider(syncProvider);   // the function to get the time from the RTC

  FlashTime = millis();
}

void loop() {
  unsigned long currentMillis = millis();
  blinkClock();
}

//Delay time expired function
boolean CheckTime(unsigned long &lastMillis, unsigned long wait)
{
  //is the time up for this task?
  if (currentMillis - lastMillis >= wait)
  {
    lastMillis += wait;  //get ready for the next iteration
    return true;
  }
  return false;
}
//END of CheckTime()

void blinkClock() {
  if ( (minute() % 15) == 0) {
    Blink15Min();
  }
}

void Blink15Min() {
  if (minute() == 15 && has15run == 0) {
    n = 1;
    blinkCount();
    has15run = 1;
    has00run = 0;
  }
  else if (minute() == 30 && has30run == 0) {
    n = 2;
    blinkCount();
    has30run = 1;
  }
  else if (minute() == 45 && has45run == 0) {
    n = 3;
    blinkCount();
    has45run = 1;
    has30run = 0;
  }
  else if (minute() == 0 && has00run == 0) {
    n = 4;
    blinkCount();
    blinkHour();
    has00run = 1;
    has45run = 0;
  }
}

void blinkCount() {
  for (i = 0; i < n; i++) {
    Blink4Times();
  }
}

void Blink4Times() {
  // Serial.println("Alarm: - turn lights on and off in sequence at 15 minutes");
  turnOffLights ();
  switch (lState) {
    case OneOn:
      if (CheckTime(FlashTime, 300UL))
      {
        Serial.println("Blink String 1");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin1, LOW);
        lState = TwoOn;
      }
      break;

    case TwoOn:
      if (CheckTime(FlashTime, 300UL))
      {
        Serial.println("Blink String 2");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin1, HIGH);
        digitalWrite(lightPin2, LOW);
        lState = ThreeOn;
      }
      break;

    case ThreeOn:
      if (CheckTime(FlashTime, 300UL))
      {
        Serial.println("Blink String 3");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin2, HIGH);
        digitalWrite(lightPin3, LOW);
        lState = FourOn;
      }
      break;

    case FourOn:
      if (CheckTime(FlashTime, 300UL))
      {
        Serial.println("Blink String 4");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin3, HIGH);
        digitalWrite(lightPin4, LOW);
        lState = FourOff;
        nightLights();
      }
      break;

    case FourOff:
      if (CheckTime(FlashTime, 300UL))
      {
        Serial.println("Turn Off String 4");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin3, HIGH);
        digitalWrite(lightPin4, HIGH);
        lState = OneOn;
        turnOffLights ();
      }

    default:
      // this is just to help debugging
      Serial.println("Unexpected step number!");
      break;
  }
}

void blinkHour() {
  hr = (hour());
  if (hr > 12) {
    hr == hr - 12;
  }
  for (i = 0; i < hr; i++) {
    blinkAll();
  }
}

void blinkAll () {
  turnOffLights ();
  switch (BlinkString) {
    case AllOn:
      if (CheckTime(FlashTime, 1000UL))
      {
        Serial.println("Blink All Strings");
        FlashTime = millis(); //initialize the next wait time
        turnOnLights ();
        BlinkString = AllOff;
      }
      break;

    case AllOff:
      if (CheckTime(FlashTime, 1000UL))
      {
        Serial.println("Turn Off All Strings");
        FlashTime = millis(); //initialize the next wait time
        turnOffLights ();
        BlinkString = AllOn;
      }
      break;

    default:
      // this is just to help debugging
      Serial.println("Unexpected BlinkALL step number!");
      break;
  }
}

void turnOffLights () {
  digitalWrite(lightPin1, HIGH); // Make sure all lights are off
  digitalWrite(lightPin2, HIGH); // Make sure all lights are off
  digitalWrite(lightPin3, HIGH); // Make sure all lights are off
  digitalWrite(lightPin4, HIGH); // Make sure all lights are off
}

void turnOnLights () {
  digitalWrite(lightPin1, LOW); // Make sure all lights are on
  digitalWrite(lightPin2, LOW); // Make sure all lights are on
  digitalWrite(lightPin3, LOW); // Make sure all lights are on
  digitalWrite(lightPin4, LOW); // Make sure all lights are on
}
void nightLights() {
  if ( hour() > 19 && hour() <= 24) {
    turnOnLights ();
  }
  else if ( hour() >= 0 && hour() < 5) {
    turnOnLights ();
  }
  else {
    turnOffLights ();
  }
}

This little bit of code seems like magic since you can basically use it like I was using Alarm.delay in my original code. (At least I think that is how it works.)

//Delay time expired function
boolean CheckTime(unsigned long &lastMillis, unsigned long wait)
{
  //is the time up for this task?
  if (currentMillis - lastMillis >= wait)
  {
    lastMillis += wait;  //get ready for the next iteration
    return true;
  }
  return false;
}
//END of CheckTime()

 void magic () {

if (CheckTime(FlashTime, 1000UL))
      {
        foo();
      }

When data is passed on to the lower function, I would not use a global variable, but a parameter.
I do see a sequence for the blinking, but I don't see a state machine for the main problem flow. I came up with the next code:

boolean done15 = false;   // a flag to indicate if the blinking has already been done

void loop() 
{
  // Check time for a new quarter, use a 5 minute window
  // A delay or more than 5 minutes will skip the code for this quarter.

  int minutes = minute();   // grab current minute just once to avoid update of minute while code is running.
  int minute15 = minutes%15;   // count every quarter
  if ( minute15 >= 0 && minute15 <= 5 && !done15)   // check if within window of 5 minutes
  {
    int quarter = minutes / 15;   // quarter is 0,1,2,3 for 00,15,30,45 minutes.
    MagicBlink (quarter);      // pass on the parameter for the quarter.

    // we have done the code for this quarter.
    done15 = true;
  }
  else
  {
    done15 = false;        // reset the flag outside the window.
  }
}

void MagicBlink (int quarter)
{
    // This function contains all the magic for the blinking.

    switch( quarter)
    {
    case 0:
      break;
    case 1:
      break;
    case 2:
      break;
    case 3:
      break;
    }
}

Peter_n:
When data is passed on to the lower function, I would not use a global variable, but a parameter.
I do see a sequence for the blinking, but I don't see a state machine for the main problem flow.

Peter_n,

Thank you for the code. I just spent the afternoon and most of the evening working with my previous code to combine things into the switch case statement. I was able to get rid of a few functions and consolidate but it is still unwieldy. I tested it on 123D Circuits and it seems to work. I will be able to be in the location of the project tomorrow and will try my code out. Ugly but seems to work.

Now I will spend time with your code because it seems so simple. How do I keep track of the full blinks on the hour? My ugly code does that but I have not used parameters before. I read on the forums that global variables are bad and I understand why but have not known a way not to use them. I will try to tinker with your model.

Programmatically I hope to have the 4 light strings (1, 2, 3, 4) blink sequentially once on the quarter hour, twice on the half hour, three times on the three-quarter hour and four times on the hour. Then on the hour after the sequence of strings blinking is finished I want all 4 strings to blink on and off in unison for the number of times that the hour is. My ugly code seems to do this but I hope to make it simpler and easier to use so I will look to modifying yours. Just have to think about how to get the hour blinks as well as the delays in.

Thanks again.

I posted my revised working code in the next post due to length. Another reason to figure out your model, save space.

Revised working code mentioned in last post, here due to length limit.

unsigned long currentMillis;
unsigned long FlashTime;
unsigned long BlinkTime;
int has15run = 0;
int has30run = 0;
int has45run = 0;
int has00run = 0;
int hasHourRun = 0;
int blinkCount = 4;
int BlinkString;
int n;
int i;
int hr;

//// Remove variables below - Only used for virtual clock sumulation on 123D Circuits

int min;
int hour = 2;
int minute = 58;
unsigned long MinuteTime;
unsigned long PrintTime;
unsigned long HourTime;
unsigned long LEDTime;
unsigned long NightTime;
/// end remove variables

enum LightStates { OneOn, TwoOn, ThreeOn, FourOn, FourOff, StandbyStrings, BlinkAll, NightCheck };
enum BlinkStates { AllOn, AllOff, StandbyBlinkAll };
BlinkStates bState = StandbyBlinkAll;
LightStates lState = NightCheck;  //we start out in this machine state

void setup() {
  pinMode(lightPin1, OUTPUT);      // sets the digital pins as output
  pinMode(lightPin2, OUTPUT);
  pinMode(lightPin3, OUTPUT);
  pinMode(lightPin4, OUTPUT);
  pinMode(deflateFanPin, OUTPUT);
  pinMode(inflateFanPin, OUTPUT);
  pinMode(deflateDamperPin, OUTPUT);
  pinMode(inflateDamperPin, OUTPUT);
  pinMode(winchUpPin, OUTPUT);
  pinMode(winchDownPin, OUTPUT);

  digitalWrite(lightPin1, HIGH);        // Prevents relays from starting up engaged
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, HIGH);
  digitalWrite(deflateFanPin, HIGH);
  digitalWrite(inflateFanPin, HIGH);
  digitalWrite(deflateDamperPin, HIGH);
  digitalWrite(inflateDamperPin, HIGH);
  digitalWrite(winchUpPin, HIGH);
  digitalWrite(winchDownPin, HIGH);

  Serial.begin(9600); // Set up for Serial Monitor to be able to see this work
  pinMode(13, OUTPUT);
  Serial.println("Start New Program");
  // Wire.begin();
  // rtc.begin();
  // rtc.adjust(DateTime(__DATE__, __TIME__)); //comment this out when the RTC has been set
  // setSyncProvider(syncProvider);   // the function to get the time from the RTC
  lState = NightCheck;  //we start out in this machine state
  bState = StandbyBlinkAll;
  FlashTime = millis();
  BlinkTime = millis();

  /// remove below for final sketch
  HourTime = millis();
  MinuteTime = millis();
  PrintTime = millis();
  LEDTime = millis();
  NightTime = millis();
  //end remove

}

//Delay time expired function
boolean CheckTime(unsigned long &lastMillis, unsigned long wait)
{
  //is the time up for this task?
  if (currentMillis - lastMillis >= wait)
  {
    lastMillis += wait;  //get ready for the next iteration
    return true;
  }
  return false;
}
//END of CheckTime()

void loop() {
  currentMillis = millis();
  Blink15Min();
  Blink4Times();
  clockCount ();

  //just some code to see if the sketch is blocking
  if (CheckTime(LEDTime, 500UL))
  {
    digitalWrite(13, !digitalRead(13));
    Serial.print("Time");
    Serial.print(hour);
    Serial.print(":");
    Serial.println(minute);
  }
}
void Blink15Min() {
  if (minute == 15 && has15run == 0) {
    blinkCount = 4;
    lState = OneOn;
    has15run = 1;
    has00run = 0;
  }
  else if (minute == 30 && has30run == 0) {
    blinkCount = 3;
    lState = OneOn;
    has30run = 1;
    has15run = 0;
  }
  else if (minute == 45 && has45run == 0) {
    blinkCount = 2;
    lState = OneOn;
    has45run = 1;
    has30run = 0;
  }
  else if (minute == 0 && has00run == 0) {
    blinkCount = 1;
    lState = OneOn;
    has00run = 1;
    hasHourRun = 0;
    has45run = 0;

  }
}

void Blink4Times() {
  switch (lState) {
    case OneOn:
      turnOnOne ();

      if (CheckTime(FlashTime, 500UL))  {
        Serial.println("Blink String 1");
        FlashTime = millis(); //initialize the next wait time
        lState = TwoOn;
      }
      break;

    case TwoOn:
      turnOnTwo ();
      if (CheckTime(FlashTime, 500UL)) {
        Serial.println("Blink String 2");
        FlashTime = millis(); //initialize the next wait time
        lState = ThreeOn;
      }
      break;

    case ThreeOn:
      turnOnThree ();
      if (CheckTime(FlashTime, 500UL)) {
        Serial.println("Blink String 3");
        FlashTime = millis(); //initialize the next wait time
        lState = FourOn;
      }
      break;

    case FourOn:
      turnOnFour ();
      if (CheckTime(FlashTime, 500UL)) {
        Serial.println("Blink String 4");
        FlashTime = millis(); //initialize the next wait time
        lState = FourOff;
      }
      break;

    case FourOff:
      if (CheckTime(FlashTime, 500UL)) {
        Serial.println("Turn Off String 4");
        FlashTime = millis(); //initialize the next wait time
        digitalWrite(lightPin4, HIGH);
        blinkCount++;
        if (blinkCount <= 4) {
          lState = OneOn;
        }
        else if (blinkCount >= 5 && hasHourRun == 0) {
          blinkHour();
          lState = BlinkAll;
        }
        else {
          lState = NightCheck;
        }
      }
      break;

    case StandbyStrings:
      if (CheckTime(FlashTime, 500UL)) {
        Serial.println("Standby Strings");
      }
      break;

    case BlinkAll:
      {
        switch (bState)
        {
          case AllOn:
            turnOffLights ();
            if (CheckTime(BlinkTime, 500UL))
            {
              Serial.println("Blink All Strings");
              BlinkTime = millis(); //initialize the next wait time
              turnOnLights ();
              bState = AllOff;
            }
            break;

          case AllOff:
            if (CheckTime(BlinkTime, 500UL))
            {
              Serial.println("Turn Off All Strings");
              BlinkTime = millis(); //initialize the next wait time
              turnOffLights ();
              hr = hr - 1;
              if (hr > 0) {
                bState = AllOn;
              }
              else {
                bState = StandbyBlinkAll;
                lState = NightCheck;
                hasHourRun = 1;
              }
            }
            break;
          case StandbyBlinkAll:
          
            break;
        }
      break;
      
      case NightCheck:
        nightLights();
        break;

      default:
        // this is just to help debugging
        Serial.println("Unexpected BlinkALL step number!");
        break;
      }
  }
}

void blinkHour() {
  hr = (hour);
  if (hr > 12) {
    hr == hr - 12;
  }
  lState = BlinkAll;
  bState = AllOn;
  // hasHourRun = 1;
}

void nightLights() {
  if ( hour >= 13 && hour <= 24)
  {
    turnOnLights ();

    if (CheckTime(NightTime, 500UL)) {
      Serial.println("Night Lights On");  // Only for debugging
      NightTime = millis();
    }
  }
  else if ( hour >= 0 && hour <= 6)
  {
    turnOnLights ();
    if (CheckTime(NightTime, 500UL)) {
      Serial.println("Night Lights On");  // Only for debugging
      NightTime = millis();
    }
  }
  else
  {
    turnOffLights ();
    if (CheckTime(NightTime, 500UL)) {
      Serial.println("Night Lights Off");  // Only for debugging
      NightTime = millis();
    }
  }
}

void turnOffLights () {
  digitalWrite(lightPin1, HIGH); // Make sure all lights are off
  digitalWrite(lightPin2, HIGH); // Make sure all lights are off
  digitalWrite(lightPin3, HIGH); // Make sure all lights are off
  digitalWrite(lightPin4, HIGH); // Make sure all lights are off
}

void turnOnLights () {
  digitalWrite(lightPin1, LOW); // Make sure all lights are on
  digitalWrite(lightPin2, LOW); // Make sure all lights are on
  digitalWrite(lightPin3, LOW); // Make sure all lights are on
  digitalWrite(lightPin4, LOW); // Make sure all lights are on
}

void turnOnOne () {
  digitalWrite(lightPin1, LOW);
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, HIGH);
}

void turnOnTwo () {
  digitalWrite(lightPin1, HIGH);
  digitalWrite(lightPin2, LOW);
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, HIGH);
}

void turnOnThree () {
  digitalWrite(lightPin1, HIGH);
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, LOW);
  digitalWrite(lightPin4, HIGH);
}

void turnOnFour () {
  digitalWrite(lightPin1, HIGH);
  digitalWrite(lightPin2, HIGH);
  digitalWrite(lightPin3, HIGH);
  digitalWrite(lightPin4, LOW);
}

You can attach code to sketches to avoid the posting limit.

How to use this forum

My mistake. Thanks for letting me know. I will do that from now on with long code.

Hopefully I am able to figure out some of the suggestions above to make better, cleaner shorter code for my project.

I think I understand Peter_n's code for the first part. I still need to change my code to reflect his efficiency.

I attempted to add in a State Machine cycle for controlling the winch and the inflatable's fans. I made a function with one Switch statement that cycles through.

The Switch statement for the winch and fans still has tons of if's and else's. I am not sure how to reduce them. Any suggestions?

Thank you.

Full code attached below. Winch and fan Switch function embedded for convenience.

void InflateCycle() {
  switch (wState) {

    case FirstInflate:
      inflate ();
      if (CheckTime(InflateWait, 2400000UL)) {
        InflateWait = millis(); //initialize the next wait time
        wState = StayInflated;
      }
      break;

    case StayInflated:
      inflate ();
      if (CheckTime(InflateWait, InflatedPeriod)) {
        InflateWait = millis(); //initialize the next wait time
        wState = Deflate;
      }
      break;

    case Deflate:
      deflate();
      if (CheckTime(InflateWait, DeflatePeriod)) {
        InflateWait = millis(); //initialize the next wait time
        WinchUpCount = 22;
        wState = WinchUpCycle;
      }
      break;

    case WinchUpCycle:
      if (WinchUpCount > 0) {
        if (winchUpState = HIGH) {
          if (CheckTime(InflateWait, UpOffTime)) {
            winchUpState = LOW;  // Turn it on
            digitalWrite(winchUpPin, winchUpState);
            InflateWait = millis(); //initialize the next wait time
          }
        }
        else {
          if (winchDownState = LOW) {
            if   (CheckTime(InflateWait, UpOnTime))  {
              winchUpState = HIGH;  // turn it off
              digitalWrite(winchUpPin, winchUpState);
              WinchUpCount = WinchUpCount - 1;
              InflateWait = millis(); //initialize the next wait time
            }
          }
        }
      }
      else {
        wState = Inflate;
        InflateWait = millis(); //initialize the next wait time
      }
      break;

    case Inflate:
      inflate();
      if (CheckTime(InflateWait, InflatePeriod)) {
        InflateWait = millis(); //initialize the next wait time
        WinchDownCount = 22;
        wState = WinchDownCycle;
      }
      break;

    case WinchDownCycle:
      if (WinchDownCount > 0) {
        if (winchDownState = HIGH) {
          if (CheckTime(InflateWait, DownOffTime)) {
            winchDownState = LOW;  // Turn it on
            digitalWrite(winchDownPin, winchDownState);
            InflateWait = millis(); //initialize the next wait time
          }
        }

        else {
          if (winchDownState = LOW) {
            if (CheckTime(InflateWait, DownOnTime)) {
              winchDownState = HIGH;  // turn it off
              digitalWrite(winchDownPin, winchDownState);
              InflateWait = millis(); //initialize the next wait time
              WinchDownCount = WinchDownCount - 1;
            }
          }
        }
      }
      else {
        wState = StayInflated;
        InflateWait = millis(); //initialize the next wait time
      }
      break;
  } // end switch(wState)
} // end inflate cycle

void inflate() {
  digitalWrite(deflateFanPin, HIGH); // Make sure deflate fan is off
  digitalWrite(deflateDamperPin, HIGH); // Make sure deflate damper is closed
  digitalWrite(inflateFanPin, LOW); // Turn on inflate fan
}

void deflate() {
  digitalWrite(inflateFanPin, HIGH); // Turn off inflate fan
  digitalWrite(deflateFanPin, LOW); // Turn on deflate fan
  digitalWrite(deflateDamperPin, LOW); // Open deflate damper
}

//Delay time expired function
boolean CheckTime(unsigned long &lastMillis, unsigned long wait)
{
  //is the time up for this task?
  if (currentMillis - lastMillis >= wait)
  {
    lastMillis += wait;  //get ready for the next iteration
    return true;
  }
  return false;
}
//END of CheckTime()

_20150609_0822_Light_Strings_without_Alarm_Delay.ino (11.7 KB)

All those if's and else's doesn't have to be a problem. Sometimes that is needed.
It is just hard for me to understand your code, because I would do it in a different way.

P.S.: That is by the way a common problem when a number of people work on a project. Someone is asked to change the code a little, and ends up rewriting that part of code because he/she doesn't understand it, and by rewriting it, new errors are introduced.