Interrupt Timers?

Hi, I am writing some code to control a compressor based on temperature. After turning the compressor off, it needs at least 3 minutes for the pressure to stabilize before you can turn it back on. The hysteresis is usually enough for this, but I wan't to make it fool-proof.

I have been told I can use interrupt timers so the uC can continue reading the temps, but also be timing. Basically, when the temp reaches the hysteresis temp, it waits 3 minutes then allows the digitalWrite.

Could someone please help me out with it? I've been doing reading on the interrupt timers, but not having a larger background in microcontrollers is becoming a problem trying to understand everything ..

// Variables will change:
int buttonStateUp; 
int buttonStateDown;   
int lastButtonStateUp = LOW;   
int lastButtonStateDown = LOW; 
long lastDebounceTimeUp = 0; 
long lastDebounceTimeDown = 0; 
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int mintemp = 10;

int i = 0;

int sample = 30;
float rawArray[30];
int raw;

float averageRaw;
float sumRaw;

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  pinMode(13, OUTPUT);
}

void loop() {    
  int upBtn = digitalRead(2);
  int downBtn = digitalRead(3);
  
  
  float raw = analogRead(3);    
  rawArray[i] = raw;

  for (int i = 0; i < 30; i++) 
    sumRaw = sumRaw + rawArray[i];

  averageRaw = sumRaw/sample;
  float volts = averageRaw/204.8; 
  // 1024/5 = 204.8  
  //float volts = raw/204.8; 
  float kelvin = (volts * 1000)/10;
  float celcius = kelvin - 273.15; 
  Serial.print(celcius);
  Serial.print("C");
  Serial.println(" ");


  i++;
  if (i >= sample) {
    i = 0;
  }
  sumRaw = 0;
  delay(200);
  

  // If the switch changed, due to noise or pressing:
  if (upBtn != lastButtonStateUp) {
    // reset the debouncing timer
    lastDebounceTimeUp = millis();
  } 
  
  if ((millis() - lastDebounceTimeUp) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateUp = upBtn;
  }
  
if (buttonStateUp == HIGH) {
  mintemp++;
}
  lastButtonStateUp = upBtn;


  // If the switch changed, due to noise or pressing:
  if (downBtn != lastButtonStateDown) {
    // reset the debouncing timer
    lastDebounceTimeDown = millis();
  } 
  
  if ((millis() - lastDebounceTimeDown) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateDown = downBtn;
  }
  
if (buttonStateDown == HIGH) {
  mintemp--;
}
  lastButtonStateDown = downBtn;

  
  
  if (celcius < mintemp) {
    digitalWrite(13, LOW);
  }
  int hysteresis = mintemp + 6;
  if (celcius > hysteresis){
    // NEED A 3 MINUTE DELAY BEFORE THIS DIGITAL WRITE
    digitalWrite(13, HIGH);
  }
  delay(200);                     
}

Cheers,
Dan :slight_smile:

You really don't need interrupts for these sorts of timescales.
Have a look at the Blink Without Delay tutorial.

I tried implementing the delay code, but the relay still switches on instantly ...

// Variables will change:
int buttonStateUp; 
int buttonStateDown;   
int lastButtonStateUp = LOW;   
int lastButtonStateDown = LOW; 
long lastDebounceTimeUp = 0; 
long lastDebounceTimeDown = 0; 
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int mintemp = 10;

int i = 0;

int sample = 30;
float rawArray[30];
int raw;

float averageRaw;
float sumRaw;
long previousMillis = 0; 
long interval = 10000; 

#include <LiquidCrystal.h>
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  pinMode(13, OUTPUT);
  pinMode(7, OUTPUT);
  lcd.begin(2, 16);
}

void loop() {    
  int upBtn = digitalRead(2);
  int downBtn = digitalRead(3);
  
  
  float raw = analogRead(3);    
  rawArray[i] = raw;

  for (int i = 0; i < 30; i++) 
    sumRaw = sumRaw + rawArray[i];

  averageRaw = sumRaw/sample;
  float volts = averageRaw/204.8; 
  // 1024/5 = 204.8  
  //float volts = raw/204.8; 
  float kelvin = (volts * 1000)/10;
  float celcius = kelvin - 273.15; 
  lcd.clear();
  lcd.print("Water:");
  lcd.print(" ");
  lcd.print(celcius);
  lcd.print("C");
  Serial.println(celcius);


  i++;
  if (i >= sample) {
    i = 0;
  }
  sumRaw = 0;
  

  // If the switch changed, due to noise or pressing:
  if (upBtn != lastButtonStateUp) {
    // reset the debouncing timer
    lastDebounceTimeUp = millis();
  } 
  
  if ((millis() - lastDebounceTimeUp) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateUp = upBtn;
  }
  
if (buttonStateUp == HIGH) {
  mintemp++;
}
  lastButtonStateUp = upBtn;


  // If the switch changed, due to noise or pressing:
  if (downBtn != lastButtonStateDown) {
    // reset the debouncing timer
    lastDebounceTimeDown = millis();
  } 
  
  if ((millis() - lastDebounceTimeDown) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateDown = downBtn;
  }
  
if (buttonStateDown == HIGH) {
  mintemp--;
}
  lastButtonStateDown = downBtn;

  
  
  if (celcius < 25) {
    digitalWrite(7, LOW);
  }
  int hysteresis = mintemp + 6;
  if (celcius > 27){
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis; 
    digitalWrite(7, HIGH);
  }
  }
  delay(200);                     
}

