Heater continues heating after reaching target temperature

Hello.

I was able to put together the circuit heater controller from the following link:

and I have it running out of a 12V PC power supply.
I did the auto tune and the parameters I got are as follows:
#define KP 13.69
#define KI 0.12
#define KD 385.05

#define SMOOTHING_WINDOW 5
#define MOSFET_GATE_PIN 3
#define THERMISTOR_PIN A6

#define KY040_CLK 9
#define KY040_DT 8
#define KY040_SW 2
#define KY040_STEPS_PER_NOTCH 2

bool heaterOn = false, pOnM = false;
double pwmOut, curTemp, setTemp = 0;

NTC_Thermistor* thermistor = new NTC_Thermistor(THERMISTOR_PIN, 99800, 100000, 25, 3950);
I am using a larger resistor with the thermistor.

The circuit works well execpt that the heater continues heating even after it reaches the target temperature.
If I press the rotary button it stops and the temperature gows down, as expected. The LED turns on once the target temperature is reached, goes of once it is surpassed. So the program seems to be OK, but why doesn´t the heater stop heating after reaching the target?
If anyone has built this circuit and has any advise on what I should check, I would greatly appreciate it.
Thank you!

... because your code (for the heater) does not react to the threshold crossing.

But the program is made so that the Temperature remains constant once it reaches the desired threshold.
I will check if the gate of the MOSFET remains at the same voltage. But it should shut the current once the target is reached.

You should post your sketch and a circuit drawing for better help.

2 Likes

I suspect your code is blocking. We cannot determine that unless we can see it. Also an annotated schematic would help as well. Be sure to show all connections, power, ground, power sources and note any leads over 25cm/10".

We don't have your exact system, so it is hard to tell.

Do you have data? One reason PIDs continue heating after reaching the target temperature is that the Ki term winds up during the initial heating cycle, and needs to operate over temp for a while to unwind.

1 Like

Sure, thanks.
Here is a picture of the diagram and of the built circuit.



Below is the sketch.
When the heater is off as selected by the rotary switch, the voltage at the gate of the STP16NF06FP MOSFET is zero: When I setup a temperature the voltage changes from 0.2 to about 2.5V and fluctuates. But it seems to remain on even after reaching the target temperature, except if I turn it of by pressing the rotary switch again. Then it goes back to zero.

