How to program a “silence alarm” button?

Hi all,

I am working on a project for a fridge temperature monitor and alarm.
It consists of a temperature sensor, 3 LEDs, a piezo and a button.
I have set temperature ranges and depending on the measured temperature, the Arduino lights up either the ok LED, the high temp LED, the low temp LED or blinks all LEDs if the sensor returns an error.
Each alarm also has a piezo beeping pattern: keep beeping twice if temperature is too low, beep once if temperature is too high or continuous beep if error.
Maybe this makes it a bit clearer:

Temp ºC measured | Alarm type
=================+===============
Temp<-30         | error
-----------------+---------------
-30<= Temp <= 0  | low temp alarm
-----------------+---------------
0<Temp<=8        | ok
-----------------+---------------
8<Temp<=100      | high temp alarm
-----------------+---------------
Temp>100         | error

My problem is to make the “silence alarm” button work properly. I can silence the alarm alright but if the alarm type changes (ie goes straight from high temperature alarm to error) then the alarm keeps silent because I don't know how to reset the alarmAcknowledged variable. I thought about resetting the alarmAcknowledged variable when the temperature is normal but if it goes from low temp to error or high temp to error and the alarm has been silenced then it stays silent. I want the piezo to resume beeping if the system changes alarm condition even if it has been silenced before.

Any idea how to achieve that?

I want the piezo to resume beeping if the system changes alarm condition even if it has been silenced before.

You need to keep track of what caused the alarm last time, so that you can tell if a new condition wants to trigger the alarm.

How about posting some code?

PaulS:

I want the piezo to resume beeping if the system changes alarm condition even if it has been silenced before.

You need to keep track of what caused the alarm last time, so that you can tell if a new condition wants to trigger the alarm.

How about posting some code?

Sure

int tempPin = 0;
int loLedPin = 8;
int okLedPin = 9;
int hiLedPin = 10;
int piezoPin = 11;
int switchPin = 2;

int analogTempRead;
float celsiusTempRead;
int vSupply;
// PJ - Change 1: Pre declare volt here
float volt;
int piezoPitch = 100;
int loopLen;
float avgCelsiusTempRead;

byte prevBtnState;  // 1 = alarm acknowledged; 0 = alarm not acknowledged
byte currBtnState;
byte curr2BtnState;
byte alarmAck;

void setup() {
  Serial.begin(115200);
  vSupply = 5;
  pinMode(loLedPin, OUTPUT);
  pinMode(okLedPin, OUTPUT);
  pinMode(hiLedPin, OUTPUT);
  pinMode(piezoPin, OUTPUT);
  Serial.println("\n\nReady.");
} 

void loop() 
{
  loopLen = 0;
  // reading sensor and converting to volts
  Serial.println();
  //  analogTempRead = 0;     // PJ - Change 2: This is not needed with the next line change!                  
  //for (int i =0; i<16; i++) // PJ - Change 3: No need to take and average 10 readings
  analogTempRead = analogRead(tempPin);

  //  float volt = analogTempRead * (aref_voltage/10.0)/16368; // 16368 = 1024 * 16
  volt = analogTempRead * (vSupply/1024.0); // change 4: 1024.0, otherwise will calc integer value!!
  Serial.print(volt); 
  Serial.println(" V");


  float celsiusTempRead = ((volt-.5)*100);
  
  
  // debug
  //  celsiusTempRead = 500;

  Serial.println("+=======+===========+");
  Serial.print("|Instant| "); Serial.print(celsiusTempRead, 2); Serial.println("*C   |");
  // Serial.println("+-------+-----------+");
  // Serial.print("|Average| "); Serial.print(avgCelsiusTempRead, 2); Serial.println("*C   |");
  Serial.println("+=======+===========+");
  
  // "silence alarm" button processing
  currBtnState = digitalRead(switchPin);   // start of debounce filter
  delay(5);
  loopLen += 5;  
  curr2BtnState = digitalRead(switchPin);  // end of debounce filter
  if (currBtnState == curr2BtnState) {
    if (currBtnState != prevBtnState) {
      if (currBtnState == LOW) {
        alarmAck = 1;
      }
    }
    prevBtnState = currBtnState;
  }
  
  // returned temperature processing
  if (celsiusTempRead >= -30 && celsiusTempRead <= 0) {
    Serial.println("ALARM: LOW TEMP");
    digitalWrite(loLedPin, HIGH);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, LOW);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
      delay(100);
      analogWrite(piezoPin, 0);
      delay(50);
      analogWrite(piezoPin, piezoPitch);
      delay(100);
      analogWrite(piezoPin, 0);
      loopLen += 250;
    }
  }
  if (celsiusTempRead > 0 && celsiusTempRead <= 8) {
    Serial.println("Temperature OK");
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, HIGH);
    digitalWrite(hiLedPin, LOW);
  }
  if (celsiusTempRead > 8 && celsiusTempRead <= 100) {
    Serial.println("ALARM: HIGH TEMP");
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, HIGH);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
      delay(300);
      analogWrite(piezoPin, 0);
      loopLen += 300;
    }
  }
  if (celsiusTempRead < -30 || celsiusTempRead > 100) {
    Serial.println("ALARM: TEMP OUTSIDE SPECIFICATIONS");
    digitalWrite(loLedPin, HIGH);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, HIGH);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
      delay(300);
      loopLen += 300;
    }
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, LOW);
  }

  // Change 5: Not much point taking readings more than once per second
  delay(1000-loopLen);
}

