PID controlled cooling and heating Beer fermenter

Hi.

I just started learning about Arduino and programming.
This is my first version and before I will go much more further i hope fore some feedback and guidance.
Is this a good way to control this fermenter tank?

/********************************************************
 * This is a code worked from PID RelayOutput Example, This version control cooling and heating on one tank.
 * Same as basic example, except that this time, the output
 * This version control cooling and heating on one tank.
 * 1th. goal is to PID control 2 pcs (125liter) fermenters with glycerol jacked (relay to pump and heating element in lower part) and 1 chiller tank for glycerol
 * 2th. gold is to make a Temperature profile (example: 2 days with 19,5°C 4 days with 21°C 6 days with 19°C) 
 * 3th. gold is to make a Temperature logger over network with Raspberry PI B+
 * 4th. gold is to add display to monitor the temperatures. Automatic cycle showing set and act temperature. 
 * 5th. gold is to Set temperature profile and chiller temp from encoder switch
 ********************************************************/

#include <PID_v1.h>
#define RelayPin1 5
#define RelayPin2 6




double Setpoint1, Setpoint2, Setpoint3, Hysteresis, Input1, HeatingOutputValue1, CoolingOutputValue1;

//Specify the links and initial tuning parameters
PID HeatingPID1(&Input1, &HeatingOutputValue1, &Setpoint1,2,5,1, DIRECT);
PID CoolingPID1(&Input1, &CoolingOutputValue1, &Setpoint1,2,5,1, REVERSE);

int WindowSize1 = 5000;  //Heating cycle time
int WindowSize2 = 5000;  //Cooling cycle time
unsigned long windowStartTime1;
unsigned long windowStartTime2;

void setup()
{
  windowStartTime1 = millis();
  windowStartTime2 = millis();
  //initialize the variables we're linked to
  Setpoint1 = 20  ; //Fermenter no. 1 Set value
  Setpoint2 = 20  ; //Fermenter no. 2 Set value
  Setpoint3 = -7  ; //Chiller temperature
  Hysteresis = 2/2 ;  
  
  //tell the PID to range between 0 and the full window size
  HeatingPID1.SetOutputLimits(0, WindowSize1);
  CoolingPID1.SetOutputLimits(0, WindowSize2);
  //turn the PID on
  HeatingPID1.SetMode(AUTOMATIC);
  CoolingPID1.SetMode(AUTOMATIC);  
  	  Serial.begin(9600); 

}

void loop()
{
  Input1 = analogRead(0);  //Temperatur sensor Fermenter no. 1
  
//Tells the PID's zero its value and not work against each other?
if (Input1 < Setpoint1-Hysteresis) HeatingPID1.Compute();
if (Input1 < Setpoint1+Hysteresis) CoolingOutputValue1 = 0;

if (Input1 > Setpoint1+Hysteresis) CoolingPID1.Compute();
if (Input1 > Setpoint1-Hysteresis) HeatingOutputValue1 = 0;

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if(millis() - windowStartTime1>WindowSize1)
  { //time to shift the Relay Window
    windowStartTime1 += WindowSize1;
  }
  if(HeatingOutputValue1 < millis() - windowStartTime1) digitalWrite(RelayPin1,HIGH);
  else digitalWrite(RelayPin1,LOW);
 
 
  if(millis() - windowStartTime2>WindowSize2)
  { //time to shift the Relay Window
    windowStartTime2 += WindowSize2;
  }

  if(CoolingOutputValue1 < millis() - windowStartTime2) digitalWrite(RelayPin2,HIGH);
  else digitalWrite(RelayPin2,LOW);

 Serial.print("Setpoint1:");
  Serial.print(Setpoint1);
   Serial.print("Input1:");
    Serial.print(Input1);
       Serial.print("HeatingOutputValue1:");
    Serial.print(HeatingOutputValue1);
           Serial.print("CoolingOutputValue1:");
    Serial.println(CoolingOutputValue1);

}

Best Regards

BeerPal

Is this a good way to control this fermenter tank?

In my opinion, no.

  Input1 = analogRead(0);  //Temperatur sensor Fermenter no. 1

If you used a meaningful name for the argument to analogRead, and stored the returned value in a variable with a meaningful name, the code would be much better, and there would be no need for the comment.

  Setpoint1 = 20  ; //Fermenter no. 1 Set value
  Setpoint2 = 20  ; //Fermenter no. 2 Set value
  Setpoint3 = -7  ; //Chiller temperature

Define 3. Use 1. Why?

int WindowSize1 = 5000;  //Heating cycle time
int WindowSize2 = 5000;  //Cooling cycle time
unsigned long windowStartTime1;
unsigned long windowStartTime2;

Meaningful names means that you could ditch the comment.

Why are there two windows? Why are there two PIDs? The temperature is either too high, just right, or too low. The same temperature isn't going to be used to heat and cool AT THE SAME TIME, is it?

The output from the PID process is a value that defines how much of a change is needed. Positive value would indicate, to me, that heating is needed, while negative values would mean that cooling is needed. So, it seems to me that you only need one PID.

Your inconsistent formatting and multiple statements on one line make the code hard to read, too. Pick ONE style and use it consistently, even if its the wrong style.

Hi PaulS

Thanks for your feedback.

PaulS:

Is this a good way to control this fermenter tank?

In my opinion, no.

Regardig why i want i want to use PID is that the Glycerol jacket is very big and i am afraid to overshoot the set temp with to much.

PaulS:   Input1 = analogRead(0);  //Temperatur sensor Fermenter no. 1

If you used a meaningful name for the argument to analogRead, and stored the returned value in a variable with a meaningful name, the code would be much better, and there would be no need for the comment.

Sure thing that I will try to make better code :) this is my second code I make.

PaulS:   Setpoint1 = 20  ; //Fermenter no. 1 Set value   Setpoint2 = 20  ; //Fermenter no. 2 Set value   Setpoint3 = -7  ; //Chiller temperature

Define 3. Use 1. Why?

Just for further upgrade in code! when expanding Line 2 and 3 has no function here.

PaulS: int WindowSize1 = 5000;  //Heating cycle time int WindowSize2 = 5000;  //Cooling cycle time unsigned long windowStartTime1; unsigned long windowStartTime2;

Meaningful names means that you could ditch the comment.

Why are there two windows? Why are there two PIDs? The temperature is either too high, just right, or too low. The same temperature isn't going to be used to heat and cool AT THE SAME TIME, is it?

The output from the PID process is a value that defines how much of a change is needed. Positive value would indicate, to me, that heating is needed, while negative values would mean that cooling is needed. So, it seems to me that you only need one PID.

I believe that maybe i need diffrent windows for heating (heating element) and cooling (glycerol pump from chiller) But i am not sure. and is it possible to make one to heat and cool? how?

I believe that maybe i need diffrent windows for heating (heating element) and cooling (glycerol pump from chiller)

Again, it seems to me that the actual temperature is either just right (make no changes), too low (need to heat), or too high (need to cool).

Now, if the idea is to heat for a while, and then cool for a while, where the heating and cooling windows don't overlap, then two PIDs makes sense. But, you'd know which one to update (call compute() for), based on whether the heating or cooling window was open.

But, then, the only thing I know about beer is how to drink it.