NanoRama

Enter Your Project for a chance to win a Nano Grand Prize bundle for the most innovative use of Arduino plus a $400 shopping cart!

Submit an EntrySubmit an Entry  Back to homepage
Project14 Home
Monthly Themes
Monthly Theme Poll

 

What's for dinner? FR4 with a side of solder and flux gravy, sprinkled with 0603s.

Setup

 

My entry in the Arduino Day 2020: NanoRama: Which Board Are You Going to Use for Your Arduino Day Project?  giveaway was very straight forward:

 

"I am going to build a hot plate for reflow soldering with a CMH heater element. The GPIO is controlling power through a PWM controlled power FET and the ADC is reading back the temperature  of the hot plate through an thermistor (maybe more sensors if needed). With some calibration I can program a nice temperature profile for reflowing boards."

 

And to my surprise, I received an Arduino Nano Every in the mail and got to work. Features are creeping slowly across my desk. I am having 2 different hot plates in mind and planning to work with 2 different temperature sensors (more to that later). One hot plate will be just the bare MCH (this is the right order of letters for a metal-ceramic heater) element, which should have a very good response to power supply changes, but will only fit very small boards of ~20mm x 60mm. The 2nd hot plate will be an aluminum plate with 2 MCH elements mounted in series for more power and a larger heated area (70mm x 120mm = ~3x5 sqin) to solder larger boards.

 

Ingredients

 

- Arduino Nano Every (provided by Element14 as part of the NanoRama contest)

- different MCH elements - Metal Ceramic Heaters

- NTC

- PTFD

- Power Supply, a USB-C PD 95W (max 20V 4.5A)

- A USB-C PD Sink, here my trusted USB-C Power Delivery Sink (BCR)

- P-FET irf9358 http://www.farnell.com/datasheets/2613807.pdf

- N-FET 2N7002

- resistors and capacitors as needed.

 

Fail of the project

 

Sometimes parts at hand are not a good idea to use. I had some NTCs (type TDK NTCG164KF104FT1S) around, which I thought I could use as temperature sensors for this project. Nothing wrong with those. Well, they are 0603 size! This includes having solder joints at the part sitting tight on the hot plate, which is used to melt some solder. What could possibly go wrong.

 

And the temperature range is specified up to +150°C, so they would have a really hard time to show usable functionality at +260°C.

 

So, kids out there: Think before you build and read the datasheet before you use stuff.

 

To the rescue came a leaded PTFD and a TO35 style NTC, also with long leads, that help to have sufficient thermal distance (the electronics counterpart to social distancing in these trying times).

 

Temperature Probes

 

After some research, I decided to order these 2 temperature sensors:

- Littlefuse 104JG1J

A NTC Thermistor with 100 KOhm base resistance and 2% tolerance, max temperature +300°C in DO-35 glass encapsulated axial package.

Datasheet: http://www.farnell.com/datasheets/2613807.pdf

- TE NB-PTCO-024

A RTD Sensor, PTFD Type, 100 Ohm base resistance,max temperature 600°C 2mm x 5mm leaded package.

Datasheet: http://www.farnell.com/datasheets/2305684.pdf

 

Full Circuit

All ingredients sprinkled over the breadboard, resulted in the following circuit.

Circuit Diagram

The NTC is set up in the lower end of a resistive voltage divider. With the temperature up to 260°C, this gives a large enough voltage swing to feed directly into the ADC of the Arduino.

The PTFD needs a high resistor relative to its own resistance to limit the current to <1.4mA. With a resistance change of only 38.5Ohm/100°C the voltage swing is only ~100mV for 260°C. The added opamp in non-inverting configuration has a gain of 15.2, which brings the maximum voltage to 4.6V at 260°C. This is enough to have good resolution with the ADC.

The power switching circuit is a basic high-side P-FET (IRF9358). This is a dual FET, so I took both in parallel to reduce the total Rds_on and reduce the wasted power dissipation. A simple 2N7002 N-FET acts as the gate driver.

 

Let me know in the comments if you have any questions. I should go to the dark side and install Fritzing for proper Arduino documentation.

 

The Build

 

 

{gallery} The Build

Build Step 1: I built up the large hot plate with 2x 2 Ohm (1.9 Ohm to be precise) heater elements in series, mounted on the bottom of an aluminium plate. This is where the next issue started, but it is hard to tell by just looking at the picture. Later more...

Build Step 2: The heaters are fixed to the plate with duct-tape, real duct-tape made out of aluminium foil with a sticky side. I was concerned that the glue would give way when heated, but so far it is holding together wonderful.

Build Step 3: The plate got mounted to the protoboard, nicely showcasing the element14 sticker. The 2 screws provide enough heat isolation, that the base of the protoboard does not get more than a little warm.

