Using millis for alarm delay

Hi all,

I found an excellent home alarm system wrote by Steven Bell at the following site:https://create.arduino.cc/projecthub/thestevenbell/arduino-alarm-system-db084a
The problem I have though, is that the alarm alerts only for as long as the PIR outputs a high signal. Ideally, in a home alarm the alarm should alert for a fixed period of time (eg. 5 minutes), or until manually deactivated with a code within the 5 minute period.
What I thus need is for the alarm to latch onto a alerting state for a fixed period of time. I have tried using the millis function, but as a newcomer to that function, I have not been able to make it work (after a week of trying different ways) I realize the sketch employs advanced programming techniques, thus I am at a loss to figure out exactly where to use the millis function, as it should start counting when the alarm is triggered.
I have considered using a discrete timer (555 timer, controlled by the Arduino), but that adds too much complexity to the project, a programmatical method is simpler.
The use of Delay is not practical in this case, as it blocks the disarming of the alarm by entering a code.
All advice will be greatly appreciated.

[code]

/*

  LCD backpack SDA pin to analog pin A4 (on Nano)
    LCD backpack SDL pin to analog pin A5 (on Nano)
    LCD VCC pin to 5V
    LCD GND pin to GND
*/
#include <TwiLiquidCrystal.h>
TwiLiquidCrystal lcd(0x27);
//#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
// LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
//LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);
boolean doInitilizeLCD = true;

// --KEY PAD START ----------------------------------------------------

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
char customKey;
char accessCode[] = "9113";
char keyPadInput[] = "zzzz";
unsigned char inputCounter = 0;
unsigned char inputMaxCount = 4;
// --KEY PAD END ------------------------------------------------------

int ledPin = 11;  // LED on Pin 11, lights up when PIR is active and armed.
int powerledPin = 13; // LED on Pin 13, indicates power on, always HIGH
int pirPin = 10; // Input for HC-S501
int pirValue; // Place to store read PIR Value
int doorPin = A0; // door magnetic switch
int doorValue; // Place to store read magnetic switch Value
int alarmPin = A2; // Relay to switch siren
int statusPin = A3;  // LED on Pin A3, lights up when PIR is active, must be off before arming.

// --Active Buzzer START ----------------------------------------------
int buzzer = 12;//the pin of the active buzzer
boolean shouldBeAlerting = false;
// --Active Buzzer END ------------------------------------------------------

enum ArmedStates {ARMED, DISARMED};
ArmedStates armedState = DISARMED;
short armedTimerInterval = 5;
unsigned long armedTimer = millis();
unsigned long previousMillis1 = 0;
unsigned long startMillis = 0;
unsigned long currentMillis;
//const unsigned long delayTime = 600000;

bool debug = false;

void setup() {

  Serial.begin(9600);
  while (!Serial); // wait for Serial to connect
  Serial.println("ready");

  pinMode(buzzer, OUTPUT); // set up pin as output

  pinMode(ledPin, OUTPUT); // set up pin as output

  pinMode(powerledPin, OUTPUT); // set pin as output

  pinMode(alarmPin, OUTPUT); // set pin as output

  pinMode(pirPin, INPUT); // set pin as input

  pinMode(doorPin, INPUT); // set pin as input

  pinMode(statusPin, OUTPUT); // set pin as output


  lcd.begin(16, 2); // set up and register the LCD
  lcd.backlight();

  //startMillis = millis();  //initial start time
}

void loop() {
  digitalWrite(powerledPin, HIGH);

  if (pirValue == HIGH) {

    digitalWrite(statusPin, HIGH);

  } else {

    digitalWrite(statusPin, LOW);
  }

  initilizeLCD();

  handleKeyPadInput();

  handlePIR();

  alert();

  //handleAlarm;
}

const char* getStateString(enum ArmedStates armedState) {
  switch (armedState)
  {
    case ARMED: return "ARMED";
    case DISARMED: return "DISARMED";
  }
}

//
void initilizeLCD() {
  if (doInitilizeLCD) {

    doInitilizeLCD = false;
    lcd.clear();
    String a = getStateString(armedState);

    if (debug) {
      Serial.println("armedState: " + a);
    }

    lcd.setCursor(0, 0);
    lcd.print(a);
    lcd.setCursor(0, 1);
    lcd.print("PRESS * to ARM.");
  }
}

void handleLCD(bool shouldClearLCD) {
  if (shouldClearLCD) {
    lcd.clear();
  }

  String a = getStateString(armedState);

  if (debug) {
    Serial.println("armedState: " + a);
  }

  lcd.setCursor(0, 0);
  lcd.print(a);
  lcd.setCursor(0, 1);


  lcd.print("DISARM KEY:");
  uint8_t cursorStart = 11;
  lcd.setCursor(cursorStart, 1);
  lcd.cursor();

  // overwrites the LCD screen positions with blank space or keypad input
  for (int i = 0; i < 4; i++) {
    if (keyPadInput[i] == 'z') {
      lcd.setCursor(cursorStart + i, 1);
      lcd.print(" ");
    } else {
      lcd.setCursor(cursorStart + i, 1);
      lcd.print(keyPadInput[i]);
    }
  }
}