Reading through the code shows your logic is all wrong. You use the variable i defined in two places in the loop(), while it is not wrong it is never a good idea because it is confusing. Therefore you are getting the wrong result from your running average. There is probably other stuff going wrong as well.

Simplify it, use print statements to see what values you are getting at crucial points in the program and trace where what you think is happening diverges from what actually is.

The code is actually working beautifully, it's just the 3 minute delay that I can't seem to figure out.

if(currentMillis - previousMillis > interval) {

You initialise "previousMillis" to zero, so after 10 seconds from reset, this comparison will be true.

BTW, Anders spelled his surname "Celsius" :wink:

Is there a way I can have it only store millis() once when the if is called, not every time it loops through?

Cheers,
Dan

You could implement it as a simple state-machine.

Arghh! I tried starting on some code to have it only read the millis once per if, but got stuck as to where to have it reset.

int readmillis = 0;
<snip>
  if (celcius > hysteresis){
    if (readmillis == 0){ 
    unsigned long currentMillis = millis();
    readmillis = 1;
    if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    digitalWrite(7, HIGH);
  }
<snip>

Instead of previousMillis and currentMillis as names, try using more meaningful names. What exactly is previousMillis supposed to record? Is it when the relay was last opened? Last closed? Something else?

Give the variable a more meaningful name, based on it's true purpose, and it will become obvious where you need to SET it.

The problem with your code is not with determining "now" (as you are storing it in currentMillis). It is with initializing "then" (previousMillis).

The types of previousMillis and interval may be issues, too. It's important to store data returned by a function in a variable that matches the type returned by the function, to prevent truncation and data loss.

Got it working, with help from people in the Arduino chat :slight_smile:

// Variables will change:
int buttonStateUp;
int buttonStateDown;
int lastButtonStateUp = LOW;
int lastButtonStateDown = LOW;
long lastDebounceTimeUp = 0;
long lastDebounceTimeDown = 0;
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int mintemp = 10;

int i = 0;

int sample = 30;
float rawArray[30];

bool isStabilising = true;
unsigned long startTime;

int STABILISING_INTERVALL = 10000;

float averageRaw;
float sumRaw;

#include <LiquidCrystal.h>
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(7, OUTPUT);
  lcd.begin(2, 16);
}

void loop() {
  int upBtn = digitalRead(2);
  int downBtn = digitalRead(3);


  float raw = analogRead(3);
  rawArray[i] = raw;

  for (int i = 0; i < 30; i++)
    sumRaw = sumRaw + rawArray[i];

  averageRaw = sumRaw/sample;
  float volts = averageRaw/204.8;
  // 1024/5 = 204.8
  //float volts = raw/204.8;
  float kelvin = (volts * 1000)/10;
  float celcius = kelvin - 273.15;
  lcd.clear();
  lcd.print("Water:");
  lcd.print(" ");
  lcd.print(celcius);
  lcd.print("C");
  Serial.println(celcius);


  i++;
  if (i >= sample) {
    i = 0;
  }
  sumRaw = 0;


  // If the switch changed, due to noise or pressing:
  if (upBtn != lastButtonStateUp) {
    // reset the debouncing timer
    lastDebounceTimeUp = millis();
  }

  if ((millis() - lastDebounceTimeUp) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateUp = upBtn;
  }

if (buttonStateUp == HIGH) {
  mintemp++;
}
  lastButtonStateUp = upBtn;


  // If the switch changed, due to noise or pressing:
  if (downBtn != lastButtonStateDown) {
    // reset the debouncing timer
    lastDebounceTimeDown = millis();
  }

  if ((millis() - lastDebounceTimeDown) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonStateDown = downBtn;
  }

if (buttonStateDown == HIGH) {
  mintemp--;
}
  lastButtonStateDown = downBtn;

Serial.println(mintemp);

  if (celcius < mintemp) {
    digitalWrite(7, LOW);
    isStabilising = true;
  }
  int hysteresis = mintemp + 6;
  if (celcius > hysteresis)
  {
    if (isStabilising == true)
    {
        startTime = millis();
        isStabilising = false;
    }
    else
    {
        if((millis() - startTime) > STABILISING_INTERVALL) {
            digitalWrite(7, HIGH);
        }
    }
  }
  delay(200);
}

Got it working

Are you sure about that?
What if you change the value of "sample" to 50?

I'd really think twice about your use of "i", globally and locally..