Build Step 4: For the FET to switch the power, I used an old version of the USB-C PC board with only the FET+ support components.

Build Step 5: The plate was supposed to draw 5A from my 20V USB-C PD power supply and was wondering why it did not heat up as fast as expected. It turned out that one of the elements was 19 Ohm instead of 1.9 Ohm as I thought was written on it (totally omitting the missing comma). As luck will have it, the 19 Ohm heater in parallel with a 5.1 Ohm heater element is almost exactly 4 Ohm again. So my test could continue almost as planned. I just moved the 5.1 Ohm element closer to the center as it accounts for 80% of the heating power.

Build Step 6: Everything, including the Arduino Nano Every, mounted on a piece of laminated particle board with 2 hooks holding down the protoboard with the big hot plate to avoid tipping over and burn holes in my antistactic mat. It would still burn the laminate, but that would be just smelly and not an ugly mess. This is the first full setup with the small heater connected (top right) that is used for the initial temperature profile tests. One can even see the stain dots on the heater, where I put some small solder pieces to see them melt. Which they did. 20V 4.8A power is supplied by the green USB-C PD board. The circuit to the left of the Arduino is the opamp and the PTFD heater, which is patiently waiting for its testing.

 

 

Code

 

I was debating to use the PID library, but decided to roll my own code. And to make life easier, I am going to ignore the letters P and I and only use the derivative term of the temperature measurement, in the form of the rate of temperature change, for my control loop. Time passes and I learnt that I cannot ignore the letter P for the proportional term. The change in temperature could be perfect, but with an unknown offset, the system could do whatever it wants to.

The main loop is comparing the measured temperature rise against the set temperature rise defined in the temperature profile. Heating is turned on or off accordingly. Heating happens through a basic PWM signal with a limit to the maximum on-time. This is a safety feature, as a 100% heat cycle might break the MCH element without an additional sink for the heat. To avoid overshot the PWM signal is further limited when the measured temperature is reaching the next fix point in the profile.

Temperature measurement is done in 2 steps. The ADC measures the voltage of the resistor divider consisting of a fixed resistor and the temperature element and the resistance of the element is calculated. Then the temperature is calculated through approximation formulas for the different thermal elements.

Formulas for the NTC:

Resistor divider with the NTC on the high side connected to the 5V rail:

Approximation formula for the NTC element:  T=(Tβ)/(β+T0×lnβ/R0) −273.15°C

Formulas for the PTFD:

Resistor divider with the PTFD on the low side, including a 15.1643x non-inverting opamp to fit the signal better into the dynamic range of the ADC:

Approximation formula for the PTFD element:  T=2.5974×R−259.74

 

Some telemetry data is printed to the serial port and the PWM state is mirrored at the on-board LED for visual feedback.

 

Probably not the most elegant code, I will always be a hardware person.

// Arduino Hot Plate
// written by Wolfgang Friedrich
// 4. May 2020

// Target Platform: Arduino Nano Every

#include <Arduino.h>

#define LED_PIN 13                // 13 = LED; 5 pin for PWM
#define PWM_PIN 4                // 5 pin for PWM

//Temperature Sensor type
#define NTC 1
#define PTFD 2

const uint8_t        NTCInput  = A7;
const uint8_t        PTFDInput = A6;


const uint32_t SINGLE_TICK = 100;        // in millisec
const uint8_t MAX_SEGMENTS = 7;            // 
const double profile_time[]        = { 1, 90, 180, 210, 240, 270, 300};    // seconds into the profile
//const double profile_time[]        = {  1, 9, 18, 21, 24, 27, 30};    // TEST seconds into the profile
//const double profile_temp[]    =  { 25, 50, 60, 60, 80, 40,  25}; // TEST temperature at time point
const double profile_temp[]    =  { 25, 150, 200, 217, 260, 217,  25}; // temperature at time point
//const double profile_temp[]    =  { 25, 100, 120, 170, 200, 100,  25}; //  TEST temperature at time point

const uint8_t PWM_MAX = 10;
const uint8_t AVG_COUNT = 10;


uint32_t    current_tick = 0;
uint32_t    last_tick = 0;
uint32_t    tick_counter = 0;
uint32_t    actual_time = 0;
bool        tick_click = false;
uint8_t        profile_segment = 0;
uint8_t        pwm_count = 0;
uint8_t        pwm_max_on = 8;
double        set_temp_rise = 0;            // in deg/sec
double        measured_temp_rise = 0;        // in deg/sec
double        measured_temp = 0;            // in deg
double        last_measured_temp = 0;        // in deg
double        expected_temp = 0;            // in deg

uint8_t        incomingByte = 0;


