Temp control - code help

Hi all,
This is my first post here, so Hi to all!

I am still a noob at coding, but i am learning, but being a DIY guy and a home brewer, i decided to built my own version of the STC-1000 temp controller!
So basically i need to control fermentation temp, if the temp drops below set temp, energize the heat relay, if too warm energize the cooling relay. Id like to insert time delays so the fridge doesn’t work overtime!

below is the code i have, this is code i got from the net, but i altered it a bit. it works kinda OK, but the problems i have:

  1. fridge_relay output pin (5) seems to be floating, it switches on and off during simulation.
  2. the (“H”) symbol for heating, doesnt display when the heating relay is switched.
  3. At times both relays will energize??

i am using proteus 8 to simulate.

any advice will be appreciated!

thanks!

Fermentation_Temp_Control.ino (2.73 KB)

Please post your code.
Use code tags.

Why are you using Proteus?

I had proteus on my pc, and thats what i learned on initially, can you suggest a better sim?
i did get the code from the internet, i just adjusted it to have a second output relay (heater).

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

volatile long lastDebounceTimeU = 10;
volatile long lastDebounceTimeD = 10;
#define ONE_WIRE_BUS 4
#define debounceDelay 300

byte buttonInterruptUp = 0;
byte buttonInterruptDown = 1;

LiquidCrystal lcd(12, 11, 10, 9, 8, 6);
double currentTemp = 25.0;
double setTemp = 20.00;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

const int COOLING = 1;
const int RESTING = 0;
const double precision = 0.3; //.5 degrees precision

int fridge_status;
int startup_time = 1000;

const int fridge_relay = 5;
const int heater_relay = 7;

boolean tooWarm(){
return (currentTemp >= (setTemp));
}

boolean tooCold(){
return (currentTemp <= setTemp); //temp keeps dropping after temp is reached
}

boolean tempCorrect(){
return (currentTemp == setTemp);
}

void setup(){
digitalWrite(fridge_relay, LOW); //start with fridge off

lcd.begin(16, 2);
//Serial.begin(9600);
lcd.print("Starting...");

attachInterrupt(buttonInterruptUp, tempUp, RISING);
attachInterrupt(buttonInterruptDown, tempDown, RISING);
sensors.begin();
pinMode(fridge_relay, OUTPUT);
pinMode(heater_relay, OUTPUT);
fridge_status = RESTING;
} 

void loop() {
delay(startup_time); //allow lcd to be initialized first
startup_time = 10;

getTemp();
lcdPrint();

if(tooWarm()){
delay(3000);
digitalWrite(fridge_relay, HIGH);
fridge_status = COOLING;
}

if(tooCold()){
delay(1000);
digitalWrite(heater_relay, HIGH);
fridge_status = RESTING;
}

if(tempCorrect()){
digitalWrite((heater_relay), LOW);
digitalWrite((fridge_relay), LOW);
}
}

void tempUp(){//dont feel like adding a debouncing circuit.. this is good enough
if((millis()-lastDebounceTimeU) > debounceDelay){
lastDebounceTimeU = millis();
if(setTemp < 100.00){ //was 100
setTemp += 0.5;
lcdPrint();
}
}
}

void tempDown(){
if((millis()-lastDebounceTimeD) > debounceDelay){
lastDebounceTimeD = millis();
if(setTemp > 1.00){   //was 30
setTemp -= 0.5;
lcdPrint();
}
}
}

void getTemp(){
sensors.requestTemperatures();
double t = sensors.getTempCByIndex(0);
if(t > 0 && t < 120){ //faulty wiring causes weird temperatures..
currentTemp = t;
} 
}

void lcdPrint(){
lcd.begin(16,2); //sometimes lcd goes blank, i guess this fixes it....

lcd.clear();
lcd.setCursor(0,0);
lcd.print("TEMP");
lcd.setCursor(5,0);
lcd.print(currentTemp);
lcd.setCursor(10,0);
lcd.print((char)223);
lcd.setCursor(11,0);
lcd.print("C");

lcd.setCursor(1,1);
lcd.print("SET");
lcd.setCursor(5,1);
lcd.print(setTemp);
lcd.setCursor(10,1);
lcd.print((char)223);
lcd.setCursor(11,1);
lcd.print("C");

//print a "*" on the display if the fridge is on
lcd.setCursor(15,1);
if(fridge_status == COOLING){
lcd.print("C");
}
if(fridge_status == RESTING){
lcd.print("H");
}
if(tempCorrect){
lcd.print(" ");
}
lcd.display();
}

I've never used a sim, so no, I'm afraid I can't.

Why are you using interrupts?

I take it there is an easier way, without interrupts?

Polling.
But you have to get rid of all the delay()s.

Actually, even with interrupts, you have to get rid off delays.

I take it there is an easier way, without interrupts?

Do you need faster than fraction of millisecond reactions? For what you’re doing I doubt that 10 second response would be too slow.

