Shop OBEX P1 Docs P2 Docs Learn Events
3D printer thermo controller for extruder — Parallax Forums

3D printer thermo controller for extruder

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.

Comments

  • ManAtWork

    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;
    }
    
  • Hello Bruce,

    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°C   
            
    
    The 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.
Sign In or Register to comment.