PID controller with relay output and DS18B20 as feedback

Hi.

Using the example called: "PID_RelayOutput" from the PID-v1 library, i'm trying to change the sensor input from an analog input to using a DS18B20 temperature sensor on a digital pin on the arduino UNO board.

(i'm building a temperature regulator for driving a SSR by the means of a temperature feedback from a DS18B20 sensor)

My problem is that i can't get the PID loop to respond even if i set the SP to 1 or 100, the output stays on (high/true). The current temperature is approx. 20 degrees C.
I know that i have a valid temperature reading, since i have connected to a LCD and i get a fine reading of the actual temperature.
I'm not quite sure how to set the signal from the DS18B20 as the input for my PID since all previous posts only says "use the temperature signal directly.."

Therefore i have simply replaced the input=AnalogRead in the code with: "Input=sensors.getTempCByIndex(0);"
Which is how i write the temperature reading to my LCD. Can't tell if this is correct or not.

/********************************************************
 * 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>

// Libraries for the DS18B20 sensor
#include <OneWire.h>
#include <DallasTemperature.h>





// Temp. sensor on PIN 7 on the Arduino
#define ONE_WIRE_BUS 7

//Solid state relay on PIN 13 on the Arduino
#define RELAY_PIN 13

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

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

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

int WindowSize = 5000;
unsigned long windowStartTime;

void setup()
{
  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 20;

  //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()
{
  sensors.requestTemperatures();
  Input = sensors.getTempCByIndex(0);
  myPID.Compute();

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

}

What else could i have done wrong? I've tried setting the PID as REVERSE/DIRECT, but i still do not get a response from the output when the setpoint passes the actual temperature.

double Kp=2, Ki=5, Kd=1;

What led you to believe those are reasonable tuning parameters?

I did not say they were reasonable?
But I need a response from the PID before I can start finding the right parameters.
Ziegler and Nichols-method could be used, but I need the regulator to be able to change its output.

Your logic is apparently backwards. I added some prints:

  sensors.requestTemperatures();
  Input = sensors.getTempCByIndex(0);
  Serial.print("Input: ");
  Serial.println(Input);
  myPID.Compute();
  Serial.print("Output: ");
  Serial.println(Output);

and set the setpoint to 30C (ambient is about 23C)

Output increases continually (obviously, because I have no heater attached) until it's 5000. As it increases, the LED simulating the heater stays on less and less until at 5000 it's off all the time. As Output rises, you should be calling for more and more heat but unless the relay logic is reversed you seem to be doing the opposite.

Hi wildbill

Did you get my program to work??
I can see that you have only added some prints (as you're saying) and have not altered anything else in the sketch.
I guess that means that my program should work?

I have tried setting the SP to 1, 20, 30 and 100. The output is high all the time.

All I changed was those debugging prints and the setpoint. The LED on the Uno attached to pin 13 comes on for progressively less and less time until output reaches 5000 at which time it stays off.

I see I've assumed you're heating something - is that correct?

Relay modules are often Active Low (set the output LOW to engage the relay). If your SSR is NOT Active Low you should reverse the output values.

You are absolutely correct.
But if i'm cooling or heating at the moment doesnt really matter. I just want a reaction. For now, i can change the setpoint below and above the ambient temperature (which in my case is around 20 degrees C) without anything happening. The LED for PIN 13 is always on (and yes, i'm sure i'm looking at the right LED).

I just tried printing "Output" to my LCD. It says 0.00 no matter how i adjust the setpoint, which tells me that the PID is doing absolutely nothing.

Once again, did you just copy my sketch from the topic and pasted it in your IDE, and then it worked?

While I'm not familiar with the Arduino PID library, I'm am quite comfortable with PID in general.

My approach to troubleshooting a loop that doesn't behave properly is always first start with both I and D terms (the time based terms) set to zero. In this way, you should be dealing with only P portion and it is a simple matter to determine if the loop is working correctly since the output should be (actual - set point) * gain. If P is not gain and described as proportional, then the reciprocal of proportional is gain. The point is that you should be able to calculate what the output should be, given the actual, setpoint and gain. Once you have the correct output relationship to input, you've got want you need to use Z-N or other method to get in the ballpark tuning parameters.

Mathniel:
Once again, did you just copy my sketch from the topic and pasted it in your IDE, and then it worked?

All I changed was those debugging prints and the setpoint to 30. The LED on the Uno attached to pin 13 comes on for progressively less and less time until output reaches 5000 at which time it stays off.

Try an external LED on another pin.

Now i can get the PID responding through its 0-5000 value!

I wrote the program from scratch and now it works. Perhaps some syntax error..

But i still cant get the digital output on pin 13 to respond.

All this code does is to keep the output off(low) for 5000 msek (WinowSize) and then it goes on(high), and stays this way forever, even if i change the setpoint from 1 to 1000 and back again. I can also set the PID parameters as i wish, it changes nothing.

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

Mathniel:
But i still cant get the digital output on pin 13 to respond.

So load the blink example and see if the LED still works.

I already did. It works perfectly well when i load the blink example.

The output on PIN 13 is ON(high) even if i set the controller to REVERSE. And it doesnt matter if the SP is 1 or 1000.

I can also force the value "Output" to be either "0" or "5000". The digital output on pin 13 stays high.

Have you, by any chance, still got the working code open in your IDE and could you paste it below just to make 100% sure that we are doing the same thing?

Thanks a lot in advance..

Oops, look at that, I lied - there's a serial.begin and println in setup too :wink:

/********************************************************
 * 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>

// Libraries for the DS18B20 sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// Temp. sensor on PIN 7 on the Arduino
#define ONE_WIRE_BUS 7

//Solid state relay on PIN 13 on the Arduino
#define RELAY_PIN 13

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

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

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

int WindowSize = 5000;
unsigned long windowStartTime;

void setup()
{
  Serial.begin(115200);
  Serial.println("Starting");
  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 30;

  //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()
{
  sensors.requestTemperatures();
  Input = sensors.getTempCByIndex(0);
  Serial.print("Input: ");
  Serial.println(Input);
  myPID.Compute();
  Serial.print("Output: ");
  Serial.println(Output);

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

}

OK, but i guess that "serial.begin" and "printIn" does not affect the function of the PID?

I got it to work now with the digital output and everything.

My problem this time was that i had added some texts for my LCD which included timers (delay 10000 msek) in the "void loop". This affected the On/Off funtion of the PID, once i deleted the timers it worked.

Thanks a lot for your help!

I believe:

sensors.getTempCByIndex(0)

returns a float, not an int.

If the program is working properly, you could share the sketch?

Thanks!

I have the same problem. Has been solved? even my output is always high, regardless of the target temperature. Tank's

Try this:

#include <PID_v1.h>
// Libraries for the DS18B20 sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// Temp. sensor on PIN 7 on the Arduino
// DON'T FORGET A PULL-UP RESISTOR FOR THE OneWire BUS!
const byte ONE_WIRE_BUS_PIN  = 7;

// Relay on PIN 13 on the Arduino
const byte RELAY_PIN = 13;
const bool RELAY_PIN_ACTIVE_LEVEL = LOW;  // LOW for ON, HIGH for OFF

OneWire oneWire(ONE_WIRE_BUS_PIN);
DallasTemperature TemperatureSensors(&oneWire);

//Define input/output variables for PID
double Setpoint, Input;  // Temperature (must be in the same units)
double Output;  // 0-WindowSize, Part of PWM window where output is ACTIVE.

//Specify the links and initial tuning parameters
double Kp = 10; // Proportional Constant: Active 10 milliseconds for each degree low
double Ki = 0;  // Integral Constant: Increase to prevent offset (Input settles too high or too low)
double Kd = 0;  // Differential Constant:  Increase to prevent overshoot (Input goes past Setpoint)

// Note: "DIRECT" means Higher Output -> Higher Input. (like a heater raises temperature)
//       "REVERSE" means Higher Output -> Lower Input. (like a cooler lowers temperature)
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

const float WindowSize = 5000.0;
unsigned long WindowStartTime = 0;

void setup()
{
  Serial.begin(115200);

  //initialize the variables we're linked to
  Setpoint = 20.0;  // Desired temperature

  //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()
{
  unsigned long currentTime = millis();

  TemperatureSensors.requestTemperatures();
  Input = TemperatureSensors.getTempCByIndex(0);
  
  myPID.Compute();

  /************************************************
     turn the output pin on/off based on pid output
   ************************************************/
  if (currentTime - WindowStartTime > WindowSize)
  {
    //time to shift the Relay Window
    WindowStartTime += WindowSize;
  }

  // "Output" is the number of milliseconds in each
  // window to keep the output ACTIVE
  if (Output < (currentTime - WindowStartTime))
    digitalWrite(RELAY_PIN, RELAY_PIN_ACTIVE_LEVEL);  // ON
  else
    digitalWrite(RELAY_PIN, !RELAY_PIN_ACTIVE_LEVEL);  // OFF

  // Display the current state every ten seconds
  const unsigned long updateInterval = 10000;
  static unsigned long updateTimer = 0;
  if (currentTime - updateTimer <= updateInterval)
  {
    updateTimer += updateInterval;
    Serial.print("Setpoint: ");
    Serial.print(Setpoint, 1);
    Serial.print("°C, Input:");
    Serial.print(Input, 1);
    Serial.print("°C, Output:");
    Serial.print((int)Output);
    Serial.print(" milliseconds (");
    Serial.print((Output * 100.0) / WindowSize, 0);
    Serial.println("%)");
  }
}
1 Like