-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathIntelligent_hotplate.ino
527 lines (445 loc) · 17.4 KB
/
Intelligent_hotplate.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
/*
This is a rework of the Electronoobs DIY reflow hotplate project found here. https://electronoobs.com/eng_arduino_tut155.php
I did a major rewrite of the control code and added PID control of the heater. This made a big difference to the heating and
in my opinion helps a lot.
I also used a small electric hot plate rather than a cloths iron because it was cheap and this way I can make changes to that
portion of the system if I like. https://www.amazon.ca/gp/product/B08R6F5JH8/ref=ppx_yo_dt_b_search_asin_image?ie=UTF8&psc=1
Started April 20th 2022
December 2022
Major changes..
Added additional cooking mode. This means that this is no longer just for SMD soldering.
There is a "Normal mode" that is just like a regular hot plate. You set a setting number that is anywhere between 0 - 100%
that will PWM modulate the SSR on and off at that percentage. Just like a standard hotplate. This mode does not look at
the thermocouple for feedback at all.
There is "intelligent mode" that uses the thermocouple to set the hotplate at a specific temperature and keep it there until
the hotplate is turned off.
Turning off the hotplate is done by pressing button 2 at any point.
*/
// Libraries
#include "max6675.h" //Download it here: http://electronoobs.com/eng_arduino_max6675.php
#include <Wire.h>
#include "LiquidCrystal_I2C.h" //Download it here: http://electronoobs.com/eng_arduino_liq_crystal.php
#include <PID_v1.h>
#include <RotaryEncoder.h>
#include <Adafruit_NeoPixel.h>
// definitions - pin definitions and constants
#define neopixelPIN 8 // The output pin for the Neopixels
#define NUMPIXELS 8 // This how many Neopixels are connected to PIN
#define lowTempThreshold 40 // temperature threshold to start turn the LEDs from red to green
#define highTempThreshold 55 // The temperature that the LEDs are fully red
#define SSR 9 // the output pin that the SSR is connected to
#define thermoDO 4 // Data pin for MAX6675 (thermocouple amp)
#define thermoCS 5 // CS pin for MAX6675
#define thermoCLK 6 // Clock pin for MAX6675
#define but_1 10 // Button 1 input
#define but_2 11 // This pin will be used to end cooking
#define DT 2 // Data pin for encoder
#define CLK 3 // Clock pin for encoder
// Variables
double Setpoint, Input, Output; // variable needed for the PID loop
unsigned long LEDtimer; // used to determine when to update the LEDs
unsigned long LEDinterval = 500; // how often in milliseconds to update the LEDs
unsigned int millis_before, millis_before_2; // used for time tracking in the loop
unsigned int millis_now = 0; // used to keep track of the current time of the loop
int refresh_rate = 1000; // how often to update the display in milliseconds
int temp_refresh_rate = 100; // how often to check the temperature in milliseconds
unsigned int seconds = 0; // used in the display to show how long the sequence has been running
bool but_1_state = false; // used to track of the button has been pushed. used for debouncing the button
unsigned long but_1_timer = 0; // used for checking the time for debouncing the button push
int max_temp = 260; // ****** this is the high temperature set point for SMD mode. *******
float temperature = 0; // this is the variable that holds the current temperature reading
int heatStage = 0; // used for keeping track of where we are in the heating sequence
int stage_2_set_point = 150; // this is the "soak" temperature set point
unsigned long stage2Timer = 0; // used to keeping track of when we went into stage two
unsigned long stage4Timer = 0; // used for keeping track of when we went into stage 4
unsigned long soakTime = 100; // how long to soak the board for in seconds
int reflowTime = 60; // how long to reflow the board for in seconds
int cookMode = 1; // This is used to know what mode is selected
boolean cookStatus = 0; // this is used to know if we are actively cooking
String Names[] = // this is the text displayed in the display in the various stages for SMD cooking mode
{
"Off",
"Heat",
"Soak",
"Blast", // I am sure there is a real name for this stage but I dont know it and this seemed cool...
"Reflow",
};
// instantiate objects
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); // Start MAX6675 SPI communication
LiquidCrystal_I2C lcd(0x27, 20, 4); // Address could be 0x3f or 0x27
PID myPID(&Input, &Output, &Setpoint, 7, .01, 0, DIRECT); // create the PID object
RotaryEncoder myEnc(DT, CLK, RotaryEncoder::LatchMode::TWO03); // setup the encoder
Adafruit_NeoPixel pixels(NUMPIXELS, neopixelPIN, NEO_GRB + NEO_KHZ800);
// Setup function is executed only once at startup
void setup()
{
myPID.SetMode(AUTOMATIC); // turn the PID controller on
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixels.clear(); // Set all pixel colors to 'off'
pinMode(SSR, OUTPUT); // Define the OUTPUT for the Solid State Relay
digitalWrite(SSR, LOW); // Start with the SSR off
pinMode(but_1, INPUT_PULLUP); // Setup the button input
pinMode(but_2, INPUT_PULLUP); // Setup the end button input
Serial.begin(115200);
lcd.init(); // Init the LCD
lcd.backlight(); // Activate backlight
millis_before = millis();
millis_now = millis();
displayTemperature(); // display the current temperature on the display and LEDs
displayMode();
}
// The main loop
void loop()
{
// Read encoder and change the cooking mode variable based on the direction
// the encoder is turned
displayTemperature(); // display the current temperature on the display and LEDs
myEnc.tick(); // maintain the encoder object
int temp = myEnc.getPosition(); // get the encoder reading
// if the encoder is turned, then increment or decrement
// the cookMode variable
if (temp > 0)
{
cookMode++;
if (cookMode >= 4)
{
cookMode = 1;
}
displayMode();
myEnc.setPosition(0);
temp = 0;
}
else if (temp < 0)
{
cookMode--;
if (cookMode <= 0)
{
cookMode = 3;
}
displayMode();
myEnc.setPosition(0);
temp = 0;
}
// if the encoder button is pressed then call the function
// that executes the selected cooking mode.
if (!digitalRead(but_1))
{
switch (cookMode)
{
case 1:
regularCook();
break;
case 2:
intelligentCook();
break;
case 3:
smdCook();
break;
default:
break;
}
}
} // end of void loop
// **************** Functions begin here **************************
// Check the temperature of the cooktop surface via the thermocouple
// and change the LEDs color based on the temperature reading
void displayTemperature()
{
if (millis() - LEDtimer > LEDinterval) // Check to see if it is time to update the LEDs with the most recent temperature
{
int tempReading = thermocouple.readCelsius(); // read the temperature sensor
if (tempReading > highTempThreshold)
{ // if temperature is HIGHER than tempThreshold turn the LEDs all RED
sendColors(150, 0, 0);
}
else if (tempReading < lowTempThreshold)
{ // if temperature is LOWER than tempThreshold turn the LEDs all GREEN
sendColors(0, 150, 0);
}
else if (tempReading >= lowTempThreshold && tempReading <= highTempThreshold)
{ // if the temperature is in between the low and high temperature thresholds then
// transition from green to red
int temperatureColor = map(tempReading, lowTempThreshold, highTempThreshold + 1, 0, 150);
sendColors(temperatureColor, 150 - temperatureColor, 0);
}
lcd.setCursor(0, 3); // Move the course to the bottom right corner of the screen
lcd.print("Temp: ");
lcd.print(tempReading, 1); // print the current temperature to the screen
LEDtimer = millis(); // take note of the current time for the next pass of this function
}
} // end of displayTemperature function
// This function displays the currently selected cooking mode on the LCD
void displayMode()
{
// lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select Mode...");
switch (cookMode)
{
case 1:
lcd.setCursor(0, 2);
lcd.print("Standard Mode ");
break;
case 2:
lcd.setCursor(0, 2);
lcd.print("Intelligent Mode");
break;
case 3:
lcd.setCursor(0, 2);
lcd.print("SMD Mode ");
break;
default:
lcd.setCursor(0, 2);
lcd.print(" FAULT! ");
break;
}
}
// This function will cook using the thermocouple
// and keep the burner at a specific temperature
void intelligentCook()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Intelli-Cook");
lcd.setCursor(0, 2);
lcd.print("Set Temp:");
int temperatureSetPoint = 0;
int temp = 0;
while (1) // start of the loop for the intelligent cooking loop
{
myEnc.tick();
temp = myEnc.getPosition();
if (temp > 0) // keep the temperature within bounds
{
temperatureSetPoint = temperatureSetPoint + 5;
if (temperatureSetPoint > 600)
{
temperatureSetPoint = 600;
}
lcd.setCursor(10, 2);
lcd.print(temperatureSetPoint);
myEnc.setPosition(0);
}
else if (temp < 0)
{
temperatureSetPoint = temperatureSetPoint - 5;
if (temperatureSetPoint < 0)
{
temperatureSetPoint = 0;
}
lcd.setCursor(10, 2);
lcd.print(temperatureSetPoint);
myEnc.setPosition(0);
}
// this is the code that manages the cooking
millis_now = millis(); // track the current time
if (millis_now - millis_before_2 > temp_refresh_rate) // if it has been more than RefreshRate then get the current temperature
{
millis_before_2 = millis_now; // track the current time for the next loop
temperature = thermocouple.readCelsius(); // read the temperature sensor
Input = temperature; // set the input field for the PID loop
//
Setpoint = temperatureSetPoint; // set the set point for the PID
myPID.Compute(); // run the compute for the pid using the current variables
analogWrite(SSR, Output); // We change the Duty Cycle of the relay
}
// If button 2 is pressed, exit cooking.
if (!digitalRead(but_2))
{
analogWrite(SSR, 0);
delay(500);
displayMode();
break;
}
displayTemperature(); // display the current temperature on the display and LEDs
} // end of While loop for the intelligent cook mode
} // ends of intelligentCook function
// This function will cook like a normal burner
// and set the burner at a percent of maximum
void regularCook()
{
int percent = 0; // used for setting the PWM percent for cooking
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Regular Cook");
lcd.setCursor(0, 2);
lcd.print("Set point:");
while (1) // start loop for the heating and wait until the exit button is pressed to end cooking
{
myEnc.tick();
int temp = myEnc.getPosition();
if (temp > 0)
{
percent++;
if (percent > 100)
{
percent = 100;
}
myEnc.setPosition(0);
}
else if (temp < 0)
{
percent--;
if (percent < 0)
{
percent = 0;
}
myEnc.setPosition(0);
}
int mapValue = map(percent, 0, 100, 0, 255);
analogWrite(SSR, mapValue);
Serial.print("mapValue =");
Serial.println(mapValue);
lcd.setCursor(11, 2);
lcd.print(String(percent));
// if the end button is pressed then exit cooking
if (!digitalRead(but_2))
{
analogWrite(SSR, 0);
delay(100);
displayMode();
break;
}
displayTemperature(); // display the current temperature on the display and LEDs
} // end of While loop
} // end of regular cook function
// This function is used for cooking SMD PC boards
// using a SMD reflow profile
void smdCook()
{
seconds = 0;
heatStage = 1;
// start of the loop for the SMD cooking
while (1)
{
displayTemperature(); // display the current temperature on the display and LEDs
// millis_now = millis(); // track the current time
if (millis() - millis_before_2 > temp_refresh_rate) // if it has been more than RefreshRate then get the current temperature
{
millis_before_2 = millis(); // track the current time for the next loop
temperature = thermocouple.readCelsius(); // read the temperature sensor
Input = temperature; // set the input field for the PID loop
}
// This is the first heating stage handler
if (heatStage == 1)
{
Setpoint = stage_2_set_point; // set the set point for the PID
myPID.Compute(); // run the compute again because we just made a change to the set point
analogWrite(SSR, Output); // We change the Duty Cycle of the relay
if (temperature >= stage_2_set_point) // check to see if se need to move onto the next stage
{
heatStage++; // increment the stage counter and
stage2Timer = seconds; // track the current time
}
}
// This is the second heating stage handler
if (heatStage == 2)
{
myPID.Compute(); // run the PID compute cycle
analogWrite(SSR, Output); // We change the Duty Cycle of the relay
int stage2Temp = seconds - stage2Timer; // see how long we have been in stage two
if (stage2Temp > soakTime) // if longer than the soakTime variable
{
heatStage++; // move to the next stage
}
}
// This is the third heating stage handler
if (heatStage == 3)
{
Setpoint = max_temp; // now set the set pont to the max_temp setting
myPID.Compute(); // recalculate the PID value because we just changed the Setpoint
analogWrite(SSR, Output); // We change the Duty Cycle of the relay
if (temperature >= max_temp) // check to see if we reached the max_temp set point
{
heatStage++; // if we did move on to the next stage
stage4Timer = seconds; // take note of the time we moved into the next stage
}
}
// This is the forth heating stage handler
if (heatStage == 4)
{
myPID.Compute(); // run the PID compute cycle
analogWrite(SSR, Output); // We change the Duty Cycle of the relay
int temp = seconds - stage4Timer; // see how long we have been in this stage
if (temp > reflowTime) // if we have been here for the full time
{
heatStage++; // move on to the next stage
analogWrite(SSR, LOW); // turn of the relay
}
}
// This is the fifth and last heating stage handler
if (heatStage == 5)
{
lcd.clear(); // clear the display and write complete to let the user know we are done
lcd.setCursor(0, 1);
lcd.print(" COMPLETE ");
seconds = 0; // Reset timer
heatStage = 0;
delay(5000);
}
if (millis() - millis_before > refresh_rate) // every second, update the display
{
millis_before = millis(); // track the current time
seconds++; // increment the time counter
Serial.println(temperature); // output the temperature to the serial monitor for plotting
if (heatStage == 0) // if we are in stage zero then update display for that stage
{
digitalWrite(SSR, LOW);
lcd.clear();
lcd.setCursor(0, 0);
if (temperature > 50) // check the temperature and display Hot if above 50 degrees
{
lcd.print(" HOT!!! ");
}
else // if not, just say Not Running
{
lcd.print("NOT RUNNING");
}
lcd.setCursor(0, 1);
lcd.print("SSR: OFF");
lcd.setCursor(0, 2);
lcd.print("Stage - ");
lcd.print(Names[heatStage]);
// lcd.setCursor(0, 3);
// lcd.print("Temp: ");
// lcd.print(temperature, 1);
} // end of heatStage == 0
else if (heatStage > 0 && heatStage < 7) // display the appropriate thing if a sequence is running
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RUNNING - PWM: ");
lcd.print(Output, 1);
lcd.setCursor(0, 1);
lcd.print("SSR: ON ");
lcd.print("Time: ");
lcd.print(seconds);
lcd.setCursor(0, 2);
lcd.print("Stage - ");
lcd.print(Names[heatStage]);
// lcd.setCursor(0, 3);
// lcd.print("Temp: ");
// lcd.print(temperature, 1);
} // end of display update for all active stages
} // end of millis_now - millis_before > refresh_rate
// If button 2 is pressed, exit cooking.
if (!digitalRead(but_2))
{
analogWrite(SSR, 0);
delay(500);
lcd.clear();
displayMode();
break;
}
}
} // End of SMDcook
// This function is used by the displayTemperature
// function to send the colors to the LEDs
void sendColors(int red, int green, int blue)
{
for (int i = 0; i < NUMPIXELS; i++)
{
pixels.setPixelColor(i, pixels.Color(red, green, blue));
pixels.show(); // Send the updated pixel colors to the hardware.
}
}