Potentiometer controlling thermostat (temperature sensor+relay) freeze issue

Hello,

I’ve been using my Arduino Uno copy to control my beer brewing thermostat for some time. To facilitate temperature change, I lately installed a potentiometer connected to an LCD display to control the temperature setpoint. This has been working well, except for the fact that sometimes when I change the temperature setpoint using the potentiometer the relay doesn’t follow, i.e. if I put the setpoint temperature below the actual temperature the relay doesn’t stop, and if I put the setpoint temperature above the actual temperature the relay doesn’t start. If I press the reset button, the problem disappears, but it is annoying to have to press the reset button every time a setpoint change has been made.

Moreover, sometimes the board and LCD display freeze, causing the relay to be stuck in either on or off position but the displayed temperature to remain the same. I did not have this problem before, when I just manually changed the temperature setpoint in the code instead. Could it be that the code is too extensive or complicated, so the board is overloaded? I’m only using around 40% of the board memory.

To more easily notice the freezing problem, I added a blinking LED to give me an indication of if it decides to freeze.

Below is the code, the first part of which is only to identify the temperature sensor. I apologise if you find it messy, but since I’m not much of a programmer I’ve only used other people’s code and modified it to my purposes. I’m sure there’s a quicker way to do it

Equipment:
Arduino Uno copy from FastTech
Arduino Compatible IIC / I2C Serial 2.5" LCD 1602 Display Module, FastTech (Brand=QAPASS I think)
Keyes 5V Relay Module for Arduino, FastTech
10k potentiometer

#include <OneWire.h>
#include <DallasTemperature.h>

#include <Wire.h>  // Comes with Arduino IDE

#include <LiquidCrystal_I2C.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// arrays to hold device address
DeviceAddress insideThermometer;

#define RELAY1  7
const int LED = 9; // the pin for the LED
float old_tempC = 20; //Value used to store temperature and start heating from the beginning

int Buzzer = 12; //the pin for the buzzer is 12
int Potpin = 2;
int Setpoint;
int val;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

void setup(void)
{
  pinMode(LED, OUTPUT); // LED is as an OUTPUT
  pinMode(RELAY1, OUTPUT);


  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");
  lcd.begin(16, 2); // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight(); // lcd backlight on

  // locate devices on the bus
  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");
  digitalWrite(RELAY1, 1); //To turn relay on in the beginning

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 12 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 10);

  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC);
  Serial.println();
}

// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  val = analogRead(Potpin);
  Setpoint = map(val, 0, 1023, 20, 100);
  lcd.setCursor(0, 0); //Start at character 0 on line 0
  lcd.print("Setpoint:");
  lcd.print(Setpoint);
  lcd.setCursor(0, 1);
  lcd.print("Actual temp:");
  lcd.print(tempC);

  if ((tempC < Setpoint) && (old_tempC >= Setpoint)) {
    digitalWrite(RELAY1, 1);          // Turns ON Relays 1
    tone(Buzzer, 261); //261 is A          //Turn off to remove unnecessary noise
    // Serial.println("Temperature low, start heating");
    lcd.clear();
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Temp low,");
    lcd.setCursor(0, 1);
    lcd.print("start heating");
    digitalWrite(LED, LOW);
    delay(1000);
    noTone(Buzzer);

  }
  else
  {
    digitalWrite(LED, HIGH);
    noTone(Buzzer);
    lcd.clear();
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Setpoint:");
    lcd.print(Setpoint);
    lcd.setCursor(0, 1);
    lcd.print("Actual temp:");
    lcd.print(tempC);

  }
  if ((tempC >= Setpoint) && (old_tempC <= Setpoint))  {
    //  Serial.println("Temperature high, stop heating");
    digitalWrite(RELAY1, 0);         // Turns Relay Off
    lcd.clear();
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Temp high,");
    lcd.setCursor(0, 1);
    lcd.print("stop heating");
    digitalWrite(LED, LOW);
    delay(3000);
  }
  else
  {
    digitalWrite(LED, HIGH);
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Setpoint:");
    lcd.print(Setpoint);
    lcd.setCursor(0, 1);
    lcd.print("Actual temp:");
    lcd.print(tempC);
    digitalWrite(LED, HIGH);
    delay(500);
    digitalWrite(LED, LOW);
  }
  // FOR EMERGENCY TONE:
  if (tempC >= 85)
  {
    tone(Buzzer, 1000);
  }

  old_tempC = tempC;
}

