3D printer thermo controller for extruder
ManAtWork
Posts: 2,262
in Propeller 1
Hi,
I'm looking for a simple way to control the temperature of an MK8 extruder. It has an 100k NTC thermistor and a heating element for 24V. It should be no big deal to measure the temperature with an RC decay circuit and switch the heating resistor with a MOSFET and a bit of PID software running in a propeller, or even a simple 2-point on/off controller. Given enough time I could develop one myself. But I'm sure somebody has already done this before. Any hints or pointers welcome.
I'm looking for a simple way to control the temperature of an MK8 extruder. It has an 100k NTC thermistor and a heating element for 24V. It should be no big deal to measure the temperature with an RC decay circuit and switch the heating resistor with a MOSFET and a bit of PID software running in a propeller, or even a simple 2-point on/off controller. Given enough time I could develop one myself. But I'm sure somebody has already done this before. Any hints or pointers welcome.

Comments
This is not for an RC decay circuit, but you may change your mind.
Here is some C snippets, if you want the full files, just let me know.
// The number of value pairs in the table. #define TABLE_SIZE 53 // // This is a table of ADC readings and corresponding temperature values for both the // extruder and heated build platform. In determining the thermistor resistance values, // data was extracted from the EPCOS B57560G1104F000 datasheet, because that is the // thermistor being utilized for both the extruder and heated build platform. // // During the calculations of this table, a resistor having a nominal value of 4.7K, // with a 10% tolerance was utilized, however the calculations were performed and based // upon the actual resistance, which was 4,602 Ohms. LOOKUPTEMP lookup_temp[TABLE_SIZE] = { {.sample = 1591, .temp = 20}, {.sample = 1577, .temp = 25}, {.sample = 1560, .temp = 30}, {.sample = 1540, .temp = 35}, {.sample = 1517, .temp = 40}, {.sample = 1490, .temp = 45}, {.sample = 1459, .temp = 50}, {.sample = 1424, .temp = 55}, {.sample = 1386, .temp = 60}, {.sample = 1343, .temp = 65}, {.sample = 1296, .temp = 70}, {.sample = 1245, .temp = 75}, {.sample = 1191, .temp = 80}, {.sample = 1135, .temp = 85}, {.sample = 1076, .temp = 90}, {.sample = 1016, .temp = 95}, {.sample = 956, .temp = 100}, {.sample = 895, .temp = 105}, {.sample = 835, .temp = 110}, {.sample = 776, .temp = 115}, {.sample = 719, .temp = 120}, {.sample = 665, .temp = 125}, {.sample = 612, .temp = 130}, {.sample = 563, .temp = 135}, {.sample = 517, .temp = 140}, {.sample = 474, .temp = 145}, {.sample = 434, .temp = 150}, {.sample = 397, .temp = 155}, {.sample = 362, .temp = 160}, {.sample = 331, .temp = 165}, {.sample = 302, .temp = 170}, {.sample = 276, .temp = 175}, {.sample = 252, .temp = 180}, {.sample = 231, .temp = 185}, {.sample = 211, .temp = 190}, {.sample = 193, .temp = 195}, {.sample = 177, .temp = 200}, {.sample = 162, .temp = 205}, {.sample = 149, .temp = 210}, {.sample = 136, .temp = 215}, {.sample = 125, .temp = 220}, {.sample = 115, .temp = 225}, {.sample = 106, .temp = 230}, {.sample = 98, .temp = 235}, {.sample = 90, .temp = 240}, {.sample = 83, .temp = 245}, {.sample = 77, .temp = 250}, {.sample = 71, .temp = 255}, {.sample = 66, .temp = 260}, {.sample = 61, .temp = 265}, {.sample = 57, .temp = 270}, {.sample = 53, .temp = 275}, {.sample = 49, .temp = 280}, }; double extruder_celsius; double bed_celsius; /**************************************************************************/ /*! @brief Given the appropriate channel, update the proper external temperature variables. */ /**************************************************************************/ void UpdateTemp(uint8_t channel) { double samples[NUMSAMPLES]; uint8_t i; double average; // take N samples in a row, with a slight delay for (i = 0; i < NUMSAMPLES; i++) { // read channel the specified channel samples[i] = readADC_SingleEnded(channel); pause(10); } // average all the samples out average = 0; for(i = 0; i < NUMSAMPLES; i++) { average += samples[i]; } average /= NUMSAMPLES; if(channel == 0) { extruder_celsius = lookupTemp(lookup_temp, average); } if(channel == 1) { bed_celsius = lookupTemp(lookup_temp, average); } } /**************************************************************************/ /*! @brief Given an ADC sample average, lookup the appropriate temperature. */ /**************************************************************************/ double lookupTemp(const LOOKUPTEMP *table, double sample) { int index; index = 0; // find the two points in the table to use while(index < TABLE_SIZE && sample < table[index].sample) { index++; } // make sure the point isn't past the end of the table if( index == TABLE_SIZE) { return table[index - 1].temp; } // make sure the point isn't before the beginning of the table if(index == 0) { return table[index].temp; } return table[index].temp; }thanks for the code but I finally wrote my own because I'm not using C, I use a different sensor and I have no real ADC on my board. Because of the exponential R vs. temperature curve it's still quite accurate with only the RC decay circuit. I use a PD controller because a simple on/off or P controller results in enormous overshot due to the delay in the thermal path. PD gives a much better response and PID is not necessary becausse nominal/actual deviation is less than 1°C.
{{ ExtruderHeater.spin temperature controller for 3D-printer extruder uses an RC decay timer circuit (single slope EDC) and a 100k NTC resistor for temperature measurement 33nF capacitor to ground NTC + 100R to VDD }} CON _clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz _xinfreq = 5_000_000 clkfrq = 80_000_000 baudRate = 115_000 pinRC = 0 ' PA0 I/O RC decay circuit pinOut = 1 ' PA1 Out Smart FET for heating modeA = %01100<<26 + pinRC ' counter mode: NEG detect t_1ms = clkfrq / 1000 t_10ms = clkfrq / 100 Tnom = 190 ' nominal temperature in °C kp = 5 ' proportional gain in %/°C kd = 20 ' differential gain in %/(°C/s) VAR long buffer[10] long t ' temperature in 0.1°C steps long dt ' temperature rise/fall in 0.1°C/s long bi ' buffer index long pn ' nominal power in % long pa ' actual power OBJ 'com: "FullDuplexSerial" PUB main | r, lt, c 'com.Start(31, 30, 0, baudRate) DIRA[pinOut]:= 1 c:= CNT repeat r:= MeasureR t:= RtoTemp (r) lt:= buffer[bi] buffer[bi++]:= t bi//= 10 dt:= t - lt pn:= ((Tnom*10 - t) * kp - dt * kd) / 10 pn#>= 0 pn<#= 100 pa+= pn if pa => 100 pa-= 100 OUTA[pinOut]:= 1 else OUTA[pinOut]:= 0 {com.str (String ("r=")) com.dec (r) com.str (String (" t=")) com.dec (t/10) com.tx (",") com.dec (t//10) com.str (String (" d=")) com.dec (dt*kd) com.tx (13) } waitcnt (c+= clkfrq / 10) PRI MeasureR : R ' measure NTC resistor value DIRA[pinRC]:= 1 FRQA:= 1 PHSA:= 0 waitcnt (t_1ms + CNT) CTRA:= modeA DIRA[pinRC]:= 0 waitcnt (t_10ms + CNT) CTRA:= 0 R:= (PHSA-563) * 100 / 142 ' gain and offset compensation PRI RtoTemp (R): Temp | i, l, h ' calculate temperature from resistance value ' unit is 0.1°C h:= tableR[0] if R > h return -999 ' underflow repeat i from 1 to 42 l:= tableR[i] if R > l return i*50 - (R-l)*50 / (h-l) h:= l return +999 ' overflow DAT tableR long 325600, 253400, 198700, 157000, 124900, 100000, 80590, 65350, 53300, 43720 ' 0..45°C long 36060, 29890, 24900, 20840, 17350, 14810, 12560, 10700, 9154, 7860 ' 50..95°C long 6773, 5858, 5083, 4426, 3866, 3387, 2977, 2624, 2319, 2055, 1826 ' 100..150°C long 1623, 1442, 1281, 1138, 1011, 899, 799, 710, 630, 560, 498, 442 ' 155..210°CThe R vs. temp table is just a rough guess. I extrapolated it from a datasheet of a different 10k NTC I had. If somebody has the exact values please let me know.