Set-point temperature can't be achieved in PID

Hello, I am controlling a cartridge heater (12VDC-40W) to maintain a desired temperature with the help of this N-Channel MOSFET, 100K-Ohm Thermistor and obviously Arduino UNO. I used the PID_Relay Output example given with Arduino. And I used the example from Adafruit to measure the temperature using Thermistor. Everything is fine. No need of Autotune. Just example code suffices for me. But the only problem is, I set the temperature at 100C. But the code keeps the heater controlled at around 65C. That means, the code doesn't hold the temperature at desireds level but still it works in the sense that it maintains a specific temperature (more or less). For my case, this accuracy works well. So, would you please help anyone to identify the problem? How to set setpoint? NB. I may need to maintain temp up to around 250 (the thermistor has limit up to 300C). I used this assembled code:

#include <PID_v1.h>
#define RelayPin 7

// which analog pin to connect
#define THERMISTORPIN A0         
// resistance at 25 degrees C
#define THERMISTORNOMINAL 100000      
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25   
// how many samples to take and average, more takes longer
// but is more 'smooth'
#define NUMSAMPLES 5
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 4267
// the value of the 'other' resistor
#define SERIESRESISTOR 100000 
int samples[NUMSAMPLES];

//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);
  analogReference(EXTERNAL);
  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()
{
  uint8_t i;
  float average;
 
  // take N samples in a row, with a slight delay
  for (i=0; i< NUMSAMPLES; i++) {
   samples[i] = analogRead(THERMISTORPIN);
   delay(10);
  }
 
  // average all the samples out
  average = 0;
  for (i=0; i< NUMSAMPLES; i++) {
     average += samples[i];
  }
  average /= NUMSAMPLES;
 
  //Serial.print("Average analog reading "); 
  //Serial.println(average);
 
  // convert the value to resistance
  average = 1023 / average - 1;
  average = SERIESRESISTOR / average;
  //Serial.print("Thermistor resistance "); 
  //Serial.println(average);
 
  float steinhart;
  steinhart = average / THERMISTORNOMINAL;     // (R/Ro)
  steinhart = log(steinhart);                  // ln(R/Ro)
  steinhart /= BCOEFFICIENT;                   // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                 // Invert
  steinhart -= 273.15;                         // convert to C
 
  Serial.print("Temperature "); 
  Serial.print(steinhart);
  Serial.println(" *C");
 
  delay(1000);
  
  Input = steinhart;
  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(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);

}

I have attached a screen-shot of serial monitor and the wiring diagram. Thank you for your time!

How do you know the temperature is 65 C?
How did you check the accuracy of your thermistor and conversion code?
If that all checks out correctly, is it possible that the heater is full on, and cannot provide any more heat ? I doubt you could boil a coffee cup full of water with a 40 W heater.
Is the power supply adequate for the heater?

A screen shot listing what the variable "Output" is doing, would be more useful.

A 40 watt heater should potentially be capable of increasing the temperature of a cup of coffee by about half a degree per second. Because of losses it won't do that well, but it should be noticeable.

Does you coffee start out at room temperature and heat up to 65 and goes no further, or does it start at 65 ?

jremington:
How do you know the temperature is 65 C?

I checked with a pyrometer and confirmed the temp.

jremington:
How did you check the accuracy of your thermistor and conversion code?

I used only the thermistor program to only measure the temp. I was pretty satisfied with the result/accuracy. Not only that, I changed both the variable "setpoint" and "WindowSize" but it didn't cause any change in output. :frowning:

jremington:
If that all checks out correctly, is it possible that the heater is full on, and cannot provide any more heat ? I doubt you could boil a coffee cup full of water with a 40 W heater.

Yes! just like you, I also though and tested one same heater to check the maximum temp what it can go up. Interestingly, it was more than 450C. I used a silicone (which can withstand maximum 454C) to coat the heater and then heating, after 50/60 min, it burnt and caused fire. So, I am pretty sure heater rating is more than enough.

jremington:
Is the power supply adequate for the heater?

The power adapter rating is 12V-3.33A, what is exactly same as heater. Right?

Thank you though. Any suggestion ....please!!!

The other problem I can see, is that you heating scheme seems to be based on a variable duty cycle within a 5 second time frame, but your loop( ) function is running on a cycle of about 1.2 seconds. You algorithm should still work anyway, but the potential for bugs is there.

A 40 watt heater should potentially be capable of increasing the temperature of a cup of coffee by about half a degree per second. Because of losses it won't do that well, but it should be noticeable

Actually, this statement I made a few minutes ago is wrong. I had the conversion factor from joules to calories the wrong way around.

40 watts of heat is only 9 calories, which is sufficient to heat 9 grams of water by 1 degree per second. That will heat 250 grams of coffee by only 0.037 degrees per second. A cup of coffee which is 65 C will be losing heat faster than that, and will lose heat even more quickly, as the temperature rises above ambient.