void loop(void)
{
  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  //  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  delay(1000);
  printTemperature(insideThermometer); // Use a simple function to print out the data

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

Thank you for taking your time to read this. If you have any suggestions on how to get the commands to start acting on a change in potentiometer reading immediately, every time, or on how to get rid of the complete relay freeze, please let me know.

Best regards,
Tomas Niemeyer

EDIT: Removed the unnecessary commented out commands, used the Auto Format function. Haven’t looked into any other command instead of PrintTemperature yet.

The printTemperature() function needs to be renamed. It does FAR more than print the temperature.

You need to use Tools + Auto Format to fix your lousy indenting.

You need to get rid of all the commented out code. Clearly, it doesn't do anything. Don't make use wade through all the crap to find the useful stuff.

Thank you PaulS. I tried removing the printTemperature function but then I have no means of getting the temperature and, like I said, I'm not so good at reading code so I couldn't find it in the DallasTemperature library.

Even if I would change the function, do you think it would change the freezing issue (when I change the Setpoint value using the potentiometer, the relay is still fixed in the same position as before the change)? Do you think this problem can be fixed using a code reset function , say 10s after the "val" (potentiometer value) has been changed?

I also removed the commented out parts and used the Auto Format function to make it easier for you guys to read.

Thankful for your help, Tomas

I tried removing the printTemperature function but then I have no means of getting the temperature

Stuff and nonsense. The printTemperature() function was able to get the temperature. Why can't you? Blatantly steal the damned code.

Or, leave the printTemperature() function there, but rename it to doEverythingButTheKitchenSink().

As to your other questions, I don't know. What I do know is that the logic in printTemperature() is hard to follow.

You do one thing if the temperature has dropped below the setpoint, and something else if it hasn't. Then, you do something if the temperature has risen above the setpoint, and something else if it hasn't. I'm confused by that.

I did something similar for a project… using a MAX6675 thermocouple, but the principle’s the same… this code ran once a second.

actTEMP = thermocouple.readCelsius();

 if ((actTEMP +1 ) < targetTEMP) digitalWrite( heater, HIGH);
         

 if ((actTEMP -1 ) >  targetTEMP) digitalWrite( heater, LOW);

that was was for a fairly large heater ( >2kw) with a large thermal mass, so I’m expecting a bit of overshoot… and a bit of hysteresis avoids loads of useless switching.

If you wanted to hold it to 0.01C that’d be a whole different ballgame.

Allan

This is a problem:

  if ((tempC >= Setpoint) && (old_tempC <= Setpoint))  {

The intent appears to be to check whether the temperature is above the setpoint and further, whether this is the first time the arduino has noticed this. That’s fine, until you change the setpoint.

Consider a situation where the setpoint is higher than the measured temperature and the heater has been on for some time. Let’s say the setpoint is 22 and the actual temperature is 20. I’ll assume that it takes a while to heat up and that old_tempC is 19.9.

Now you change the setpoint to 18.

When that if executes, (tempC >= Setpoint) or 20>18 is true, which is good. However, (old_tempC <= Setpoint) or 19.9<=18 is not, so the relay will not be turned off. The logic which is supposed to ensure you only turn off once is defeated by the setpoint change.

You could keep a variable that maintains the state of the heater, is it on or off? Use that instead of old_tempC. i.e. if the beer is too hot and the heater is on, turn it off (being sure to change the heater state variable to reflect this) and display the stop heating stuff on the LCD. Same thing for turning the heater on.

You probably should have a deadband around the setpoint so you’re not constantly turning the heater on & off when you’re close to the setpoint. q.v. hysteresis.

Thank you for your replies. Before reading your post, Wildbill, I finally also realised the problem was the setpoint change in the code. I solved it in a different way, however, using an old_setpoint function inside the old_temperature function present before. Now you have given me another input on how to solve it that I need to consider. One never does seem to finish these things, right? :slight_smile:

Until then, here’s the new code:

#include <avr/wdt.h> //Include Watch Dog Timer
#include <OneWire.h>
#include <DallasTemperature.h>

#include <Wire.h>  // Comes with Arduino IDE
// Get the lcd I2C Library here:
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
// Move any other LCD libraries to another folder or delete them
// See Library "Docs" folder for possible commands etc.
#include <LiquidCrystal_I2C.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// arrays to hold device address
DeviceAddress insideThermometer;

#define RELAY1  7
const int LED = 9; // the pin for the LED
float old_tempC = 20; //Value used to store temperature and start heating from the beginning
int Buzzer = 12; //the pin for the buzzer is 12
int Potpin = 2;
int Setpoint;
int val;
int old_Setpoint;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

void setup(void)
{
    wdt_disable(); //always good to disable it, if it was left 'on' or you need init time
  pinMode(LED, OUTPUT); // LED is as an OUTPUT
  pinMode(RELAY1, OUTPUT);


  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");
  lcd.begin(16, 2); // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight(); // lcd backlight on

  // locate devices on the bus
  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");
  digitalWrite(RELAY1, 1);
  // assign address manually.  the addresses below will beed to be changed
  // to valid device addresses on your bus.  device address can be retrieved
  // by using either oneWire.search(deviceAddress) or individually via
  // sensors.getAddress(deviceAddress, index)
  //insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };

  // Method 1:
  // search for devices on the bus and assign based on an index.  ideally,
  // you would do this to initially discover addresses on the bus and then
  // use those addresses and manually assign them (see above) once you know
  // the devices on your bus (and assuming they don't change).
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  // method 2: search()
  // search() looks for the next device. Returns 1 if a new address has been
  // returned. A zero might mean that the bus is shorted, there are no devices,
  // or you have already retrieved all of them.  It might be a good idea to
  // check the CRC to make sure you didn't get garbage.  The order is
  // deterministic. You will always get the same devices in the same order
  //
  // Must be called before search()
  //oneWire.reset_search();
  // assigns the first address found to insideThermometer
  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");

  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 12 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 10);

  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC);
  Serial.println();
  old_Setpoint = analogRead(Potpin);    //This is probably useless but I haven't attempted to launch the code after having removed it, so it stays for now
  old_Setpoint = map(Setpoint, 0, 1023, 20, 100); //See above
  delay(1000);
  Setpoint = old_Setpoint;
    wdt_enable(WDTO_8S); //enable Watchdog Timer, and set it to 8s
}
void(* resetFunc) (void) = 0; //declare reset function @ address 0


// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  val = analogRead(Potpin);
  Setpoint = map(val, 0, 1023, 20, 100);
  lcd.setCursor(0, 0); //Start at character 0 on line 0
  lcd.print("Setpoint:");
  lcd.print(Setpoint);
  lcd.setCursor(0, 1);
  lcd.print("Actual temp:");
  lcd.print(tempC);
  Serial.println(Setpoint);
  Serial.println(old_Setpoint);


  if (((tempC + 0.5 < Setpoint) && (old_tempC + 0.5 >= Setpoint)) || ((tempC + 0.5 < Setpoint) && (old_tempC + 0.5 >= old_Setpoint)))
  {
    digitalWrite(RELAY1, 1);          // Turns ON Relays 1
    tone(Buzzer, 261); //261 is the tone A         //Turn off to remove unnecessary noise
    lcd.clear();
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Setpoint:");
    lcd.print(Setpoint);
    lcd.setCursor(0, 1);
    lcd.print("Actual temp:");
    lcd.print(tempC);
    delay(1000);
    noTone(Buzzer);

  }
  if (((tempC - 0.5 > Setpoint) && (old_tempC  - 0.5 <= Setpoint)) || ((tempC - 0.5 > Setpoint) && (tempC - 0.5 <= old_Setpoint))) {
    digitalWrite(RELAY1, 0);          //Turns OFF Relay
    noTone(Buzzer);
    lcd.clear();
    lcd.setCursor(0, 0); //Start at character 0 on line 0
    lcd.print("Setpoint:");
    lcd.print(Setpoint);
    lcd.setCursor(0, 1);
    lcd.print("Actual temp:");
    lcd.print(tempC);
  }
if(tempC-5>Setpoint) digitalWrite(RELAY1, 0); //Backup to shut off the relay if the temperature goes above the setpoint and the old_temp function somehow malfunctions
if(tempC+100<Setpoint) tone(Buzzer, 1000); //Backup to give alarm if temperature sensor reading fucks up and display -127 degr C
  

  old_tempC = tempC;
  old_Setpoint = Setpoint;
}

void loop(void)
{
  digitalWrite(LED, HIGH);
  wdt_delay(1000);
  digitalWrite(LED, LOW);
  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  sensors.requestTemperatures(); // Send the command to get temperatures

  printTemperature(insideThermometer); // Use a simple function to print out the data
}
void wdt_delay(unsigned long msec) {
wdt_reset();

while(msec > 1000) {
wdt_reset();
delay(1000);
msec -= 1000;
}
delay(msec);
wdt_reset();
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

Thanks again for the help,
Tomas