Logic for 15-hour/9-hour interval vs. 12-hour interval

I need help with logic relating to time intervals. I have successfully written code that will open and close a door every 12 hours as needed.

In the EVENING, the code checks to see if the door is OPEN and if it is, it will CLOSE the door. If the door is already CLOSED the code does nothing.

In the MORNING, the code checks to see if the door is CLOSED and if it is, it will OPEN the door. If the door is already OPEN the code does nothing.

The code works perfectly on the 12 intervals.

Now that it is summer, I need the door to stay open longer while the sun is out (please don’t suggest sunlight sensor :confused: ). I split the 12-hour interval into two intervals: 15 hours for day and 9 hours for night.

When I run the summertime code, the door CLOSES as expected but then it continuously OPENS and CLOSES.

What am I missing and where did I go wrong?

Thanks!!

12-hour code that works perfectly:

/* Coop_Door_Rotation
  Opens the coop door in one direction and then waits some time to close it.
*/

#include <Wire.h>
#include <Adafruit_MotorShield.h>


// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();

// Connect one stepper w/xxx steps to the bottom shield
Adafruit_StepperMotor *myMotor = AFMS.getStepper(2048, 1);

unsigned long CurrentTime = 0;    // Starts the clock
unsigned long DoorTime = -43170000;       // will store last time door moved, -43170000 begins T-30 seconds
unsigned long DoorStatus = 0;     // 0 = open, 1 = closed
unsigned long DailyStatus = 0;     // 0 = Evening, 1 = Morning

const long DoorInterval = 43200000;           // Door interval; 12 hrs or 43,200,000 milliseconds

int LimitPin = 10;  // Limit switch
int ButtonPin = 11;  // Button

void setup() {
  Serial.begin(9600);
  Serial.println("Coop_Door_Rotation");
  AFMS.begin();  // create with the default frequency 1.6KHz
  myMotor->setSpeed(6);  // 10 rpm

  pinMode(LimitPin, INPUT);    // limit as input
  digitalWrite(LimitPin, HIGH); // turns on pull-up resistor after input
  pinMode(ButtonPin, INPUT);    // button as input
  digitalWrite(ButtonPin, HIGH); // turns on pull-up resistor after input
}

void loop() {

  CurrentTime = millis();

  // DAILY Status Updates
  //If EVENING and CLOSED do nothing and advance the clock
  if (CurrentTime - DoorTime >= DoorInterval && DailyStatus == 0 && DoorStatus == 1)
  {
    DoorTime += DoorInterval;
    DailyStatus = 1; // Next action is in the MORNING
  }

  //If MORNING and OPEN do nothing and advance the clock
  if (CurrentTime - DoorTime >= DoorInterval && DailyStatus == 1 && DoorStatus == 0)
  {
    DoorTime += DoorInterval;
    DailyStatus = 0; // Next action is in the EVENING
  }

  // CLOSE door if EVENING and OPEN
  if (CurrentTime - DoorTime >= DoorInterval && DailyStatus == 0 && DoorStatus == 0) {
    {
      while (digitalRead(LimitPin) == HIGH)  // when pin goes HIGH/Switch is open
        myMotor->step(1, BACKWARD, SINGLE); //run the stepper
    }
    myMotor->release(); //power down the stepper
    DoorTime += DoorInterval;
    DailyStatus = 1; // Next action is in the MORNING
    DoorStatus = 1; // Door is CLOSED
  }
  // OPEN door if MORNING and CLOSED
  if ((CurrentTime - DoorTime >= DoorInterval) && (DailyStatus == 1 || DoorStatus == 1))
  {
    myMotor->step(10250, FORWARD, SINGLE); //run the stepper
    myMotor->release(); //power down the stepper
    DoorTime += DoorInterval;
    DailyStatus = 0; //Next action is in the EVENING
    DoorStatus = 0; // Door is OPEN
  }
  // CLOSE door if BUTTON is pushed and door is OPEN
  if (digitalRead(ButtonPin) == LOW && DoorStatus == 0) {
    {
      while (digitalRead(LimitPin) == HIGH)  // when pin goes HIGH/Switch is open
        myMotor->step(1, BACKWARD, SINGLE); //run the stepper
    }
    myMotor->release(); //power down the stepper
    DoorStatus = 1; // Door is CLOSED
  }
  // OPEN door if BUTTON is pushed and door is CLOSED
  if (digitalRead(ButtonPin) == LOW && DoorStatus == 1) {
    myMotor->step(10250, FORWARD, SINGLE); //run the stepper
    myMotor->release(); //power down the stepper
    DoorStatus = 0; // Door is OPEN
  }
}

Summertime code that does not work as expected:

/* Coop_Door_Rotation_Summer
  Opens the coop door in one direction and then waits some time to close it.
*/

#include <Wire.h>
#include <Adafruit_MotorShield.h>


// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();

// Connect one stepper w/xxx steps to the bottom shield
Adafruit_StepperMotor *myMotor = AFMS.getStepper(2048, 1);

unsigned long CurrentTime = 0;    // Starts the clock
unsigned long DoorTime = -53970000;       // will store last time door moved, -53970000 begins T-30 seconds
unsigned long DoorStatus = 0;     // 0 = open, 1 = closed
unsigned long DailyStatus = 0;     // 0 = Evening, 1 = Morning

