Skip to content

Temperature control

Ryan Jarvis edited this page Dec 15, 2018 · 60 revisions

Measuring temperatures

Before we can control the temperature, we need to measure it. We can use special chips for temperature reading. The most common method used in the RepRap community is to use a (NTC thermistor. With the help of the circuit shown below, the AVR can measure a voltage, which depends on the used resistors and the temperature at the thermistor.

Schematic: Thermistor

So how does it work? For a long and good description read nopheads excellent article. The short version is, the NTC changes its resistance. Using a NTC means, with increasing temperature the resistance drops. This causes the measured voltage to change. With the full circuit, we end with the formula:

VMeasured = VRef*RS / (RS+R2)

If you have no resistor R1, RS = R. If you have, then

RS = R * R1 / (R+R1)

After knowing how to convert a temperature into a voltage, how do we get this into the firmware? The current Arduino generation uses AVR controller with a resolution of 10 bit. The voltage is converted into a number between 0 for ground and 1023 for VRef, which is normally 5V. The new XMega AVR controller have a resolution of 12 bit, resulting in values from 0 to 4095. To be ready for the future, the firmware adds 4 readings for a temperature calculation, resulting in a range from 0 to 4092. From this value, the firmware must compute the temperature at the thermistor. So far, so good. Unfortunately, the voltage-temperature-curve is nonlinear and differs for every thermistor type. Even different 100K thermistors have different curves. As a solution, the firmware uses a lookup table from which it interpolates temperatures between given values.

For a better understanding, lets have a look at one of these tables:

#define USER_THERMISTORTABLE0  {\
  {1*4,864*8},{21*4,300*8},{25*4,290*8},{29*4,280*8},{33*4,270*8},{39*4,260*8},{46*4,250*8},{54*4,240*8},{64*4,230*8},{75*4,220*8},\
  {90*4,210*8},{107*4,200*8},{128*4,190*8},{154*4,180*8},{184*4,170*8},{221*4,160*8},{265*4,150*8},{316*4,140*8},{375*4,130*8},\
  {441*4,120*8},{513*4,110*8},**{588*4,100*8}**,{734*4,80*8},{856*4,60*8},{938*4,40*8},{986*4,20*8},{1008*4,0*8},{1018*4,-20*8}	}
  

For those with little C experience: The define must be in one line, which isn't very readable. So to break it into different lines, append a backslash at the end of the line, to indicate that the line continues in the following line.

Format:
(ADC value of resistance, Temperature in Celsius * 8)

What you see is the lookup table for an EPCOS G550 thermistor. The table consists of two columns. The first is the voltage reading and the second is the temperature. In the table, you see that the readings are multiplied with 4 the temperature is multiplied by 8. I've taken this format, because most firmwares use a reading range from 0-1023 and full integer values for temperatures. Repetier-Firmware uses a higher resolution for input and temperature and these factors convert old tables you may have into the needed value range for Repetier-Firmware. If you don't have a matching table, you have to compute one.

Looking into the datasheet gives characteristic curve 8404 for this thermistor. A look into the table shows temperatures and a factor RT/R25. Knowing R25 is 100K, we can compute the resistance for every temperature.

Example:
Let's look at 100°C for EPCOS 8404 data table. We have a value of 6369 ohms nominal resistance (EPCOS G550 spec):

(In this example, R1 is not present; R2 = 4.7K)
(If the table shows a RT/R25 factor value instead, multiply the RT/R25 factor with the resistance value at 25C to get the resistance. IE: 0.06369 * 100000 = 6369 for a 100K resistor)

Values:
R100 = 6369 ohms (from the spec table or calculated by the RT/R25 factor)
R2 = 4700 ohms (fixed value on controller)
ADCrange = 4093 (ADC value range from 0 to 4092. See above, three of the 0 to 4095 range values are reserved)

Calculations:
ADC100 = ADCrange * ((R100) / (R100 + R2))
ADC100 = 4093 * (6369 / (6369 + 4700)) = 2355
ADC100 = 2355 = 588 * 4 (rounded integer value)

The data was entered as "{588 * 4, 100 * 8}" on the table above.
Entering the value as "{2355, 800}" is also acceptable.

Compute the values and enter them into one of the user definable thermistor lookup tables in Configuration.h. The order matters - start with the highest temperature. You can leave the first value for safety.

comment used in configuration.h:

There are many different thermistors, which can be combined with different resistors. This result in unpredictable number of tables. As a resolution, the user can define one table here, that can be used as type 5 for thermistor type in extruder/heated bed definition. Make sure, the number of entries matches the value in NUM_TEMPS_USERTHERMISTOR0. If you span definition over multiple lines, make sure to end each line, except the last, with a backslash. The table format is {{adc1,temp1},{adc2,temp2}...} with increasing adc values. For more informations, read http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html

If you have a sprinter temperature table, you have to multiply the first value with 4 and the second with 8. This firmware works with increased precision, so the value read goes from 0 to 4095 and the temperature is temperature*8.

