Code Help. Temperature Controlled Relay. Need help with relay-cooling PAST the set temperature threshold it started at

Hey!! So I've looked high and low for several days at other temperature projects and I don't seem to be able to find what I'm looking for. (I know right! There are 78,257,597 Temperature projects on the internet. Anyway I feel justified to add one more so:

Briefly, I want a user to be able to adjust/set the temperature threshold on the LCDKeypad as the current temperature prints to the LCD as well as a few other small unrelated features. Everything is working great!! The only issue I have and need help fixing is I'd like the cooling system relay to kick on at the set temperature BUT not click off immediately when the temperature lowers and == the threshold again. What I'm finding is the relay is "bouncing" and the cooling system is turning on and off and on...

for example: user sets threshold for 76degrees.... Arduino turns on cooling when temperature rises to 76 degrees.... Arduino turns off cooling at 72degrees (not the 76degree threshold).... and then Arduino turns on again at 76 degrees.

So far I've tried several different ideas but since I'm fairly new to coding, I believe I'm just doing something wrong. I have read an entire Arduino Programming book so I do understand many concepts but without a matching example I'm having a hard time applying the concepts for use in my project.

(BTW.. I'm using a LCD/Keypad Shield so all buttons are tied to A0 so I can't use any interupts on the button presses.)

/* Temperature controlled relay using a BME280 sensor and a SainSmart 16x2 LCD w/ Keypad
    The key pad has all buttons tied to A0 with different resistence values on each button to call the desired character
      Programmed by Jerimy
*/

#include <DFR_LCD_Keypad.h> //simply has keypad resistance limits
#include <Wire.h>
#include <LiquidCrystal.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define RELAY  13  // Relay for the A/C unit
#define ALTITUDE 191.0 // Altitude of Brownsville MD  (my town)

int last_key, key;
float setTemp = 74.5; //default temperature so it doesn't start at zero when changing temps
int val = 0; //The value to test our keypad resistence value

const int redLED = 12; // red LED A/C is on
const int greenLED = 11; // green LED A/C is off

float temperature;
float humidity;
float pressure;

Adafruit_BME280 bme; // I2C

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

DFR_LCD_Keypad keypad(A0, &lcd);

//***********************************************************************************************************************

void setup()
{
  lcd.begin(16, 2);
  //Serial.begin(9600);

  lcd.setCursor(0, 0);
  lcd.print("Big Bud Sys By:");
  lcd.setCursor(0, 1);
  lcd.print("DNozz");
  delay(2000);
  lcd.clear(); //clear the screen
  lcd.print("Reading Sensors");
  bool status;
  delay(2000);
  lcd.clear(); //clear the screen

  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(greenLED, LOW); //Green LED On
  digitalWrite(redLED, LOW);    //Red LED Off
  digitalWrite(RELAY, LOW);      //Turn off Relay

  status = bme.begin(0x76);  //The I2C address of the sensor I use is 0x76

  // if m.controller can't "find" the sensor at address 0x76
  if (!status) {
    lcd.print("check wiring");
    //Serial.println("Can't find the BME280 sensor, check wiring!");
    while (1);
  }

  if (status) {
    lcd.print("Sensor Working");
    //Serial.println("Sensor is Working");
    delay(1500);
  }
  lcd.clear();
  lcd.print("Up $ Down change");
  lcd.setCursor(0, 1);
  lcd.print("the temperature");
  delay(3000);
  lcd.clear();
  lcd.print("Hit select for");
  lcd.setCursor(0, 1);
  lcd.print("Humid. & Pres.");
  delay(3000);
  lcd.clear();
}
//***********************************************************************************************************************


void loop()     {

  //val = analogRead(0); // read the value from the sensor connected to A0.
  //Serial.println(val); //Prints the value coming in from the analog sensor for testing.

  getPressure();
  getHumidity();
  getTemperature();

  // Controls the relay and LEDs based on the set temperature
  if (temperature > setTemp)  {
    digitalWrite(RELAY, HIGH);  {
      digitalWrite(redLED, LOW);
      digitalWrite(greenLED, HIGH);
      //      while (setTemp - temperature < 1) {   //THIS IS WHERE I NEED HELP!!!       want to A/C to cool 4 degrees past the set temp to avoid repeated on and off.
      //        digitalWrite(RELAY, HIGH);          // This while statement doesnt work at all!!
      //        digitalWrite(greenLED, HIGH);
      //      }
    }
  }
  else if (temperature <= setTemp)  {
    digitalWrite(RELAY, LOW);
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);
  }

  // General display of current sensor readings
  lcd.setCursor(0, 0);

  String temperatureString = String(temperature, 1);
  lcd.print("Temp: ");
  lcd.print(temperatureString);
  lcd.print((char)223);
  lcd.print("F ");
  lcd.setCursor(0, 1);
  lcd.print("Set Temp= ");
  lcd.print(setTemp, 1);
  lcd.print((char)223);
  lcd.print("F ");


  //looking for changes on A0 (button presses)
  last_key = keypad.get_last_key();
  key      = keypad.read_key();

  if (key != last_key) {      // a button was pushed!

    lcd.clear();
    lcd.setCursor(0, 0);
    switch (key) {
      case KEY_UP:  {
          setTemp += 0.1;
          //lcd.print("Temp Set to: ");
          //lcd.print(setTemp);
          //delay(800);
          break;
        }
      case KEY_DOWN:  {
          setTemp -= 0.1;
          //lcd.print("Temp Set to: ");
          //lcd.print(setTemp);
          //delay(800);
          break;
        }
      case KEY_SELECT:  {
          String humidityString = String(humidity, 0);
          lcd.print("Hum: ");
          lcd.print(humidityString);
          lcd.print("%");
          lcd.setCursor(0, 1);
          lcd.print("Prs: ");
          String pressureString = String(pressure, 2);
          lcd.print(pressureString);
          lcd.print(" hPa");
          delay(5000);
          break;
        }
    }
  }
}