If you don’t use delay() or a library that forces the rest of the code to wait, you don’t need interrupts. The temperature readings are probably your biggest wait.

With both heater and cooler maybe you should have 2 points each to cover thermal inertia of the total system. Does the cooler use refrigerant in tubes that will still be cooling the tank after the compressor is shut off? How long between the heat start/stop and temperature change start/stop?

The hot points at the low end and cooling points after a gap above, at least 3 or 4C between?

Keep track of heater on/off and cooler on/off with 2 variables.

If the temperature is <= heater low
{
If the heater is off, turn the heater on.
}
Else if the temperature is >= heater high
{
If the heater is on, turn the heater off
}

If the temperature is <= cooler low
{
If the cooler is on, turn the cooler off.
}
Else if the temperature is >= cooler high
{
If the cooler is off, turn the cooler on
}

The structures above could use fewer lines. I wanted to make it clear and it will compile the same.

I wonder how many Peltier wafers it’d take to do fine control to keep temperature in the groove.

I used to know an online Oryx who is an aerodynamics PhD.

ok so im removing the interrupts.
Now im struggeling with the setpoint increase and decrease buttons.

this is what i coded:

const int SetTemp_Up = 2;
const int SetTemp_Down = 3;

pinMode(SetTemp_Up, INPUT);
pinMode(SetTemp_Down, INPUT);

void tempDown(){
if(digitalRead(SetTemp_Down) == HIGH){ 
setTemp -= 0.5;
lcdPrint();
}
}

needless to say, its not adjusting the temp

Needless to say, that code doesn't compile, so can't run

its just a part of the code concerning the buttons.
heres the whole thing, it does compile

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

#define ONE_WIRE_BUS 4



LiquidCrystal lcd(12, 11, 10, 9, 8, 6);
double currentTemp = 25.0;
double setTemp = 20.00;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

const int COOLING = 1;
const int RESTING = 0;
const double precision = 0.3; //.5 degrees precision

int fridge_status;
int startup_time = 1000;

const int fridge_relay = 5;
const int heater_relay = 7;
const int SetTemp_Up = 2;
const int SetTemp_Down = 3;

boolean tooWarm(){
return (currentTemp >= (setTemp));
}

boolean tooCold(){
return (currentTemp <= setTemp); //temp keeps dropping after temp is reached
}

boolean tempCorrect(){
return (currentTemp == setTemp);
}

void setup(){
digitalWrite(fridge_relay, LOW); //start with fridge off

lcd.begin(16, 2);
//Serial.begin(9600);
lcd.print("Starting...");

sensors.begin();
pinMode(fridge_relay, OUTPUT);
pinMode(heater_relay, OUTPUT);
pinMode(SetTemp_Up, INPUT);
pinMode(SetTemp_Down, INPUT);
fridge_status = RESTING;
} 

void loop() {
delay(startup_time); 
startup_time = 10;

getTemp();
lcdPrint();

if(tooWarm()){
digitalWrite(fridge_relay, HIGH);
fridge_status = COOLING;
}

if(tooCold()){
digitalWrite(heater_relay, HIGH);
fridge_status = RESTING;
}

if(tempCorrect()){
digitalWrite((heater_relay), LOW);
digitalWrite((fridge_relay), LOW);
}
}

void tempUp(){  
if(digitalRead(SetTemp_Up) == HIGH){                                                                         
setTemp += 0.5;
lcdPrint();
}
}

void tempDown(){
if(digitalRead(SetTemp_Down) == HIGH){ 
setTemp -= 0.5;
lcdPrint();
}
}


void getTemp(){
sensors.requestTemperatures();
double t = sensors.getTempCByIndex(0);
if(t > 0 && t < 120){ 
currentTemp = t;
} 
}

void lcdPrint(){
lcd.begin(16,2); 

lcd.clear();
lcd.setCursor(0,0);
lcd.print("TEMP");
lcd.setCursor(5,0);
lcd.print(currentTemp);
lcd.setCursor(10,0);
lcd.print((char)223);
lcd.setCursor(11,0);
lcd.print("C");

lcd.setCursor(1,1);
lcd.print("SET");
lcd.setCursor(5,1);
lcd.print(setTemp);
lcd.setCursor(10,1);
lcd.print((char)223);
lcd.setCursor(11,1);
lcd.print("C");

//print a "*" on the display if the fridge is on
lcd.setCursor(15,1);
if(fridge_status == COOLING){
lcd.print("C");
}
if(fridge_status == RESTING){
lcd.print("H");
}
if(tempCorrect){
lcd.print(" ");
lcd.display();
}
}

needless to say, its not adjusting the temp

Nowhere in the code do you actually call tempUp() or tempDown().

I would expect that the functions may need to be revised to be like the IDE example 02>Digital>StateChangeDetection to not give multiple changes on a button press.

Ok i removed the interrupts, and basically recoded.