Hi,

How about adding a simple integer variable to track the current alarm state (0 = no alarm state), something like

const int NO_ALARM = 0;
const int LOW_TEMP = 1;
const int HIGH_TEMP = 2;
const int OUTSIDE_SPEC = 3;
int currentAlarmState = NO_ALARM;

Then when you get an alarm condition check to see currentAlarmState is the same as you're about to report, and do nothing if it is, reset the acknowledgement flag and start beeping if it's new, set currentAlarmState to the new value...rinse, repeat.

Cheers !
Geoff

Thanks, I finally managed to make it work.

I am facing a new issue now: the loop is quite long (2000 ms) and when I want to silence the alarm, I have to press the button a long time before the press is registered by the system. Is there a way to change this and avoid pressing the button for two secs before the software notices/reads the button?

Try using an external interrupt. Check out:

http://arduino.cc/en/Reference/AttachInterrupt
and datasheet chapter 12.

Hmmm, from what I understand any code triggered by an interrupt cannot return any variable?

Right now I have a polling systems that checks the push button. If it detects a press then it switches the alarmAck variable from 0 to 1. I cannot do this with interrupts since I cannot get alarmAck in the loop() scope or am I missing something?

I guess this code has to be run on interrupt:

  // "silence alarm" button processing
  currBtnState = digitalRead(switchPin);   // start of debounce filter
  delay(5);
  loopLen += 5;  
  curr2BtnState = digitalRead(switchPin);  // end of debounce filter
  if (currBtnState == curr2BtnState) {
    if (currBtnState != prevBtnState) {
      if (currBtnState == LOW) {
        alarmAck = 1;
      }
    }
    prevBtnState = currBtnState;
  }

Here is the whole code if it helps:

const int tempPin = 0;
const int loLedPin = 8;
const int okLedPin = 9;
const int hiLedPin = 10;
const int piezoPin = 11;
const int switchPin = 2;

const int NO_ALARM = 0;
const int LOW_TEMP = 1;
const int HIGH_TEMP = 2;
const int OUTSIDE_SPECS = 3;
int currAlarmState = NO_ALARM;
int prevAlarmState = NO_ALARM;

int analogTempRead;
float celsiusTempRead;
// PJ - Change 1: Pre declare volt here
float volt;
const int piezoPitch = 100;
int loopLen;
float avgCelsiusTempRead;

byte prevBtnState;  // 1 = alarm acknowledged; 0 = alarm not acknowledged
byte currBtnState;
byte curr2BtnState;
byte alarmAck;


unsigned long iteration;



void setup() {
  Serial.begin(9600);
  pinMode(loLedPin, OUTPUT);
  pinMode(okLedPin, OUTPUT);
  pinMode(hiLedPin, OUTPUT);
  pinMode(piezoPin, OUTPUT);
  Serial.println("\n\nReady.");
} 