If you have a PTC thermistor instead of a NTC thermistor, keep the adc values increasing and use thermistor types 50-52 instead of 5-7!

Controlling temperature

Knowing the temperature, we can start controlling the heater. The firmware currently supports two methods.

Bang-Bang control

This is the method used for a heated bed and for the extruder, if you set heat manager to 0. The principle is very simple. If the measured temperature is below the target temperature, the heater is set at full power. If the temperature rises above the target temperature, the heater is turned off.

Pros: Simple Cons: Temperature oscillates around the target temperature. For the heated bed, this is no real problem. If you are using PLA this is no big issue. With ABS you may want a more precise control.

PID control

For a better controlled temperature, the firmware uses a PID controller. This requires an output with PWM using an unused timer. Currently only RAMPS 1.0 doesn't have a free timer for PID control. PID control is only implemented for extruder. For a better overview, lets have a look at the parameter used:

/** \brief The maximum value, I-gain can contribute to the output.
A good value is slightly higher then the output needed for your temperature.
Values for startes:
130 => PLA for temperatures from 170-180°C
180 => ABS for temperatures around 240°C

The precise values may differ for different nozzle/resistor combination.
*/
#define EXT0_PID_INTEGRAL_DRIVE_MAX 130
/** \brief lower value for integral part

The I state should converge to the exact heater output needed for the target temperature.
To prevent a long deviation from the target zone, this value limits the lower value.
A good start is 30 lower than the optimal value. You need to leave room for cooling.
*/
#define EXT0_PID_INTEGRAL_DRIVE_MIN 50
/** P-gain in 0,01 units */
#define EXT0_PID_PGAIN 500
/** I-gain in 0,001 units

WATCH OUT: This value was in 0,01 units in earlier versions!
*/
#define EXT0_PID_IGAIN 1
/** Dgain in 0,01 units. */
#define EXT0_PID_DGAIN 3000
// maximum time the heater is can be switched on. Max = 255
#define EXT0_PID_MAX 255
/** \brief Set PID scaling

PID values assume a usable range from 0-255. This can be further limited to EXT0_PID_MAX by to methods.
Set the value to 0: Normal computation, just clip output to EXT0_PID_MAX if computed value is too high.
Set value to 1: Scale PID by EXT0_PID_MAX/256 and then clip to EXT0_PID_MAX.
If your EXT0_PID_MAX is low, you should prefer the second method.
*/
#define SCALE_PID_TO_MAX 0

I know, quite a number of factors, but don't worry. It's not that complicated if you understand, what the parameter are used for.

The first one you should look at is PID_MAX. It's the maximum PWM setting for your output. Normally, the heating resistor is selected, that a maximum value of 255 is allowed, meaning the full voltage (normally 12V) is send to the resistor. The lost energy at the resistor is U2/R. If you increase your voltage above the designed voltage, you may blow it with full settings. Reduce it in this case.

At the start, the controller behaves like the Bang-Bang controller, except that it uses PID_MAX instead of full power. Only if the current temperature is in a range of +/-10°C from the target temperature the real PID control starts. For the output, three terms P, I and D are computed and added to get the output value. The result is clipped to the range 0..PID_MAX

P-Term

The P-Term adds a value proportional to the temperature error.

P = (TTarget-TCurrent)P_GAIN0.01

The closer the target temperature is reached, the lower the term gets. It will always oscillate around a value some degrees below the target temperature if left alone.

I-Term

The I-Term is the most important one. The I-Term tries to find the perfect output for your temperature. It does this by incrementing/decrementing the output slowly. The speed is defined by the I_GAIN parameter. You can help a lot, if you limit the range of possible values to a known range INTEGRAL_DRIVE_MIN .. INTEGRAL_DRIVE_MAX. The min value should be at least 0 and the max not exceed 255. If you don't know what works for you, use these extreme values and watch the output with the command M203 S0. When you that the temperature stabilizes, take the mean value and add/sub 20. If you like to change temperatures, set the limits, so all possible ranges are enclosed with a safety factor.

If your I_GAIN is set to a good value, you will see that the oscillation gets smaller with time. Disturbances like different printing speed or blowing at the nozzle may increase the error again.

** D-Term**

The D-Term is the damping factor. With a good damping factor, your oscillations will slow down much faster and you get a much more stable system. Don't by show using more than 100%. The default is already 500%.

SCALE_PID_TO_MAX

This option only tells, when and how to scale down to DRIVE_MAX. If set to 0, the addition of the PID-Terms is clamped to 0..DRIVE_MAX. If you set this to 1, the PID Term gets clamped to 0..255 and is then scaled to 0..DRIVE_MAX.

What's the difference? If you have DRIVE_MAX=255 there is no difference. If you have found your perfect values for 12V and DRIV_MAX=255 and now want's to use 18V, you have to reduce DRIVE_MAX to DRIVE_MAX_OLD*(12^2/18^2)=113 to protected your resistor. If you want to keep your found factors, set DRIVE_MAX to 133 and SCALE_PID_TO_MAX 1. If you leave it 0, the found terms are too high and don't work any more.