PID sous vide cooker

Hello all,

I am making a sous vide cooker and am using a SSR and the Maxim one wire temperature sensor.

I have been struggling with the code, I am trying to use the PID library V1, but cannot make the output go high no matter what the temperature is. Here is my code so far:

#include <OneWire.h>
#include <PID_v1.h>

#define RelayPin 6

//init the one wire interface on pin 10
OneWire ow(10);

//write here the address you receive from the other program
byte sensor[8] = {0x10, 0x50, 0xC, 0x51, 0x02, 0x08, 0x00, 0x9A};

//Define Variables we’ll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void setup() {

Serial.begin(9600);

windowStartTime = millis();

//initialize the variables we’re linked to
Setpoint = 95;

//tell the PID to range between 0 and the full window size
myPID.SetOutputLimits(0, WindowSize);

//turn the PID on
myPID.SetMode(AUTOMATIC);
}

void writeTimeToScratchpad(byte* address){
//reset the bus
ow.reset();
//select our sensor
ow.select(address);
//CONVERT T function call (44h) which puts the temperature into the scratchpad
ow.write(0x44,1);
//sleep a second for the write to take place
delay(1000);
}

void readTimeFromScratchpad(byte* address, byte* data){
//reset the bus
ow.reset();
//select our sensor
ow.select(address);
//read the scratchpad (BEh)
ow.write(0xBE);
for (byte i=0;i<9;i++){
data = ow.read();

  • }*
    }
    float getTemperature(byte* address){

  • int tr;*

  • byte data[12];*

  • writeTimeToScratchpad(address);*

  • readTimeFromScratchpad(address,data);*

  • //put in temp all the 8 bits of LSB (least significant byte)*

  • tr = data[0];*

  • //check for negative temperature*

  • if (data[1] > 0x80){*

  • tr = !tr + 1; //two’s complement adjustment*
    _ tr = tr * -1; //flip value negative._

  • }*

  • //COUNT PER Celsius degree (10h)*

  • int cpc = data[7];*

  • //COUNT REMAIN (0Ch)*

  • int cr = data[6];*

  • //drop bit 0*

  • tr = tr >> 1;*

  • //calculate the temperature based on this formula :*

  • //TEMPERATURE = TEMP READ - 0.25 + (COUNT PER Celsius Degree - COUNT REMAIN)*
    // (COUNT PER Celsius Degree)

  • return tr - (float)0.25 + (cpc - cr)/(float)cpc;*
    }

//fahrenheit to celsius conversion
float f2c(float val){

  • float aux = val - 32;*
    _ return (aux * 5 / 9);_
    }