void loop() 
{
  loopLen = 0;
  
  
  iteration++;
  
  
  // reading sensor and converting to volts
  Serial.println("\n\n\n\n");
  //  analogTempRead = 0;     // PJ - Change 2: This is not needed with the next line change!                  
  //for (int i =0; i<16; i++) // PJ - Change 3: No need to take and average 10 readings
  analogTempRead = analogRead(tempPin);

  //  float volt = analogTempRead * (aref_voltage/10.0)/16368; // 16368 = 1024 * 16
  volt = analogTempRead * (5/1024.0); // change 4: 1024.0, otherwise will calc integer value!!
  Serial.print(volt); 
  Serial.println(" V\n");


  celsiusTempRead = ((volt-.5)*100);
  
  //avgCelsiusTempRead = (0.1 * celsiusTempRead + 0.9 * avgCelsiusTempRead);
  
  Serial.print("*** DEBUG MODE, iteration #"); Serial.println(iteration);
  
  if (iteration < 5) {
    avgCelsiusTempRead = 2.0;
  }
  if (iteration >= 5 && iteration < 10) {
    avgCelsiusTempRead = 15.0;
  }
  if (iteration >= 10 && iteration <15) {
    avgCelsiusTempRead = 500.0;
  }
  if (iteration >= 15 && iteration < 20) {
    avgCelsiusTempRead = -4.0;
  }
  if (iteration >= 20 && iteration < 25) {
    avgCelsiusTempRead = 15.0;
  }
  if (iteration >= 25) {
    avgCelsiusTempRead = (0.1 * celsiusTempRead + 0.9 * avgCelsiusTempRead);
  }
  


  
  // debug
  //  celsiusTempRead = 500;

  Serial.println("+=======+===========+");
  Serial.print("|Instant| "); Serial.print(celsiusTempRead, 2); Serial.println("*C\t|");
  Serial.println("+-------+-----------+");
  Serial.print("|Average| "); Serial.print(avgCelsiusTempRead, 2); Serial.println("*C\t|");
  Serial.println("+=======+===========+");
  
  // "silence alarm" button processing
  currBtnState = digitalRead(switchPin);   // start of debounce filter
  delay(5);
  loopLen += 5;  
  curr2BtnState = digitalRead(switchPin);  // end of debounce filter
  if (currBtnState == curr2BtnState) {
    if (currBtnState != prevBtnState) {
      if (currBtnState == LOW) {
        alarmAck = 1;
      }
    }
    prevBtnState = currBtnState;
  }
  
  // returned temperature processing
  if (avgCelsiusTempRead >= -30 && avgCelsiusTempRead <= 0) {
    Serial.println("ALARM: LOW TEMP");
    currAlarmState = LOW_TEMP;
    if (currAlarmState != prevAlarmState) {
      alarmAck = 0;
    }
    digitalWrite(loLedPin, HIGH);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, LOW);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
      delay(100);
      analogWrite(piezoPin, 0);
      delay(50);
      analogWrite(piezoPin, piezoPitch);
      delay(100);
      analogWrite(piezoPin, 0);
      loopLen += 250;
    } 
    prevAlarmState = currAlarmState;
  }
  if (avgCelsiusTempRead > 0 && avgCelsiusTempRead <= 8) {
    Serial.println("Temperature OK");
    currAlarmState = NO_ALARM;
    if (currAlarmState != prevAlarmState) {
      alarmAck = 0;
    }
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, HIGH);
    digitalWrite(hiLedPin, LOW);
    prevAlarmState = currAlarmState;
  }
  if (avgCelsiusTempRead > 8 && avgCelsiusTempRead <= 100) {
    Serial.println("ALARM: HIGH TEMP");
    currAlarmState = HIGH_TEMP;
    if (currAlarmState != prevAlarmState) {
      alarmAck = 0;
    }
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, HIGH);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
      delay(300);
      analogWrite(piezoPin, 0);
      loopLen += 300;
    }
    prevAlarmState = currAlarmState;
  }
  if (avgCelsiusTempRead < -30 || avgCelsiusTempRead > 100) {
    Serial.println("ALARM: TEMP OUTSIDE SPECIFICATIONS");
    currAlarmState = OUTSIDE_SPECS;
    if (currAlarmState != prevAlarmState) {
      alarmAck = 0;
    }
    digitalWrite(loLedPin, HIGH);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, HIGH);
    if (alarmAck == 0) {
      analogWrite(piezoPin, piezoPitch);
    }
    delay(300);
    loopLen += 300;
    analogWrite(piezoPin, 0);
    digitalWrite(loLedPin, LOW);
    digitalWrite(okLedPin, LOW);
    digitalWrite(hiLedPin, LOW);
    prevAlarmState = currAlarmState;
  }
  
  delay(2000-loopLen);
}

To make sure that I'm understanding your question correctly: you're thinking that the value of a variable that is set in an interrupt routine can not be accessed from outside the interrupt?

It's possible as long as you define the variable with global scope instead of defining it inside the interrupt. Look at the attachInterrupt() example and how the interrupt calls blink(), which sets state, which is then used by loop().

I'm new to interrupts as well, but I've just completed a sketch that manipulates minutes and seconds variables inside a timer1 overflow interrupt and those variables still work for the rest of my sketch.

Right now I have a polling systems that checks the push button. If it detects a press then it switches the alarmAck variable from 0 to 1. I cannot do this with interrupts since I cannot get alarmAck in the loop() scope or am I missing something?

alarmAck is already global, just make it volatile too and set it in your interrupt routine.

Better though, get rid of all those delays and use millis to control your timing.

wildbill:

Right now I have a polling systems that checks the push button. If it detects a press then it switches the alarmAck variable from 0 to 1. I cannot do this with interrupts since I cannot get alarmAck in the loop() scope or am I missing something?

alarmAck is already global, just make it volatile too and set it in your interrupt routine.

Better though, get rid of all those delays and use millis to control your timing.

Thanks, it worked and I could even get rid of the debounce. I don't know how to use millis() yet. I read some stuff but I don't understand how to use it to replace delay to have the loop slowed down to be ran every 2000ms. If you care to explain that's nice, if not I'll keep going through the tutorials on tronicstuff.com and eventually get there in a few weeks

1 Like

I think the blink no delay example is about as cut down and simplified as you'll get. It implements the blinking LED sketch without a delay which means you can have the sketch do other things but take action when it needs to toggle the LED state (without interrupts). It's in the standard examples that come with the IDE.

Cheers !
Geoff