void handleKeyPadInput() {
  customKey = customKeypad.getKey();
  if (customKey) {

    Serial.print("customKey: ");
    Serial.println(customKey);

    if (armedState == DISARMED ) {
      if (customKey == '*') {
        armedState = ARMED;
        handleLCD(true);
        //delay (30000);
      }

    } else {
      if (customKey == '*') {
        resetCodeInput();
      } else if (customKey == '#') {
        if (strcmp(keyPadInput, accessCode) == 0 ) {
          Serial.println("Keypad input matches access code");
          // Disarm the system
          resetCodeInput();
          armedState = DISARMED;
          doInitilizeLCD = true;
          initilizeLCD();
        } else {
          resetCodeInput();
        }
      } else {
        if (inputCounter <= 3) {
          keyPadInput[inputCounter] = customKey;
          inputCounter++;
          handleLCD(true);
        } else {
          // do nothing
        }
      }
    }
  }
}


void resetCodeInput() {
  Serial.println("reseting keyPadInput");
  for (int i = 0; i < 4; i++) {
    keyPadInput[i] = 'z';
  }
  inputCounter = 0;
  handleLCD(true);
}

void handlePIR() {
  pirValue = digitalRead(pirPin);
  doorValue = digitalRead(doorPin);
}

void alert() {
  if ((pirValue > 0 && armedState == ARMED ) || (doorValue == LOW && armedState == ARMED )) {
    shouldBeAlerting = true;
    //startMillis = millis;
  }
  //else if (doorValue == LOW && armedState == ARMED ) {
  //shouldBeAlerting = true;
  //}
  else {
    shouldBeAlerting = false;
  }

  if (shouldBeAlerting) {
    Serial.println("shouldBeAlerting");
  }

  handleLed();

  handleBuzz();

  handleAlarm();
}

void handleLed () {
  // handle the alarm LED
  if (shouldBeAlerting) {
    Serial.println("setting LED HIGH");
    digitalWrite(ledPin, HIGH);

  } else {
    // Serial.println("setting LED LOW");
    digitalWrite(ledPin, LOW);

  }
}

void handleBuzz() {
  if (shouldBeAlerting) {
    unsigned char i;
    for (i = 0; i < 10; i++)
    {
      digitalWrite(buzzer, HIGH);
      delay(1);//wait for 1ms
      digitalWrite(buzzer, LOW);
      delay(1);//wait for 1ms
    }
    //output another frequency
    for (i = 0; i < 20; i++)
    {
      digitalWrite(buzzer, HIGH);
      delay(2);//wait for 2ms
      digitalWrite(buzzer, LOW);
      delay(2);//wait for 2ms
    }
  }
}
void handleAlarm () {
  // handle the siren
  if (shouldBeAlerting) {

    Serial.println("siren on");
    startMillis = millis;
    const unsigned long delayTime = 600000;
    if (currentMillis - startMillis >= delayTime); // Alarm to sound for 5 minutes, or until disarmed

    digitalWrite(alarmPin, HIGH);
  }


  else {

    digitalWrite(alarmPin, LOW);
  }
}
//}


[/code]

When the alarm event occurs save the value of millis() as the start time and sound the alarm

Separately in loop(), if the current time minus the start time equals or is greater than the required period or if the reset button is pressed stop the alarm

So I was on the right track as above, which I tried and later commented out?

    startMillis = millis;

That looks right but you do it twice in the sketch. Why is that ?

This is the first time I had to use millis, and I am unfamiliar with the concept...

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

I have been studying it, and ran the examples, which was quite straightforward.

