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.
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
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=(T0×β)/(β+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.
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.
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
A good board, that is prepared with solder paste through the stencil and all components pick&placed.
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:
- Get the big plate going with a stronger power supply. Then I could reflow larger boards as well.
- Add a fan and control code to match the cool-down cycle better to the profile.
- Add a small display to make it independent from a PC.
Top Comments