/*
 * heater-with-display.ino
 *
 * Copyright 2020 Victor Chew
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Libraries required:
// - NTC_Thermister (Yurii Salimov) [Source: Arduino Library Manager]
// - PID (Brett Beuuregard) [Source: Arduino Library Manager]
// - ss_oled (Larry Bank) [Source: Arduino Library Manager]
// - TimerOne (Paul Stoffregen et al.) [Source: Arduino Library Manager]
// - ClickEncoder (0xPIT) [Source: https://github.com/0xPIT/encoder/tree/arduino]

#include <ss_oled.h>
#include <TimerOne.h>
#include <ClickEncoder.h>
#include <NTC_Thermistor.h>
#include <SmoothThermistor.h>
#include <PID_v1.h>

#define KP 17.07
#define KI 0.75
#define KD 96.57

#define SMOOTHING_WINDOW 5
#define MOSFET_GATE_PIN 3
#define THERMISTOR_PIN A6

#define KY040_CLK 9
#define KY040_DT 8
#define KY040_SW 2
#define KY040_STEPS_PER_NOTCH 2

bool heaterOn = false, pOnM = false;
double pwmOut, curTemp, setTemp = 0;

NTC_Thermistor* thermistor = new NTC_Thermistor(THERMISTOR_PIN, 99800, 100000, 25, 3950);
SmoothThermistor* sthermistor = new SmoothThermistor(thermistor, SMOOTHING_WINDOW);
PID pid(&curTemp, &pwmOut, &setTemp, KP, KI, KD, P_ON_E, DIRECT);
ClickEncoder encoder(KY040_CLK, KY040_DT, KY040_SW, KY040_STEPS_PER_NOTCH);
SSOLED ssoled;

void refreshDisplay() {
  oledFill(&ssoled, 0, 1);
  char msg[64];
  sprintf(msg, "Hotend: %dc", (int)curTemp);
  oledWriteString(&ssoled, 0, 0, 1, msg, FONT_NORMAL, 0, 1);
  sprintf(msg, "Target: %dc", (int)setTemp);
  oledWriteString(&ssoled, 0, 0, 3, msg, FONT_NORMAL, 0, 1);
  oledWriteString(&ssoled, 0, 0, 6, heaterOn ? "Heating..." : "- Heater off -", FONT_NORMAL, heaterOn ? 1 : 0, 1);
}

void encoderISR() {
  encoder.service();
}

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

  pinMode(MOSFET_GATE_PIN, OUTPUT);
  pid.SetMode(AUTOMATIC);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  encoder.setAccelerationEnabled(true);
  Timer1.initialize(1000);
  Timer1.attachInterrupt(encoderISR); 

  oledInit(&ssoled, OLED_128x64, -1, 0, 0, 1, -1, -1, -1, 400000L);
  refreshDisplay();
}

void loop() {
  // Get rotary encoder values and update display
  int _curTemp = sthermistor->readCelsius();
  int _setTemp = setTemp - encoder.getValue()*5; // minus because signal from this rotary switch works in the opposite direction
  _setTemp = min(300, max(0, _setTemp));
  bool _heaterOn = heaterOn;
  if (encoder.getButton() == ClickEncoder::Clicked) {
    _heaterOn = !_heaterOn;
    if (!_heaterOn) _setTemp = 0;
  }

  // Update display only if something changes
  if (_curTemp != curTemp || _setTemp != setTemp || _heaterOn != heaterOn) {
    curTemp = _curTemp;
    setTemp = _setTemp;
    heaterOn = _heaterOn;
    refreshDisplay(); 
  }

  // Turn on LED if we are within 1% of target temperature
  digitalWrite(LED_BUILTIN, heaterOn ? abs(_curTemp-setTemp)/setTemp <= 0.01 : LOW);

  // To speed things up, only switch to proportional-on-measurement when we are near target temperature
  // See: http://brettbeauregard.com/blog/2017/06/introducing-proportional-on-measurement/
  if (!pOnM && abs(curTemp-setTemp) <= max(curTemp, setTemp)*0.2) {
    pOnM = true;
    pid.SetTunings(KP, KI, KD, P_ON_M);
    Serial.println("P_ON_M activated.");
  }

  // Prevent thermal overrun in case of PID malfunction
  pid.Compute();
  if (curTemp < 300) {
    analogWrite(MOSFET_GATE_PIN, heaterOn ? pwmOut : 0);
  }
  else {
    analogWrite(MOSFET_GATE_PIN, 0);
    Serial.println("Thermal overrun detected!");
  }

  // Display stats
  //Serial.print("pwmOut = "); Serial.print(pwmOut); Serial.print(", "); 
  //Serial.print("diff = "); Serial.print(setTemp - curTemp); Serial.print(", "); 
  //Serial.print("curTemp = "); Serial.print(round(curTemp)); 
  //Serial.print(" / "); Serial.println(round(setTemp));
  
  delay(200);
}
1 Like

I re-ran the autotune program and adjusted the PID as follows:
KP 14.13
KI 0.09
KD 553.70

Upon reaching the target temp of 55 here is the serial monitor:
image



These pictures are from the whole circuit working properly at low temp (55 degC).
It is now slowly moving above 55 to 57C, but it takes much longer thant for higher temperatures.

What are the units on that data? Does pwmOut=0.69 mean 69% of a 20W(?) heater is needed to maintain 55°C

From the data you show it updates every 0.5 sec, and maybe you only read/take data to integer degrees and integer differences.

I don't know that I'd put much faith in the Autotune numbers.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.