But in this sketch I have tried the following` without result:

void handleAlarm () {
  // handle the siren
  if (shouldBeAlerting) {

    Serial.println("siren on");
   
    const unsigned long delayTime = 600000;
    if (currentMillis - startMillis >= delayTime); // Alarm to sound for 5 minutes, or until disarmed
    digitalWrite(alarmPin, HIGH);
    
  }


  else {

    digitalWrite(alarmPin, LOW);

I do not see why it is not working as intended.

You didn't say what is wrong. Just "not as intended", "without result". Need the detail.

Please post your complete sketch as it is now so that the context can be seen. For instance, where and when is startMillis set and what data types are you using ?

The if statement is in void handle alarm.
Start millis is set in void alert


/*

  LCD backpack SDA pin to analog pin A4 (on Nano)
    LCD backpack SDL pin to analog pin A5 (on Nano)
    LCD VCC pin to 5V
    LCD GND pin to GND
*/
#include <TwiLiquidCrystal.h>
TwiLiquidCrystal lcd(0x27);
//#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
// LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
//LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);
boolean doInitilizeLCD = true;

// --KEY PAD START ----------------------------------------------------

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
char customKey;
char accessCode[] = "9113";
char keyPadInput[] = "zzzz";
unsigned char inputCounter = 0;
unsigned char inputMaxCount = 4;
// --KEY PAD END ------------------------------------------------------

int ledPin = 11;  // LED on Pin 11, lights up when PIR is active and armed.
int powerledPin = 13; // LED on Pin 13, indicates power on, always HIGH
int pirPin = 10; // Input for HC-S501
int pirValue; // Place to store read PIR Value
int doorPin = A0; // door magnetic switch
int doorValue; // Place to store read magnetic switch Value
int alarmPin = A2; // Relay to switch siren
int statusPin = A3;  // LED on Pin A3, lights up when PIR is active, must be off before arming.

// --Active Buzzer START ----------------------------------------------
int buzzer = 12;//the pin of the active buzzer
boolean shouldBeAlerting = false;
// --Active Buzzer END ------------------------------------------------------

enum ArmedStates {ARMED, DISARMED};
ArmedStates armedState = DISARMED;
short armedTimerInterval = 5;
unsigned long armedTimer = millis();
unsigned long previousMillis1 = 0;
unsigned long startMillis = 0;
unsigned long currentMillis;
//const unsigned long delayTime = 600000;

bool debug = false;

void setup() {

  Serial.begin(9600);
  while (!Serial); // wait for Serial to connect
  Serial.println("ready");

  pinMode(buzzer, OUTPUT); // set up pin as output

  pinMode(ledPin, OUTPUT); // set up pin as output

  pinMode(powerledPin, OUTPUT); // set pin as output

  pinMode(alarmPin, OUTPUT); // set pin as output

  pinMode(pirPin, INPUT); // set pin as input

  pinMode(doorPin, INPUT); // set pin as input

  pinMode(statusPin, OUTPUT); // set pin as output


  lcd.begin(16, 2); // set up and register the LCD
  lcd.backlight();

  //startMillis = millis();  //initial start time
}

void loop() {
  digitalWrite(powerledPin, HIGH);

  if (pirValue == HIGH) {

    digitalWrite(statusPin, HIGH);

  } else {

    digitalWrite(statusPin, LOW);
  }

  initilizeLCD();

  handleKeyPadInput();

  handlePIR();

  alert();

  //handleAlarm;
}

const char* getStateString(enum ArmedStates armedState) {
  switch (armedState)
  {
    case ARMED: return "ARMED";
    case DISARMED: return "DISARMED";
  }
}

//
void initilizeLCD() {
  if (doInitilizeLCD) {

    doInitilizeLCD = false;
    lcd.clear();
    String a = getStateString(armedState);

    if (debug) {
      Serial.println("armedState: " + a);
    }

    lcd.setCursor(0, 0);
    lcd.print(a);
    lcd.setCursor(0, 1);
    lcd.print("PRESS * to ARM.");
  }
}

void handleLCD(bool shouldClearLCD) {
  if (shouldClearLCD) {
    lcd.clear();
  }

  String a = getStateString(armedState);

  if (debug) {
    Serial.println("armedState: " + a);
  }

  lcd.setCursor(0, 0);
  lcd.print(a);
  lcd.setCursor(0, 1);


  lcd.print("DISARM KEY:");
  uint8_t cursorStart = 11;
  lcd.setCursor(cursorStart, 1);
  lcd.cursor();

  // overwrites the LCD screen positions with blank space or keypad input
  for (int i = 0; i < 4; i++) {
    if (keyPadInput[i] == 'z') {
      lcd.setCursor(cursorStart + i, 1);
      lcd.print(" ");
    } else {
      lcd.setCursor(cursorStart + i, 1);
      lcd.print(keyPadInput[i]);
    }
  }
}

void handleKeyPadInput() {
  customKey = customKeypad.getKey();
  if (customKey) {

    Serial.print("customKey: ");
    Serial.println(customKey);

    if (armedState == DISARMED ) {
      if (customKey == '*') {
        armedState = ARMED;
        handleLCD(true);
        //delay (30000);
      }

    } else {
      if (customKey == '*') {
        resetCodeInput();
      } else if (customKey == '#') {
        if (strcmp(keyPadInput, accessCode) == 0 ) {
          Serial.println("Keypad input matches access code");
          // Disarm the system
          resetCodeInput();
          armedState = DISARMED;
          doInitilizeLCD = true;
          initilizeLCD();
        } else {
          resetCodeInput();
        }
      } else {
        if (inputCounter <= 3) {
          keyPadInput[inputCounter] = customKey;
          inputCounter++;
          handleLCD(true);
        } else {
          // do nothing
        }
      }
    }
  }
}


void resetCodeInput() {
  Serial.println("reseting keyPadInput");
  for (int i = 0; i < 4; i++) {
    keyPadInput[i] = 'z';
  }
  inputCounter = 0;
  handleLCD(true);
}

void handlePIR() {
  pirValue = digitalRead(pirPin);
  doorValue = digitalRead(doorPin);
}

void alert() {
  if ((pirValue > 0 && armedState == ARMED ) || (doorValue == LOW && armedState == ARMED )) {
    shouldBeAlerting = true;
    startMillis = millis;
  }
  //else if (doorValue == LOW && armedState == ARMED ) {
  //shouldBeAlerting = true;
  //}
  else {
    shouldBeAlerting = false;
  }

  if (shouldBeAlerting) {
    Serial.println("shouldBeAlerting");
  }

  handleLed();

  handleBuzz();

  handleAlarm();
}

void handleLed () {
  // handle the alarm LED
  if (shouldBeAlerting) {
    Serial.println("setting LED HIGH");
    digitalWrite(ledPin, HIGH);

  } else {
    // Serial.println("setting LED LOW");
    digitalWrite(ledPin, LOW);

  }
}

void handleBuzz() {
  if (shouldBeAlerting) {
    unsigned char i;
    for (i = 0; i < 10; i++)
    {
      digitalWrite(buzzer, HIGH);
      delay(1);//wait for 1ms
      digitalWrite(buzzer, LOW);
      delay(1);//wait for 1ms
    }
    //output another frequency
    for (i = 0; i < 20; i++)
    {
      digitalWrite(buzzer, HIGH);
      delay(2);//wait for 2ms
      digitalWrite(buzzer, LOW);
      delay(2);//wait for 2ms
    }
  }
}
void handleAlarm () {
  // handle the siren
  if (shouldBeAlerting) {

    Serial.println("siren on");
   
    const unsigned long delayTime = 600000;
    if (currentMillis - startMillis >= delayTime); // Alarm to sound for 5 minutes, or until disarmed
    digitalWrite(alarmPin, HIGH);
    
  }


  else {

    digitalWrite(alarmPin, LOW);
  }
}
//}

It is not keeping the pin high for the delayTime.

Hmmm. It's quite a long sketch. Here's the correct logic, why not you figure out whether you're doing it or not:

  • any time the PIR is active, record the current millis() time
  • any time the current time minus the recorded time exceeds some interval value, turn off the alarm, else turn on the alarm.

You're lucky, it's actually one of the simplest millis() techniques...

Like this?
I am not asking you to code for me, just for a little guidance...

[code]
void alert() {
  if ((pirValue > 0 && armedState == ARMED ) || (doorValue == LOW && armedState == ARMED )) {
    shouldBeAlerting = true;
    startMillis = millis;
     const unsigned long delayTime = 600000;
    if (currentMillis - startMillis >= delayTime); // Alarm to sound for 5 minutes, or until disarmed
  }
  //else if (doorValue == LOW && armedState == ARMED ) {
  //shouldBeAlerting = true;
  //}
  else {
    shouldBeAlerting = false;
  }

[/code]

Waaaaaay overcomplicated. Read my post again.

I can tell you at least, you should factor this to remove the repetition.

  if ((pirValue > 0) || (doorValue == LOW) && armedState == ARMED ) {//|| ( && armedState == ARMED )) {

I suspect the problem lies here:

[code]
void alert() {
  if ((pirValue > 0) || (doorValue == LOW) && armedState == ARMED ) {//|| ( && armedState == ARMED )) {
    shouldBeAlerting = true;
     const unsigned long delayTime = 600000;
    startMillis = millis;
    if (currentMillis - startMillis >= delayTime); // Alarm to sound for 5 minutes, or until disarmed
    digitalWrite(alarmPin, HIGH);

[/code]

When the sketch reaches that point on the next iteration, shouldBeAlerting becomes FALSE, and the timer does not run.

Ok gents, @UkHeliBob and @anon57585045, I thank you for your responses.
I have written several sketches by myself, but this is the first time I tried to adapt another's sketch to my requirements. I realize that is not within my scope of experience.
I grew up with analogue electronics,(op-amps, 555 timers, comparators et all), and it will be much faster for me to build an alarm using those components, as I am going away on holiday on Thursday.

I do greatly appreciate your advice.
Regards from an old-timer, and have a great festive season.

Cheers guys :+1:

This does not call the millis function:

Instead it gets the address of the millis function. Parentheses () are needed on all function calls.

Thank you. I will research the topic further.