double readTemperature (uint8_t channel, uint8_t average, uint8_t type) {

double value;
double voltage, resistance;
uint8_t i;

const double beta = 3892;
const double T0 = 298.15;
const double R0 = 100000;

    value = 0;
    for (i=0; i < average; i++) {
        value = value + analogRead(channel);
    }
    value = value / average;

    if ( type == NTC ) {
        voltage = value * 5.0 / 1023.0;                    // for Arduino Nano Every; 10 bit ADC
        resistance = 100000/(5.0/voltage-1);
        value = T0*beta / (beta + T0 * log(resistance/R0)) - 273.15;
    }
    else if ( type == PTFD ) {
        voltage = value * 5.0 / 1023.0 / 15.1643;        // for Arduino Nano Every; 10 bit ADC
        // opamp gain 15.1643 with Rin = 7.06K Rf=100K
        resistance = 2350/(3.3/(voltage)-1);
        value = 2.5974 * resistance - 259.74;            // linear approximation
        
//            Serial.print(F("\n\rValue: "));
//            Serial.print(value);
//            Serial.println(F(" degC "));
//
//            Serial.print(F("Voltage: "));
//            Serial.print(voltage);
//            Serial.println(F(" V "));
//
//            Serial.print(F("Resistance: "));
//            Serial.print(resistance);
//            Serial.println(F(" Ohm "));

        
    }
    else {
        value = 0;
    }
    return value;
}


void setup() {

    Serial.begin(115200);
    Serial.println("");
    Serial.println(F("HotPlate Controller v0.1"));
    Serial.println(F("What's for dinner?"));
    Serial.println(F("FR4 with a side of solder and flux gravy, sprinkled with 0603s."));

    pinMode (LED_PIN,  OUTPUT);
    pinMode (PWM_PIN,  OUTPUT);
    
    digitalWrite (LED_PIN, LOW);
    digitalWrite (PWM_PIN, LOW);

    measured_temp = readTemperature( NTCInput, 10, NTC ); // if more than one

    Serial.print ("Temperature: ");
    Serial.print(measured_temp, 2);
    Serial.println(F(" degC"));

    // Use button to start
    Serial.println(F("Start.... [press key]") );
    while (Serial.available() == 0) {};
    incomingByte = Serial.read();

}

void loop() {


    current_tick = millis();

    if (( current_tick - last_tick ) >= SINGLE_TICK ) {
        tick_counter++;
        last_tick = current_tick;
        tick_click = true;
    }
    else {
        tick_click = false;
    }

    actual_time = tick_counter*SINGLE_TICK/1000;    // in seconds
    
    if ( tick_click ) {
//        Serial.print (tick_counter);
    
        if ( (tick_counter) == profile_time[profile_segment]*1000/SINGLE_TICK ) {
            profile_segment = (profile_segment + 1) % MAX_SEGMENTS;
            Serial.print(F("\n\r Seg "));
            Serial.print(profile_segment);
            Serial.print (" | ");
            
            Serial.print(F("Time_set: "));
            Serial.print(profile_time[profile_segment], 2);
            Serial.print(F(" sec | "));

            Serial.print(F("T_set: "));
            Serial.print(profile_temp[profile_segment], 2);
            Serial.print(F(" degC | "));

            set_temp_rise = (double) ( (profile_temp[profile_segment]-profile_temp[profile_segment-1]) / (profile_time[profile_segment]-profile_time[profile_segment-1]) );
            Serial.print(F("dT_set: "));
            Serial.print(set_temp_rise, 2);
            Serial.println(F(" degC/sec"));
            
        }

        Serial.print ("Time: ");
        Serial.print (actual_time);
        Serial.print (" sec(");
        Serial.print (tick_counter);
        Serial.print (") | ");
    
        // read temperature
        last_measured_temp = measured_temp;
    
        measured_temp = readTemperature( NTCInput, AVG_COUNT , NTC); // if more than one
    //    measured_temp = readTemperature( PTFDInput, AVG_COUNT , PTFD); // if more than one
    
        Serial.print ("T_measure: ");
        Serial.print(measured_temp, 2);
        Serial.print(F(" degC | "));
            
        // calc measured temp rise
        measured_temp_rise = ( measured_temp - last_measured_temp ) *100 / SINGLE_TICK ;    // in degC/sec
    
        Serial.print(F("dT_measure: "));
        Serial.print(measured_temp_rise, 2);
        Serial.print(F(" degC/sec | "));
    
        pwm_count = (pwm_count + 1) % PWM_MAX;
        
        // limit heating when in cool down cycle
        if ( set_temp_rise < 0 ) {
            pwm_max_on = 1;
        }
        // slow down heating when closer than 10 degree to target temp
        if ( (profile_temp[profile_segment] - measured_temp) < 5 ) {
            pwm_max_on = PWM_MAX / 4;
        }
        else {
            pwm_max_on = PWM_MAX -1;// / 2;
        }
        
        expected_temp = (profile_temp[profile_segment]-profile_temp[profile_segment-1])/(profile_time[profile_segment]-profile_time[profile_segment-1])*(actual_time-profile_time[profile_segment-1]) + profile_temp[profile_segment-1];
    
        Serial.print ("T_expected: ");
        Serial.print(expected_temp, 2);
        Serial.print(F(" degC | "));
        
        Serial.print ("PWM_count: ");
        Serial.print(pwm_count );
        Serial.print(F(" | "));
        
        // turn heater on or off
        if ( ( measured_temp_rise < set_temp_rise) && (pwm_count < pwm_max_on) && (measured_temp < profile_temp[profile_segment])
                && ( measured_temp <  expected_temp + 5 ) && (set_temp_rise > 0 )
            ) {
            digitalWrite (PWM_PIN, HIGH);        // to open the big MOSFET
            digitalWrite (LED_PIN, HIGH);        // LED on
            Serial.print(F("PWM: 1  \r"));
        }
        else {
            digitalWrite (PWM_PIN, LOW);        // to close the big MOSFET
            digitalWrite (LED_PIN, LOW);        // LED off
            Serial.print(F("PWM: 0  \r"));
        }
    
        Serial.println ();
    
    }
    
    while (tick_counter >= profile_time[MAX_SEGMENTS-1]*1000/SINGLE_TICK-1) {
        // stop program at the end of the profile
        digitalWrite (PWM_PIN, LOW);
        digitalWrite (LED_PIN, LOW);        // LED off
        Serial.print(F("\n\r THE END!  "));
        measured_temp = readTemperature( NTCInput, 10, NTC ); // if more than one
    
        Serial.print ("T_measure: ");
        Serial.print(measured_temp, 2);
        Serial.print(F(" degC"));

        // Use button to re-start instead of delay.
        
        delay(5000);
    }
    
}

 