const long DoorIntervalDay = 54000000;           // Door interval; 15 hrs or 54,000,000 milliseconds
const long DoorIntervalNight = 32400000;           // Door interval; 9 hrs or 32,400,000 milliseconds

int LimitPin = 10;  // Limit switch
int ButtonPin = 11;  // Button

void setup() {
  Serial.begin(9600);
  Serial.println("Coop_Door_Rotation");
  AFMS.begin();  // create with the default frequency 1.6KHz
  myMotor->setSpeed(6);  // 10 rpm

  pinMode(LimitPin, INPUT);    // limit as input
  digitalWrite(LimitPin, HIGH); // turns on pull-up resistor after input
  pinMode(ButtonPin, INPUT);    // button as input
  digitalWrite(ButtonPin, HIGH); // turns on pull-up resistor after input
}

void loop() {

  CurrentTime = millis();

  // DAILY Status Updates
  //If EVENING and CLOSED do nothing and advance the clock
  if (CurrentTime - DoorTime >= DoorIntervalDay && DailyStatus == 0 && DoorStatus == 1)
  {
    DoorTime += DoorIntervalNight;
    DailyStatus = 1; // Next action is in the MORNING
  }

  //If MORNING and OPEN do nothing and advance the clock
  if (CurrentTime - DoorTime >= DoorIntervalNight && DailyStatus == 1 && DoorStatus == 0)
  {
    DoorTime += DoorIntervalDay;
    DailyStatus = 0; // Next action is in the EVENING
  }

  // CLOSE door if EVENING and OPEN
  if (CurrentTime - DoorTime >= DoorIntervalDay && DailyStatus == 0 && DoorStatus == 0) {
    {
      while (digitalRead(LimitPin) == HIGH)  // when pin goes HIGH/Switch is open
        myMotor->step(1, BACKWARD, SINGLE); //run the stepper
    }
    myMotor->release(); //power down the stepper
    DoorTime += DoorIntervalNight;
    DailyStatus = 1; // Next action is in the MORNING
    DoorStatus = 1; // Door is CLOSED
  }
  // OPEN door if MORNING and CLOSED
  if ((CurrentTime - DoorTime >= DoorIntervalNight) && (DailyStatus == 1 || DoorStatus == 1))
  {
    myMotor->step(10250, FORWARD, SINGLE); //run the stepper
    myMotor->release(); //power down the stepper
    DoorTime += DoorIntervalDay;
    DailyStatus = 0; //Next action is in the EVENING
    DoorStatus = 0; // Door is OPEN
  }
  // CLOSE door if BUTTON is pushed and door is OPEN
  if (digitalRead(ButtonPin) == LOW && DoorStatus == 0) {
    {
      while (digitalRead(LimitPin) == HIGH)  // when pin goes HIGH/Switch is open
        myMotor->step(1, BACKWARD, SINGLE); //run the stepper
    }
    myMotor->release(); //power down the stepper
    DoorStatus = 1; // Door is CLOSED
  }
  // OPEN door if BUTTON is pushed and door is CLOSED
  if (digitalRead(ButtonPin) == LOW && DoorStatus == 1) {
    myMotor->step(10250, FORWARD, SINGLE); //run the stepper
    myMotor->release(); //power down the stepper
    DoorStatus = 0; // Door is OPEN
  }
}
unsigned long DoorTime = -53970000;       // will store last time door moved, -53970000 begins T-30 seconds

It makes no sense to assign a negative value to an unsigned variable.

    DoorTime += DoorIntervalNight;

Adding to unsigned long variables can cause overflow, with undesirable results. Subtraction is always guaranteed to work.

But, really, what is the problem with dropping a couple of dollars for an RTC that makes all this hassle go away?

Your code would be a lot easier to read, debug and maintain if you would use the Time library. Then your constants can be calculated by the compiler. You won't have to sit down with a calculator every time the hours change. :slight_smile:

const byte DaylightHours = 15;
//...
const long DoorIntervalDay = DaylightHours*SECS_PER_HOUR*1000;           // Day door interval;
const long DoorIntervalNight = SECS_PER_DAY*1000 - DoorIntervalDay;           // Night door interval
//...
unsigned long DoorTime = DoorIntervalDay - 30*1000  // Begin at T-30 seconds

To deal with sunrise/sunset times, I used the solar position code from this site http://www.instesre.org/. Now all my clocks know the sun position within fractions of a degree. An RTC is an essential part of any time system, as mentioned above.

I started with a negative value so that the [(CurrentTime - DoorTime >= DoorInterval] will be true in 30 seconds. Is there a better way to do this?

Would an RTC solve my problem? I'm also afraid that the logic in the "summertime" code is flawed. Why does the door continuously open and close in the new version of the code?

Adding to unsigned long variables can cause overflow, with undesirable results. Subtraction is always guaranteed to work.

I understand what you're suggesting but adding to unsigned long variables works in the standard 12-hour code; why wouldn't it work in the "summertime" code?

I started with a negative value so that the [(CurrentTime - DoorTime >= DoorInterval] will be true in 30 seconds. Is there a better way to do this?

Of course there is. Suppose I called you and said meet me at the bar in two hours, and I'll buy drinks. You could note what time it is now, and determine when two hours in the future is, and plan to be there then.

Or, you could note the time now, and periodically, see if it has been two hours since I called.

Doing the latter does not involve trying to store a negative value in an unsigned variable (that can not hold negative values).