// This is the BME280 setup stuff and believe it has to be outside the loop brackets or it
// throws an error on line 80 "getPressure was not declared in scope"

float getTemperature()
{
  temperature = bme.readTemperature();
  temperature = Celcius2Fahrenheit(temperature);
}

float getHumidity()
{
  humidity = bme.readHumidity();
}

float getPressure()
{
  pressure = bme.readPressure();
  pressure = bme.seaLevelForAltitude(ALTITUDE, pressure);
  pressure = pressure / 100.0F;
}

float Celcius2Fahrenheit(float celsius)
{
  return celsius * 9 / 5 + 32;
}

Here is the link to my code on my Microsoft One Drive: (can edit the file and save to this folder):

I think what you need is a proportional controller (more generally PID). They are smarter that a simple thermostat that turns on and off based on the threshold. I have used the Arduino PID Library
by Brett Beauregard with moderate success. Check its PID_RelayOutput.ino example.

Arduino turns off cooling at 72degrees (not the 76degree threshold)

I don't see how that is possible. Here is what I understand the temperature control loop to be (simplified), assuming the relay does the switching:

  if (temperature > setTemp)  {
    digitalWrite(RELAY, HIGH);  
  }
  else if (temperature <= setTemp)  {
    digitalWrite(RELAY, LOW);
  }

The cooling should be turned off as soon as the temperature reaches setTemp or lower.

Most people use hysteresis to prevent rapid on/off temperature cycling. For example, turn the cooling off only when the temperature is a couple of degrees below setTemp.

  if (temperature > setTemp)  {
    digitalWrite(RELAY, HIGH);  
  }
  else if (temperature < setTemp - 2.0)  {
    digitalWrite(RELAY, LOW);
  }

Do You think I could do it if I assign another variable such as...

float setTemp2 = 0.0

void loop() {

setTemp2 = (setTemp -4);

if (temperature > setTemp)  {
    digitalWrite(RELAY, HIGH);  
  }
// ditch the else statement all together (or maybe I leave it)??
if (temperature <= setTemp2);
     turn stuff off

..but then maybe I need to add a "default" case under my switch statement for when temperature is between the two setTemps??
I truly have no idea what I'm doing! LOL!

Yes, you can.

maybe I need to add a "default" case under my switch statement for when temperature is between the two setTemps?

What should the computer & cooling system do in that case? There are only two options, on and off.

The code I suggested in the previous post is how hysteresis is normally used.

Hint: drop the semicolon below. This statement does nothing.
if (temperature <= setTemp2);

don't you need to turn on and off at the 2 different temperatures?

consider the following

// basic regulator

enum { Off = HIGH, On = LOW };

#define Period    10
#define Thresh    350
#define Margin    10

unsigned long msecLst;

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();

    if ((msec - msecLst) > Period)  {
        msecLst = msec;

        int  val = analogRead (A0);
        Serial.println (val);
        if ((Thresh + Margin) < val)
            digitalWrite (LED_BUILTIN, On);
        else if ((Thresh - Margin) > val)
            digitalWrite (LED_BUILTIN, Off);
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
    pinMode (LED_BUILTIN, Off);
}

Definitely not. Simple hysteresis will do the job. It has worked in HVAC thermostats for decades.

Measurement and control must occur in parallel, you must ensure that the reading of your sensors occurs all the time. For a more efficient control set the control ranges clearly, this example should help you

if ((Temperatura_1>=40) && (Temperatura_1<=60)) 
{
  LEDRELEFan1=1;
  SALIDAPWMFan1= 220;
}
if ((Temperatura_1>=20) && (Temperatura_1<=39.5)) 
{
  SALIDAPWMFan1= 125;
}

Thanks for the help! So Ive not heard of hysteresis and will check that out today. But I'm assuming this will fix my bouncing issue which is nice (and I may still have to use that regardless) but cooling units work better when they've been running so even if the relay didn't bounce at switching times, it'd still be more efficient to have it work as my idea above. (It's how window A/Cs work today).

But to answer you.. I was thinking even if I made a second variable (4 degrees under the 1st) the relay would still kick off as soon as

if (temperature > setTemp)  

is no longer true, regardless if I added "if (temperature <= (setTemp - 4).. So, There is a KEY_NONE variable in this library that is true when no buttons are being pushed. It was late last night when I thought about this idea, (today it doesn't seem so feasible and wondering what I was smoking) but I think my mindset was to try to add this to my switch - case: that if the relay is HIGH leave it HIGH.. if its LOW leave it LOW. Then trying to make it work with some nesting magic.. Lol.. Anyway I have a fresh mind today so I now realize I was making that way more confusing than it had to be and clearly the best way to do that would to simply add a 3rd variable to again leave it on if its on and leave it off its off set to be true if the temperature is in between the two setTemps. Again will still need to use hysteresis. So I will check those out after I get my 3dprinter going and report back to you.. Thanks !!

That is not needed or desired. To see why not , carefully work through the logic of the code suggested in post #3, with a range of input temperature variables.

Sorry Got tied up the past few days trying to get the cases for this thing 3d printed. Anyway I see what you mean and was actually debating whether a 3rd variable was even needed.. I see now when the variable turns the cooler on it doesnt turn it off unless code tells is to. Just because setTemp is no longer true when the temperature falls below the set threshold doesn't mean the relay will switch off and actually wont turn off until "setTemp - 2" is true.. Anyway I got into setting up the hysteresis. Got a few bugs in my code with that part now but not so bad. Thanks again for your help! Cheers!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.