So, you may have reached the limit of your device, where the heat output from your heater is in equilibrium with the heat losses to the surroundings.

I'm not surprised that the heater itself gets hot, but what are you heating with it?
What is the physical arrangement of the thermistor, the heater and the object being heated?
A photo might help, no more than 1000 pixels wide.

@michinyon: thanks for that clarification. I sometimes use a 250 watt "cup heater" to boil a cup of water for tea, and it takes well over 5 minutes to reach boil from room temperature.

michinyon:
So, you may have reached the limit of your device, where the heat output from your heater is in equilibrium with the heat losses to the surroundings.

Actually, the heater is connected with a metallic nozzle (think of 3D printer). So, I see the temperature goes up very quickly. Really very faster than what you are talking about.

A quick look at reviews for immersion heaters on Amazon.com suggests that a 125W immersion heater takes about 15 to 20 minutes to boil a cup of water. If you're making coffee or tea, a 40W heater probably can't keep up with heat loss to the surroundings.

khondoke:
... tested one same heater to check the maximum temp what it can go up. Interestingly, it was more than 450C. I used a silicone (which can withstand maximum 454C) to coat the heater and then heating, after 50/60 min, it burnt and caused fire.

That might prove that the surface of the heating element, insulated with silicone, can get to 450 degrees C in an hour. It doesn't prove that the heater can make enough heat to overcome losses to the surroundings from some other kind of enclosure. Is the thermistor tight against the heating element, ensconced in a coating of silicone? If not, the heater may not have enough oomph to maintain a temperature much above the surroundings.

Please describe the setup - what's being heated, how big it is, how it's insulated from the surroundings, and how far the thermistor is from the heating element, and any other things that might help with a diagnosis.

Finally, printed diagnostic messages are strangely absent from the code. I'd suggest printing the value of the window whenever it's calculated, the value of millis() each time the output switches, and the elapsed time between transitions on the output. That'll tell you a lot more about what's really happening than we can. The system may have to run for a while for the integral component of the control loop to wind up. Please show some samples of that output.

Add some insulation around the heater/thermistor and see if it gets above 65. If so it means your heater has reached its limit and you need more insulation (or a more powerful heater) to get your temperature higher.

What you really NEED to do, is put print statements in your loop() function, that prints out the values of the PID values (PTerm, ITerm, DTerm, as well as the PID input and output values). That will tell you very quickly what's going on. If you're really setting the setpoint to 100C, and the feedback value (intput) is only 65C, the PID output should be practically pegged.

Regards,
Ray L.

Hello All,
First of all, please be confirmed that the heater is more than enough to get temperature at 250 C. Really! I attached a screenshot of Time vs. Temp profile by using only thermistor program (not PID) only show you that this heater is more than enough for my case. The temp went up from room temp to 128 C in only 52 seconds. So, I had to stop the program and disconnect the heater, otherwise it could damage by heating as high as 500 C within 2/3 min. Anyway, no problem about the physical arrangement and insulation. Please be sure about that. So, it seems the problem with only code or controlling algorithm. That's why I put the problem here for you experts on programming.

I also have attached a photo. The heater and thermistor are inserted in hot end. Heater is with red wire and thermistor with silver colored wires. So, everything is like metal. That's why temp is running up so quickly.

The problem is, when ever I run PID program with this arrangement to control heater keeping desired level of temp using a MOSFET, the temp doesn't go up....:frowning:

I was wondering about the very last part of PID_Relay Output example. This one:

  /************************************************
   * 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(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);

#Why time (I mean, WindowStartTime) is important here in PID in case of controlling relay? Why WindowSize set at 5 sec is required?
#I am using MOSFET instead of Relay. Does it has any effect on coding?
#I was thinking, probably the program is written is such a way that, it can't allow heater to be ON for a longer time. And since heater can't be ON for a longer time so temp can't go up with this code. Is this impression correct?

Please have a look once again on the code and help me take my heater temp up to 250C. Thank you very much for your time. Really appreciate that......

If you're controlling the heater with a FET, why on earth would you use the relay mode of the PID? It has zero benefit.

In any case, see my post about adding print statement to loop(). Without some real data about what the PID is really doing, it's a waste of time to try to figure out why you're not getting the result you want. Print statements will tell you exactly what's going on very quickly. At a minimum, it will greatly narrow the search.

Regards,
Ray L.

Hi,

I set the temperature at 100C. But the code keeps the heater controlled at around 65C.

If you change the set point does the final temperature change but with a similar OFFSET.

Correcting OFFSET is one of the reasons you use PID, one of the PID parameters will correct this.
Or do an AUTOTUNE, to get some values that will help you get it closer.
Everything is not fine, you have OFFSET, try AUTOTUNE.

Tom........ :slight_smile:

The data you print seems to imply that the PID thinks the temperature is above 100C.

What exactly are you measuring with the IR pyrometer - they cannot measure accurately
anyway (in particular the reading depends on the nature of the target - metals are hopeless).

If there's a discrepancy between what the thermistor reads and the true temperature,
sort out the thermistor measurement first.

Find a second, independent way to check on the true temperature (multimeter with a thermocouple?).

I suspect part your problem is " delay(1000);". Since your PWM window is 5 seconds that will limit you to fewer than 5 levels of power.

Since you are using a MOSFET you can eliminate all the "window" stuff and let the Output default to 0-255 range, then use Output for analogWrite() directly to your PWM output pin.

Did you ever add code to set RelayPin to OUTPUT?

Why are you using an EXTERNAL analog reference? Do you have a precision 5V reference source? If you are connecting the Aref pin to the +5V pin you should just use DEFAULT.

Try this:

#include <PID_v1.h>
const int HeaterPin = 7;

// which analog pin to connect
#define THERMISTORPIN A0         
// resistance at 25 degrees C
#define THERMISTORNOMINAL 100000      
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25   
// how many samples to take and average, more takes longer
// but is more 'smooth'
#define NUMSAMPLES 5
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 4267
// the value of the 'other' resistor
#define SERIESRESISTOR 100000.0

//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);

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

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

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

void loop() {
  // take N samples in a row, with a slight delay
  // average all the samples out
  float average = 0;
  for (int i=0; i< NUMSAMPLES; i++) {
    average += analogRead(THERMISTORPIN);
    delay(10);
  }
  average /= NUMSAMPLES;

  //Serial.print("Average analog reading "); 
  //Serial.println(average);

  // convert the value to resistance
  average = 1023.0 / average - 1.0;
  average = SERIESRESISTOR / average;
  //Serial.print("Thermistor resistance "); 
  //Serial.println(average);

  float steinhart;
  steinhart = average / THERMISTORNOMINAL;     // (R/Ro)
  steinhart = log(steinhart);                  // ln(R/Ro)
  steinhart /= BCOEFFICIENT;                   // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                 // Invert
  steinhart -= 273.15;                         // convert to C

  Input = steinhart;
  myPID.Compute();

  Serial.print("Temperature "); 
  Serial.print(steinhart);
  Serial.print(" *C    Output=");
  Serial.println(Output);
  
  analogWrite(HeaterPin,Output);
}

khondoke:
The temp went up from room temp to 128 C in only 52 seconds.

OK, it looks like you've shown that the heater can get the thermistor to a calculated temperature above 100 degrees C.

Taking the sketch from the original post, adding code to set RelayPin to OUTPUT, and forcing the temperature - the value of the variable steinhart - to 65 after each calculation, and running it, I find that RelayPin starts high, switches for about 4 and a half minutes, and then is uniformly low. Based on the diagram in the original post, showing the MOSFET's source at GND, the drain at 12V, and the gate connected to RelayPin, I'd expect that the output should go high to turn the heater on.

I think that the sense of the RelayPin is backwards. I think the code turns the RelayPin off when it should turn it on, and on when it should turn it off. When the sketch initiates, the integral term of the PID calculation is zero, and the loop is dominated by the proportional term. That term isn't particularly large, so the value of Output is small, and RelayPin is set high most of the time. As the integral term winds up, the value of Output gets larger, and RelayPin is set low most of the time. I think that you'd start to see the temperature drop, and stabilize at room temperature, if you ran the test longer.

I note that the sense of RelayPin is reversed from the example code shown here - Arduino Playground - HomePage. I thin that it should have the same sense as in the example.

As johnwasser notes above, it would be wise to set Relaypin's mode to OUTPUT, since you're using it as an output. With RelayPin left in the default state of INPUT, the pin is open when a zero is written to it, and it's connected to VCC through a resistor of about 50K when a one is written to it. When RelayPin is open-circuit, the gate voltage will discharge through the pin's leakage. We don't know what the leakage current is, except that it's less than about a microamp, so we don't know what the MOSFET's gate voltage will be, and the performance will be hard to predict. That explains why the heater operates at all with the pin set as INPUT, and it certainly suggests that performance would be a lot better and a lot more predictable if it were set as OUTPUT.

To test this notion, you can set pin 13 as OUTPUT, and set it to follow the RelayPin. If you see the LED go dark for most of the time as time advances, I think you can expect that the heater is on for a progressively smaller portion of the time. Or, you can just connect a voltmeter to the MOSFET's gate or to the heater, and watch the output. Or, you could add code to print the setting of RelayPin ahd the time whenver the pin switches, as has been suggested before. One way or another, though, you'll have to do some troubleshooting.