Issues i still have:

  1. The increment and decrement buttons for SetTemp doesn’t work.
  2. if the measured temp cycles past the set value, the opposite relay doesnt switch off
  3. cant get the “C” , “H” and " " symbols to display when in the cooling or heating state.

Thanks

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


#define ONE_WIRE_BUS 4


LiquidCrystal lcd(12, 11, 10, 9, 8, 6);
double currentTemp = 25.0;
double setTemp = 20.00;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

const int COOLING = 1;
const int RESTING = 1;
const int CORRECT = 1;
const double precision = 0.3; //.5 degrees precision

int fridge_status;
int startup_time = 1000;
int Cooling_Delay = 5000;

const int fridge_relay = 5;
const int heater_relay = 7;
const int SetTemp_Up = 2;
const int SetTemp_Down = 3;

int SetTemp_Up_State = 0;
int SetTemp_Down_State = 0;

boolean tooWarm(){
return (currentTemp >= (setTemp));
}

boolean tooCold(){
return (currentTemp <= setTemp); //temp keeps dropping after temp is reached
}

boolean tempCorrect(){
return (currentTemp == setTemp);
}

void setup(){
digitalWrite(fridge_relay, LOW); //start with fridge off

lcd.begin(16, 2);
//Serial.begin(9600);
lcd.print("Starting...");

sensors.begin();
pinMode(fridge_relay, OUTPUT);
pinMode(heater_relay, OUTPUT);
pinMode(SetTemp_Up, INPUT);
pinMode(SetTemp_Down, INPUT);
fridge_status = RESTING;
} 

void loop() {
delay(startup_time); 
startup_time = 10;

getTemp();
lcdPrint();

if(tooWarm()){
delay(Cooling_Delay);
digitalWrite(fridge_relay, HIGH);
fridge_status = COOLING;
}

if(tooCold()){
digitalWrite(heater_relay, HIGH);
fridge_status = RESTING;
}

if(tempCorrect()){
digitalWrite((heater_relay), LOW);
digitalWrite((fridge_relay), LOW);
fridge_status = CORRECT;
}
}

void tempUp(){  
SetTemp_Up_State = digitalRead(SetTemp_Up);
if(SetTemp_Up_State == HIGH){                                                                         
setTemp += 0.5;
lcdPrint();
}
}

void tempDown(){
SetTemp_Down_State = digitalRead(SetTemp_Down);
if(SetTemp_Down_State == HIGH){
setTemp -= 0.5;
lcdPrint();
}
}

void getTemp(){
sensors.requestTemperatures();
double t = sensors.getTempCByIndex(0);
if(t > 0 && t < 120){ 
currentTemp = t;
} 
}

void lcdPrint(){
lcd.begin(16,2); 

lcd.clear();
lcd.setCursor(0,0);
lcd.print("TEMP");
lcd.setCursor(5,0);
lcd.print(currentTemp);
lcd.setCursor(10,0);
lcd.print((char)223);
lcd.setCursor(11,0);
lcd.print("C");

lcd.setCursor(1,1);
lcd.print("SET");
lcd.setCursor(5,1);
lcd.print(setTemp);
lcd.setCursor(10,1);
lcd.print((char)223);
lcd.setCursor(11,1);
lcd.print("C");

//print a "*" on the display if the fridge is on
lcd.setCursor(15,1);
if(fridge_status == COOLING){
lcd.print("C");
lcd.display();
}
lcd.setCursor(15,1);
if(fridge_status == RESTING){
lcd.print("H");
lcd.display();
}
lcd.setCursor(15,1);
if(fridge_status == CORRECT){
lcd.print(" ");
lcd.display();
}
}

Hi.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Do you have pull-up or pull-down resistors on your up and down buttons?

Thanks… Tom… :slight_smile:

Typical. Wire up a bunch of hardware, write a bunch of code, have only a couple of clues about any of it or how it should work then wonder why it doesn’t.

WHY bother with the LCD or buttons until AFTER getting the essentials working?

You have unknowns piled on top of unknowns and you don’t know what you’re even writing.

Thermostats with the set points too close run heaters to death in no time.

Yours is insane. It has ONE setpoint that is too cold, too hot and just right at the same time!

boolean tooWarm(){
return (currentTemp >= (setTemp));
}

boolean tooCold(){
return (currentTemp <= setTemp); //temp keeps dropping after temp is reached
}

boolean tempCorrect(){
return (currentTemp == setTemp);
}

And you’re worried about buttons?

The heater should turn on at perfect minus some degrees, not at perfect or less.
The ON temperature should be less than the OFF temperature. Enough less for the heater to not fire up every few seconds.

The cooler should mirror that. The two should be far enough apart to not be dueling each other.

Beer fermentation can happily proceed within a temperature range, the bacteria don’t have OCD.

But if you could build a digital temperature display that the bacteria could read, they would all refuse to ferment until the last digit was exactly right.