//celsius to fahrenheit conversion
float c2f(float val){
_ float aux = (val * 9 / 5);_

  • return (aux + 32);*
    }
    void loop()
    {
  • float temp;*
  • float tmp2;*
  • tmp2 = getTemperature(sensor);*
  • temp = c2f(tmp2);*

// Serial.print("Temp = ");
// Serial.print(temp);
Serial.println(" ");
// Serial.print(" F or ");
//Serial.print(tmp2);
//Serial.println(" C");

  • //wait 30 seconds*
    // delay(30000);

  • Input = (double)temp;*

  • myPID.Compute();*

  • Serial.print((double)Input);*

  • //Serial.print(temp);*
    _ /************************************************_
    _ * turn the output pin on/off based on pid output_
    _ ************************************************/_

  • unsigned long now = millis();*

  • if(now - windowStartTime>WindowSize)*

  • { //time to shift the Relay Window*

  • windowStartTime += WindowSize;*

  • }*

  • if(Output > now - windowStartTime) digitalWrite(RelayPin,HIGH);*

  • else digitalWrite(RelayPin,LOW);*

  • delay(1500);*
    }
    Any input would be greatly appreciated.
    Thanks

  1. The code in your post would be easier to read if you put code-tags around it (use the # button in the editor).

  2. You need to call pinMode to set pin 6 to be an output.

Sorry, I am a newbie to the forum. Thanks for the quick reply.

I added the pinMode, but I still don’t get an output.

#include <OneWire.h>
#include <PID_v1.h>

#define RelayPin 6 

//init the one wire interface on pin 10
OneWire  ow(10);

//write here the address you receive from the other program
byte sensor[8] = {0x10, 0x50, 0xC, 0x51, 0x02, 0x08, 0x00, 0x9A};

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void setup() {
 
  pinMode(RelayPin, OUTPUT);
  
  Serial.begin(9600);
 
  windowStartTime = millis();
  
  //initialize the variables we're linked to
  Setpoint = 95;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void writeTimeToScratchpad(byte* address){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //CONVERT T function call (44h) which puts the temperature into the scratchpad
  ow.write(0x44,1);
  //sleep a second for the write to take place
  delay(1000);
}

void readTimeFromScratchpad(byte* address, byte* data){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //read the scratchpad (BEh)
  ow.write(0xBE);
  for (byte i=0;i<9;i++){
    data[i] = ow.read();
  }
}
float getTemperature(byte* address){
  int tr;
  byte data[12];
 
  writeTimeToScratchpad(address);
 
  readTimeFromScratchpad(address,data);
 
  //put in temp all the 8 bits of LSB (least significant byte)
  tr = data[0];
 
  //check for negative temperature
  if (data[1] > 0x80){
    tr = !tr + 1; //two's complement adjustment
    tr = tr * -1; //flip value negative.
  }
 
  //COUNT PER Celsius degree (10h)
  int cpc = data[7];
  //COUNT REMAIN (0Ch)
  int cr = data[6];
 
  //drop bit 0
  tr = tr >> 1;
 
  //calculate the temperature based on this formula :
  //TEMPERATURE = TEMP READ - 0.25 + (COUNT PER Celsius Degree - COUNT REMAIN)
// (COUNT PER Celsius Degree)
 
  return tr - (float)0.25 + (cpc - cr)/(float)cpc;
}
 
//fahrenheit to celsius conversion
float f2c(float val){
  float aux = val - 32;
  return (aux * 5 / 9);
}
 
//celsius to fahrenheit conversion
float c2f(float val){
  float aux = (val * 9 / 5);
  return (aux + 32);
}

void loop()
{
  float temp;
  float tmp2;
  tmp2 = getTemperature(sensor);
  temp = c2f(tmp2);
 
 // Serial.print("Temp = ");
 // Serial.print(temp);
 Serial.println(" ");
 // Serial.print(" F or ");
 //Serial.print(tmp2);
//Serial.println(" C");
  //wait 30 seconds
 // delay(30000);
 
   Input = (double)temp;
  myPID.Compute();
  Serial.print((double)Input);
  

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output > now - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  delay(1500);
}

OK, you need to do some debugging to see what is going wrong. The first thing I would do is to put Serial.print calls in the main loop, just before the delay(1500) call, to display the input, set point and output variables. That will make it easier to work out where the problem lies.

[EDIT: I am assuming it's not a hardware issue; can you confirm that either relay needs less than 40mA current to drive it, or you are using a transistor to drive it?]

By using the Serial.print calls I was able to deduce that the variables are all correct. It correctly displayed the setpoint, temperature(input), and output. The output value got smaller as the temperature got closer to the setpoint, and went to zero when the temperature was higher than the setpoint. This leads me to believe that the PID algorithm is working fine, there must be a problem with the relay output.

I am using a LED connected to the output instead of the actual relay for easy debugging purposes so there shouldn't be any hardware issues. When I use the PID sample code and manually vary the input, it sets the output properly, so I am wondering if something in the temperature part of the code is screwing it up.

You could try printing a message to the serial port every time you digitalWrite to RelayPin, to determine whether the digitalWrite calls really are happening as you expect.

The digitalWrite calls aren’t happening at all. But for some reason in the pid sample code it works fine. I copied directly from the sample code, so I can’t figure out why it won’t write to the output.

Here is the sample code that works:
(doesn’t include temperature sensing)

/********************************************************
 * PID RelayOutput Example
 * Same as basic example, except that this time, the output
 * is going to a digital pin which (we presume) is controlling
 * a relay.  the pid is designed to Output an analog value,
 * but the relay can only be On/Off.
 *
 *   to connect them together we use "time proportioning
 * control"  it's essentially a really slow version of PWM.
 * first we decide on a window size (5000mS say.) we then 
 * set the pid to adjust its output between 0 and that window
 * size.  lastly, we add some logic that translates the PID
 * output into "Relay On Time" with the remainder of the 
 * window being "Relay Off Time"
 ********************************************************/

#include <PID_v1.h>
#define RelayPin 6

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;
void setup()
{
  Serial.begin(9600);
  pinMode(RelayPin, OUTPUT);
  
  windowStartTime = millis();
  
  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop()
{
  Input = 95;
  myPID.Compute();

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output > now - windowStartTime)
    digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
 
}

Can you post the code that includes the serial print of input, setpoint and output variables, along with the values it prints? I suggest printing them all on the same line, suitably spaced, with a newline at the end so you get 1 line each time through the loop.

#include <OneWire.h>
#include <PID_v1.h>

#define RelayPin 6 

//init the one wire interface on pin 10
OneWire  ow(10);

//write here the address you receive from the other program
byte sensor[8] = {0x10, 0x50, 0xC, 0x51, 0x02, 0x08, 0x00, 0x9A};

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void setup() {
 
  pinMode(RelayPin, OUTPUT);
  
  Serial.begin(9600);
 
  windowStartTime = millis();
  
  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

}
void writeTimeToScratchpad(byte* address){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //CONVERT T function call (44h) which puts the temperature into the scratchpad
  ow.write(0x44,1);
  //sleep a second for the write to take place
  delay(1000);
}

void readTimeFromScratchpad(byte* address, byte* data){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //read the scratchpad (BEh)
  ow.write(0xBE);
  for (byte i=0;i<9;i++){
    data[i] = ow.read();
  }
}
float getTemperature(byte* address){
  int tr;
  byte data[12];
 
  writeTimeToScratchpad(address);
 
  readTimeFromScratchpad(address,data);
 
  //put in temp all the 8 bits of LSB (least significant byte)
  tr = data[0];
 
  //check for negative temperature
  if (data[1] > 0x80){
    tr = !tr + 1; //two's complement adjustment
    tr = tr * -1; //flip value negative.
  }
 
  //COUNT PER Celsius degree (10h)
  int cpc = data[7];
  //COUNT REMAIN (0Ch)
  int cr = data[6];
 
  //drop bit 0
  tr = tr >> 1;
 
  //calculate the temperature based on this formula :
  //TEMPERATURE = TEMP READ - 0.25 + (COUNT PER Celsius Degree - COUNT REMAIN)
// (COUNT PER Celsius Degree)
 
  return tr - (float)0.25 + (cpc - cr)/(float)cpc;
}
 
//fahrenheit to celsius conversion
float f2c(float val){
  float aux = val - 32;
  return (aux * 5 / 9);
}
 
//celsius to fahrenheit conversion
float c2f(float val){
  float aux = (val * 9 / 5);
  return (aux + 32);
}


void loop()
{
  float temp;
  float tmp2;
  tmp2 = getTemperature(sensor);
  temp = c2f(tmp2);
 
  Input = (float)temp;
  myPID.Compute();
  
  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output > now - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  
  Serial.print ("Input:");
  Serial.print((float)Input);
   Serial.print(" ");
   Serial.print ("Setpoint:");
   Serial.print((float)Setpoint);
   Serial.print(" ");
   Serial.print ("Output:");
   Serial.print((float)Output);
   Serial.println(" ");
   
   delay(2500);
   
}

Here is a sample of the Serial.print:

Input:83.86 Setpoint:100.00 Output:174.79
Input:83.97 Setpoint:100.00 Output:182.58
Input:83.97 Setpoint:100.00 Output:191.72
Input:84.09 Setpoint:100.00 Output:198.32
Input:84.09 Setpoint:100.00 Output:207.41
Input:84.09 Setpoint:100.00 Output:215.36
Input:84.09 Setpoint:100.00 Output:223.32
Input:84.09 Setpoint:100.00 Output:231.27

And what happens if you change it to:

  ...
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output > now - windowStartTime)
  {
     Serial.print("Writing HIGH ");
     digitalWrite(RelayPin,HIGH);
  }
  else
  {
    Serial.print("Writing LOW ");
    digitalWrite(RelayPin,LOW);
  }
  
  Serial.print ("Input:");
  Serial.print((float)Input);
  ...

PS:

  1. which version of the OneWire library are you using?

  2. assuming your led is wired from pin 6 to ground (with a series resistor), look at it with the room almost in darkness, to see whether it is coming on a little bit (this is what typically happens if pins used for output are configured as inputs).

I just realised a couple of things:

  1. With the window size set at 5000, your loop time needs to be much less than that; but you have a delay(2500) call in the loop. Try reducing the delay to 100 or even less.

  2. The PID output will need to reach 5000 before the relay stays on all the time, but it doesn't get anywhere near that in your listing. Again, using a shorter delay should make it rise faster.

Well it seems to work with a delay of 100. Thank you very much!

Writing LOW Input:79.47 Setpoint:100.00 Output:3831.87
Writing HIGH Input:79.47 Setpoint:100.00 Output:3842.14
Writing HIGH Input:79.47 Setpoint:100.00 Output:3852.40
Writing HIGH Input:79.47 Setpoint:100.00 Output:3862.66
Writing LOW Input:79.47 Setpoint:100.00 Output:3872.92
Writing HIGH Input:79.47 Setpoint:100.00 Output:3883.19
Writing HIGH Input:79.36 Setpoint:100.00 Output:3894.86
Writing HIGH Input:79.36 Setpoint:100.00 Output:3904.05
Writing LOW Input:79.36 Setpoint:100.00 Output:3914.37

Now to deal with tuning, and a button/lcd user interface...