Temperature Profile

I am using a basic lead free temperature profile as shown in the graph.

 

Lead Free Reflow Temperature Profile

 

And here are temperature measurements of 2 different process runs with lots of interesting things to note.

First is the yellow trace, which was a bare PCB to test if the hot plate is able to reach max temperature with a PCB as a heat mass.

The measurements get more jittery at higher temp, which is because the NTC curve gets flatter (what a bad pun) at higher temperatures. Beyond 300 sec are less measurements, so it looks cleaner too.

At the time axis, the thicker line indicates a PWM on cycle, so the heater is heating. At the start, the temperature rise is very steep, so we are seeing a big overshoot in temperature.

During the cooling cycle, the temperature is dropping fast initially and when it was too far away from the desired temperature, it started heating again for a significant period. This is leading to a 20° higher temperature during the rest of the cooldown compared to the green trace without heat during cooldown. The cooldown is convection only with a nice exponential drop.

For the green trace, which was a real PCB+components solder cycle, I reduced the limit above the profile temperature and did not allow heating during cooldown.

The temperature is close enough to the profile, that I would call the regulation algorithm a success.

Reflow Temperature Measurements

 

Cooking a board

As it turned out, the 95W power is not enough to heat the big aluminium plate quick enough to match the profile. So I did all tests and board soldering on a single small heater element. Luckily my test PCB is small enough to fit almost nicely on the small heater.

An out-of-focus picture with a leaded solder piece and lead-free paste already melted and the lead-free piece not yet melted

Test board with 2 pieces of solder and some solder paste.

A good board, that is prepared with solder paste through the stencil and all components pick&placed.

Prepared board pre-solder stage.

And a video showing the soldering process from about 200°C to 260°C and cooling back down below 150°C. As you can see the extension with the 6-pin through hole header and the 4 resistors does not fit on the heating element and the resistors do not get soldered in the process. This is a 4X time-lapse.

 

 

Another video with a 2nd board, that has too much solder paste and you can see some bridges forming. they get resolved later through re-work with plenty of flux and a pencil-tip soldering iron. I wanted to have a shot from a higher angle, but did not have a good stand, people with motion-sickness should not watch this one. My apology.

 

 

Still hungry?

Arduino Hot Plate (2) - What's for dessert?

The culinary experience continues:

Arduino Hot Plate (3) - cooking with AC

 

For the future:

  1. Get the big plate going with a stronger power supply. Then I could reflow larger boards as well.
  2. Add a fan and control code to match the cool-down cycle better to the profile.
  3. Add a small display to make it independent from a PC.