State change detection to monitor button presses and alarms

Greetings Arduino Community!

This is my first post to the forms and I am a complete newbie, forgive me if I post this in the wrong section. I am trying to automate my curtains with the TimeAlarms library (TimeAlarms Library, Run Functions At Specific Times) but I wanted to add a button to manually open/close them as I please. I am starting to get really confused. Here are my issues:

  1. The Alarm.alarmRepeat function is calling my main function (operateCurtain) 3 times in a row with no delay. Also getting same result when I use the onboard LED instead of the stepper (tried to simplify the code).

  2. I wanted to add a state change detection function (curtainState) to keep track of when I press the button as I manually open/close the curtains. This is so when my alarm goes off the curtains don’t try to open if they’re already open. Also, I need to change the state once the alarm goes off (Haven’t added this to the code yet).

  3. The last problem I’m having is with the delays. I am printing the time on the serial monitor but I don’t think there’s a long enough delay to print the time and check the state of the button. If I hold the button down the state will change but it will call the function 3 times (once open then 2 times closed or vice versa depending on the state). Also, if the state is not detected the function (operateCurtain) will run twice with no delay.

I’m starting to think my methodology is wrong. There’s got to be an easier way to do this?

Conceptual design:

  • Use attachInterrupt to call main function (operate Curtain) using button.
  • Define a state change detection function to monitor the state of the curtains each time button is pressed (alarm?).
  • Define main function (operateCurtain) which will either open or close the curtains depending on the state function (curtainState).

Hardware:

  • Arduino Mega 2560
  • A4988 Stepper Driver
  • Nema 17 stepper

Thank you for taking the time! Sorry I tried to keep it short.

#include <TimeAlarms.h>

#include <Time.h>

AlarmID_t alarms[2];
 
#define BAUD (9600)

int x;
const int enablePin = 4;
const int stepPin = 5; 
const int dirPin = 6;

// this constant won't change:
const int buttonPin = 3;    // the pin that the pushbutton is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int curtainState = 1;         // current state of the curtains
int lastCurtainState = 0;     // previous state of the curtains

void setup() {
  pinMode(4,OUTPUT); 
  pinMode(5,OUTPUT); 
  pinMode(6,OUTPUT);
  pinMode(buttonPin, INPUT);

  digitalWrite(4,LOW);
  // initialize serial communication:
  Serial.begin(9600);
  setTime(12,0,0,2,8,2016); // hour,min,sec,day,month,year
  attachInterrupt(digitalPinToInterrupt(buttonPin), operateCurtain, RISING);
}

void loop() {
  displayTime();
  Alarm.delay(1000);
  
  // read the pushbutton input pin:
  curtainState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (curtainState != lastCurtainState) {
    // if the state has changed, increment the counter
    if (curtainState == HIGH) {
      buttonPushCounter++;
      Serial.println(buttonPushCounter);
    }
    delay(50);
  }
  lastCurtainState = curtainState;
  alarms[0] = Alarm.alarmRepeat(12,2,0, operateCurtain);   // at (hour,min,sec) open curtains
  alarms[1] = Alarm.alarmRepeat(12,3,0, operateCurtain);  // at (hour,min,sec) close curtains
}

void displayTime()
{
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void printDigits(int digits)
{
  Serial.print(":");
  if(digits<10)
  Serial.print('0');
  Serial.print(digits);
}
  
void operateCurtain()
{
  if (buttonPushCounter % 2 == 0) {
  digitalWrite(enablePin,LOW);
  digitalWrite(dirPin,LOW);
  Serial.println("Close Curtains");
  for(x = 0; x < 200; x++) // move stepper motor 1 rev counterclockwise
  {
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(1000);
  }
  } else {
  digitalWrite(enablePin,LOW);
  digitalWrite(dirPin,HIGH);
  Serial.println("Open Curtains");
  for(x = 0; x < 200; x++) // move stepper motor 1 rev clockwise
  {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(1000);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(1000);
  }
  }
}

I suspect you will find it easier to understand your own logic if you use meaningful names.

When you press a button you should change buttonState and not curtainState.

curtainState should record whether the curtains are open or closed.

It may well be that somewhere in your program you want to move the curtains because of a change in buttonState and after the curtains have moved you will want to update curtainState.

You may get some ideas from Planning and Implementing a Program

...R