-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreflow_mg_v30.ino
3148 lines (2761 loc) · 104 KB
/
reflow_mg_v30.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
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*******************************************************************************
* Title: Reflow Oven Controller
*
* Author: John Groezinger
* Date: 20 April 2017
*
* Notes regarding usage
*
* This code can best be described as a project grown out of conrol. What was meant to be
* a simple Floaster Oven build based on rocketscream hardware/software turned into an application of a time delay PID
* control loop with production implications and some self education.
*
* But, fun, nonetheless. So this is a quick overview of steps to consider when using this code.
* The code is a bit of an engineer's sandbox so there are a number of configuration options
* that can be invoked to characterize the oven, then options for configuring the oven
* for best results, and lastly a number of checks to turn this into a safer and production level oven.
* However, don't assume that all options necessariy make sense for your application.
* Finally, there are some embedded tools for debugging and verification
* Like most engineering, options are left for future work
*
* To determine proper oven parameters, I'd recommend you start by enabling the constant on feature ifdef. CONSTANT_TEMP
* This will add a 4th option to the menu which will simply set the oven to a constant temp.
* Take a look in the defines and you will find a way to set the constant temp, an optional method to disable the PID up to a point close to the setpoint,
* and ways to apply one or two PID parameter sets to the oven.
*
* For code verification, if interested, there are a few configurations to consider.
*
* There are two major DEUBG options.
* ALLPRINT is the most useful and will dump a variety of internal state change timestamps to the console.
* ALPRINT_WIN adds even more information regarding specific window the shift in the PID. It tends to be voluminous but sometimes useful.
* The NOHEAT config prevents the SSR from ever turning on. Most useful when doing simulations.
*
* So speaking of SIM simulation, by enabling this option and one of the datasets, you can input a fixed
* set of stimuli into the oven. Descriptions of the datasets are included. It was most useful for testing
* code but you can watch the response of the PID algorithm with it as well.
*
* Moving into actually using the oven, I'd recommend you review the optimized parameters from the constant temp for determination
* of what makes sense.
*
* Of primary interest are the PID Tuning Parameters for preheat, soak and reflow.
* I'd recommend you do experiments here as well as use the information from the CONSTANT run to help you decide on what makes sense.
* Options here are one pid set for everything (ONEPIDSET defined), one pid set for each stage (PID_NEAR_FAR_PREHEAT and PID_NEAR_FAR_REFLOW not defined),
* and two PID sets for PREHEAT and REFLOW based on the respective TEMP parameter if the NEAR_FAR parameter is defined.
* Also, See bangbang below.
*
* Changing the setpoints is possible for Lead and LeadFree profiles as a set of defines.
* Please note that the Preheat -> Soak transition is the same for both profiles.
*
* There are a variety of user pref config options for frequency of reading the thermocouple and processing rate,
* debounce options for the switch, and buzzer noise times.
*
* There are defines for the slope in SOAK stage (5C every 9 seconds).
* The window size and sample time can be changed but be sure you read the notes from the PID library author.
* These are tightly tied to together and frankly should be the same.
*
* From a production / safety point of view, I put in place a number of checks.
* These include ramp rate checks, over and under temp checks, and maximum time checks.
* You should review this for your oven so they don't get actuated under normal conditions.
* You may note that some of the checks are redundant but they are there nonetheless.
* In general, the oven attempts to do "the right thing".
* If there is a Preheat error, the oven aborts into cooldown. I don't attempt recovery.
* If there is a soak error, it will do the same. Checks here include overall time and not getting hot enough (still below reflow and still safely abort)
* Reflow is a bit of a quandry. You are already this far and things are working so you don't want to screw up the board.
* I opted to put in a bangbang check which has parameters defined to turn the oven full on to try and hit peak temp as a last resort.
* Ways to defeat this are noted. All of these parameters and more detailed information are below.
*
* Lastly actual usage is a bit different from the original rocket scream code.
* - By pressing the button (switch 1), you will scroll through 3 options, Pb free profile, Pb profile, and Summary.
* Holding the button for 2 seconds (until it displays "Ready!") will execute the option
* - Lead and nonLead profiles start executate as you would think, Summary displays information run since plugin (it is volatile)
* - If you wish to use serial port, set to 115.2Kbaud
* - The serial information has been augmented to include commas for easier input into Excel and changing output to DutyCycle
* Executing the summary option will result in a nonvolatile summary of oven uses being printed to serial port.
* - Both at the end of runs on the serial port and on the summary screen on the LCD, a number of useful metrics are printed including
* times and temperaturs (e.g., total run time, peak temp, time in reflow, etc)
* - Pressing the button at any time during a run will cancel exactly as before.
* - The buzzer has two modes. Quick short beeps are acknowledgement of start AND a cue to start opening the door for reflow cool down
* The second mode is long beeps indicating an error.
* - The oven used to display "cool" after reaching peak temp which, while true, is confusing since the oven stage is actually reflow.
* This is now called RF2 and after the oven drops down from max temp, a target temp will be displayed in the uppper right corner
* which will decrease at 1C/sec. This is meant to cue how to open the door to achieve optimal cooling.
*
*******************************************************************************
*
* Version: 2.1 13 March 2017
* 13 March 2017
* - Stripped out all ifdefs for old controller and started fixing indents
* - added ifdef NOHEAT for output testing.
* - Changed windowsize from 2000 to 1000.
* 2000 just doesn't seem right when the Compute loop is set to 1000.
* - PID parms testing resulted in honing in on 50/0.1/10
* - Added long switch press support to start oven
* - Adding dual profile support with short switch press
* - Confirmed loop time is about 150uS
* - Confirmed windowsize of 2000 isn't right, left at 1000
* - Added documentation below on testing
* - Reviewed Brett B PID treatise
* - Added loopIterations to count loop passes. Debug use
* - Reviewed a number of solder profiles and this seems to be in the middle
* - Given to MM for review
*
* Version 2.2 16 March 2017
* - Put in place output metrics
* - Changed sensor sampling time to 100mS (from 1S) to avoid phasing problems.
* - Potential BUG fixed, a second loop of the oven would not load PID PREHEAT parameters
* - Added more prints to serial output and cleanup labels on same
* - Add commas rather than spaces to output
* - Add column to serial output for percentage of output
* - HACK: Turned off SSR control when in cool down state (there is a logic BUG here)
* When I get a chance, look at turning off the PID
* - Remove parameter loading during cool down (see above)
* - Add summary of run to serial output
* - Fix if / if statement below (consolidate)
* - I see no need to changing the PID parameters. The oven seems to work
* ok with constant but this is mine.
* - Consider moving average to get rid of wiggles on thermocouple input.
* Not done: After increasing thermocouple loop rate, most of this went away.
* - Add defaults to case switch statements. Probably print error and shut off oven and hang.
* - Allow summary statistics to be seen on display with short button press
* - Temp display of 0.00C is has too many sig digits, it only does 0.25, 0.50, etc
* Good enough for now
* - Added compile time macro output to serial print
* - Cooling is displayed after peak attained. However, it really is still
* in reflow. An additional state could fix this.
* - Add beep / display when to open door
* - Review when PID parameters are loaded
* - Sending to MM for review
*
* Version 2.3 19 March 2017
* - buzzer Chirp at beginning of oven start
* - started experiment of turning off the PID with manual setting during cooling
* - put in double check on oven state for turning on oven relay
* - and manually turned output to 0
* - turned off the PID during cool down and lose the HACK
* - Cleanup flag variables w/enum
* - Added a few more inits
* - Changed test in SSR ON loop to be => rather than just > to get full 100% of SSR
* - Added ALLPRINT and ALLPRINT_WIN
* - Added ramp rate output
* - Reviewed V1.7 of Brett PID auto tune code and decided not to implement any features of it
* Even Brett questions usefulness of autotune.
* - Reviewed Panasonic code port and added ramp feature to printout
* - Changed time tests for thermcouple and display to be >= and not just > which fundamentally
* is a bit weird but did not result in any drift
* - Fixed bug in SSR loop which would turn the SSR every cycle even when it should not.
* - Put in place 11mS fix for getting windowStartTime much closer to PID calc time
* This woudl result in a consistent error of 1% in the output loop
* - Put in place a + or - during cooling. A + means the oven is cooling too fast (shut the door).
* "-" is open the door. blank is just right. This may need some work to give the correct feel but it's not horrid.
* - Most important. By using the ALLPRINT macros, validated the control of the SSR aligned with the output of the
* PID. This found 2 more bugs and seems to have all but eliminated some of the nondeterministic nature of the code.
* - Serial to 115200 baud
* - Release to MM
*
* V2.4 24 March 2017
* - Added moving average to input to smooth out some of the bumps
* - At the same time, kicked up the read frequency of the thermocouples
* Since the moving average is by 3, the "interesting" rate of change of temp is about 1C/sec (it will max out at 3C/sec)
* and the significance of the reading is 0.25C, I decided to do two things. I wanted to capture changes on the thermocouple
* close to the 0.25C with the moving average and secondly I decided to intentionally decouple the reading from the rest of
* the PID loop (1 sec). Therefore, a 0.25C change represents about 0.25 second and to get that through the moving average
* would be 3x that rate or sample at 12 second or 83ms/update. This also has the benefit of decoupleing the temp
* loop from the PID control loop. Which essentially addresses the observation below.
*
* NOTE from original code inspection: Biggest issue I see is the thermocouple, display, and PID
* calc all run on their own timers
* E.g., thermcouple reading is not in sync with the PID loop.
* For now I sped up the the thermcouple loop to 10X the display speed / PID calc speed but
* that really isn't the best way to do this.
* Not sure I like all the calls to millis(), potential out of phase things...
*
* Case in point on above comment. The windowStartTime is tightly tied to the PID
* compute loop since they are both running on the same timeframe. Thus, a phase difference
* results directly in an output error. In this case, the windowSizeTime clock started
* too early (about 11mS) resulting directly in an 11mS decrease in actual ON time
* from that requested by output. I have put in place a patch to start the windowStartTime
* right before the PID Calc loop for the first time to attempt to minimize this.
* This did reduce the 11mS delay to close to zero
* - Printed out PID parms with output change with ALLPRINT
* - Released to MM
*
* V2.5 24 March 2017
* - Added "State" to printout for easy grepping
* - Fixed end of reflow determination logic bug
* - Adding constant temp operation for tuning purposes
* - Per Mark review, added profile information to printout
* - Moved Data printout windowing information to ALLPRINT_WIN
* - Moved the thermocouple loop from 83 to 80 to give it a rate of 12.5Hz
* thereby allowing to move w.r.t. 1 sec windows
* That was original intent but didn't do it correct
* - As a result of the constant temp testing which showed the oven doing massive overshoot
* and reading an article on floasters, I decided to put in place code to not run
* the PID all the time during preheat as an experiment. This was very enlightening.
* It basically led me down the road of a time delay PID loop.
* Experimenting with manual control of output until temp within a certain degrees and then turn on PID
* Also experimenting with 2 PID sets in coming to the setpoing.
* - Fixed - Real bug. Canceling in the middle of a beep leaves the buzzer on
* - Completed tested with revised parameters with thermocouple on pwb surface. Deleted D parameters
* and only used I parameters in middle section. Pretty close.
* - MM
*
* V2.6 31 March 2017
* - Putting in framework for error testing and recovery for preheat stage
* - Cleanup of defines
* - Put SIMulator in place
* - Put tests in place for reflow, soak, and preheat as well as cycle overall tests
* - Put in place run status framework
* - Fixed max temp / time bug to only record when reflow is on
* - Added documentation on additional test cases during runtime, see defines below
* - Release to MM
*
* V2.7 11 April
* - Added debug macros and dual PID during preheat - big improvement in results
* - Changed token handling during cooldown to display target temp
* - Added debug macros and dual PID during reflow - big improvement in results
* - Decreased requirement for presoak ramp during first 30 seconds (10C -> 5C)
*
* V2.8 17 April
* - Added buzzer on cancel
* - Added EEPROM tracking of usage
*
* V3.0 20 April 2017
* - Minor doc edits and release to GIT
*
* Still to do and / or think about
* - Consider programming with defined function rather than programmatic implementation.
* This would allow error detection on low ramp rates. For example, the oven will not error
* if there is no heat.
* - Consider reorganization of code overall to allow for varying length of button press and menuing
* The 1 sec display loop makes display response feel slow and this is related to loop architecture
* - Anticipated bugs. This uses millis() all over the place which has a wraparound time of 55 days
* - A thermocouple error displays TC Error on the LCD and immediately aborts with no information written to log.
* Unsure of how to improve this currently
*
*******************************************************************************
*
* Oven Testing - Groezinger - 16 March 2017
* Hamilton Beach Model 31146 Wal Mart oven
* Thermocouple with no board, no middle tray in oven, drip tray in bottom
*
* Testing starting with I and D set to zero
* P term 50, 100, 200 testing showed an Integral term is needed.
* At 50, oven would never reach setpoints
* At 200, oven would hunt
* In particular, at 50, the oven would miss the 200 soak temp (at 190)
* and then proceed into reflow (since this is time based)
*
* Adding I term
* Honing in on P of 100 and setting I term to 1 and 0.2
* Observations were significant hunting still and overshoot
* It was clear the I term was contributing too much and I felt that
* in combo with the P term was driving more hunting than seen before.
*
* Reduced P term to 50
* I term of 0.5 showed "riding the peaks" in the soak phase and still
* lag of peak. Reduced to 0.1 gave a nice response thru the soak phase
* with a bit of overshoot and lag.
*
* P=50, I=0.1, D term testing
* Decided to address the overshoot with the D term. This is a minor
* adjustment and the oven would be fine without it.
* 0.1, 1.0, 10 terms tested with no difference between 0.1 and 1.0.
* From 1.0 to 10 I observed slightly less overshoot on peak and slightly
* less lag. I observed slightly less undershoot on the soak.
* A run at 50 yielded no dicernable improvement.
* Again, a minor observation. Settled in on 10
*
* Optimal Parameters, KP=50, KI=0.1, KD=10
*
* At this time, I decided to retest with windowSize of 2000 since I felt
* things were working and this was the original value. Bottom Line,
* the oven control did not work at all with significant misses on attaining
* setpoints. Back to windowSize of 1000. I am not saying that 2000 is
* wrong, but I can't figure out how that works and I don't believe it
* does since this is time based and everything else is 1 sec.
*
* Oven Testing - Groezinger - 17 March 2017
*
* As an experiment, I decided to change only the following:
* #define PID_KI_PREHEAT 0.05
* Leaving all the rest at 0.1
* The results were amazing. The oven way undershot on the soak and reflow stage.
* Unsure what this means but I suspect the change in parms changed
* the algorithm in a way not expected. Since I have honed in on one setting,
* I have added a define to stop all but one loading of the parameters.
* Note on 19 April. Beleive this was due to bugs found in the way the PID control was done.
*
* Oven Testing - Groezinger 23 March 2017
*
* With the ALlPRINT serial output, I decided to do some additional testing to
* understand the PID library workings. Indeed, setting the windowsize to 2000
* and leaving the PID Calculation at 1000 is not a good plan. The PID does
* do a recalc in the middle of a window, the output is invariably reduced,
* and the result is that you can never attain desired heat. Changing the PID calc window
* to 2000 with this would be an alternative.
*
* Oven Testing - Groezinger 30 March 2017
*
* Constant temp testing
* 10% Duty Cycle is about 100C
* 23% 145C
* 40% 192C
* 49% 240C
*
* Additional information regarding ALLPRINT output (for debugging / verification)
*
* In the SSR loop you will see a set of prints:
* SSRON / SSROFF indicates exactly this
* The loopInteration is just a counter which gets incremented every time through loop()
* It is most useful simply to tag output and compare to other output
* output is the current output of the PID 0-1000
* It also is mS that the SSR is supposed to be on out of a 1 second loop
* now is the time at the start of the PID loop
* time is the current time (close to above but not quite the same)
* lastPIDChange is the last timestampe when the PID changed the output value
* windowStartTime is the time that the current 1 second output window started
*
* So you can check that the SSR turned off at the correct time by taking the windowStartTime and
* adding the output and comparing this against the SSROFF statement time.
*
* Testing with boards
*
* Profile Selection
*
* Lead Free
*
* Preheat stage -> Oven limits ramp to around 2C/sec (max of 2.5C/sec ok)
* Curve does not put a limit on this stage
* RESULT: 1.8C/sec
* Preheat->Soak 150
* RESULT: ~100 sec (depends on start temp)
* Soak stage -> PID limits ramp to around 0.5C/sec (max 0.6C sec ok)
* RESULT: ~0.6C/sec
* Soak->Reflow 200
* RESULT: ~ 160 sec (total from start)
* Reflow stage -> Oven limits ramp to around 1C/sec (max of ?/sec ok)
* Reflow->Cooling 245 (e.g. Peak)
* RESULT: Attain peak in 50 sec, (~205 sec) need to open door
* Cooling stage -> Oven door closed limits to 0.5C/sec (max of ?/sec ok)
* Open door limits to 1.0C/sec (recommended)
*
* Kester Sn63Pb37 Recommended Profile (Lead)
*
* Preheat stage -> Oven limits ramp to around 2C/sec (max of 2.5C/sec ok)
* Curve does not put a limit on this stage
* RESULT: 1.8C/sec
* Preheat->Soak 150 Recommended attain in 90sec
* RESULT: ~90 sec
* Soak stage -> PID limits ramp to around 0.5C/sec (max 0.6C sec ok)
* RESULT: ~0.6C/sec
* Soak->Reflow 180 Recommended 2-4 minute from start
* RESULT: ~ 150 sec
* Reflow stage -> Oven limits ramp to around 1C/sec (1.3-1.6C/sec rec)
* Recommended in Reflow Zone 45-75 sec (inc cooling)
* RESULT: Attain peak in ~35 sec, need to open door
* Reflow->Cooling 225 (e.g. Peak)
* Cooling stage -> Oven door closed limits to 0.5C/sec
* Recommended 183C-> <40C over 3 minutes minimum
* Slightly Open door limits to 1.0C/sec (recommended)
*
*******************************************************************************
*
* Title: Reflow Oven Controller
*
* Date: 26-11-2012
* Company: Rocket Scream Electronics
* Author: Lim Phang Moh
* Website: www.rocketscream.com
*
* Brief
* =====
* This is an example firmware for our Arduino compatible reflow oven controller.
* The reflow curve used in this firmware is meant for lead-free profile
* (it's even easier for leaded process!). You'll need to use the MAX31855
* library for Arduino if you are having a shield of v1.60 & above which can be
* downloaded from our GitHub repository. Please check our wiki
* (www.rocketscream.com/wiki) for more information on using this piece of code
* together with the reflow oven controller shield.
*
* Temperature (Degree Celcius) Magic Happens Here!
* 245-| x x
* | x x
* | x x
* | x x
* 200-| x x
* | x | | x
* | x | | x
* | x | |
* 150-| x | |
* | x | | |
* | x | | |
* | x | | |
* | x | | |
* | x | | |
* | x | | |
* 30 -| x | | |
* |< 60 - 90 s >|< 90 - 120 s >|< 90 - 120 s >|
* | Preheat Stage | Soaking Stage | Reflow Stage | Cool
* 0 |_ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* Time (Seconds)
* This firmware owed very much on the works of other talented individuals as
* follows:
* ==========================================
* Brett Beauregard (www.brettbeauregard.com)
* ==========================================
* Author of Arduino PID library. On top of providing industry standard PID
* implementation, he gave a lot of help in making this reflow oven controller
* possible using his awesome library.
*
* ==========================================
* Limor Fried of Adafruit (www.adafruit.com)
* ==========================================
* Author of Arduino MAX6675 library. Adafruit has been the source of tonnes of
* tutorials, examples, and libraries for everyone to learn.
*
* Disclaimer
* ==========
* Dealing with high voltage is a very dangerous act! Please make sure you know
* what you are dealing with and have proper knowledge before hand. Your use of
* any information or materials on this reflow oven controller is entirely at
* your own risk, for which we shall not be liable.
*
* Licences
* ========
* This reflow oven controller hardware and firmware are released under the
* Creative Commons Share Alike v3.0 license
* http://creativecommons.org/licenses/by-sa/3.0/
* You are free to take this piece of code, use it and modify it.
* All we ask is attribution including the supporting libraries used in this
* firmware.
*
* Required Libraries
* ==================
* - Arduino PID Library:
* >> https://github.com/br3ttb/Arduino-PID-Library
* - MAX31855 Library (for board v1.60 & above):
* >> https://github.com/rocketscream/MAX31855
*
* Revision Description
* ======== ===========
* 3.0 Groezinger (see notes elsewhere)
* 1.20 Adds supports for v1.60 (and above) of Reflow Oven Controller
* Shield:
* - Uses MAX31855KASA+ chip and pin reassign (allowing A4 & A5 (I2C)
* to be used for user application).
* - Uses analog based switch (allowing D2 & D3 to be used for user
* application).
* Adds waiting state when temperature too hot to start reflow process.
* Corrected thermocouple disconnect error interpretation (MAX6675).
* 1.10 Arduino IDE 1.0 compatible.
* 1.00 Initial public release.
*******************************************************************************/
// ***** INCLUDES *****
#include <LiquidCrystal.h>
#include <MAX31855.h>
#include <PID_v1.h>
#include <EEPROM.h>
#define MAJOR_VERSION 3
#define MINOR_VERSION 0
//
// **** DEBUG AND OPERATIONAL CONFIG DEFINES ***
//
// Define to not turn on heater
// #define NOHEAT
// Extended print statements to serial, ALLPRINT gives SSR on/off information and PID calculations
// #define ALLPRINT
// ALLPRINT_WIN gives information relating to window frame calculation (a bit too much, generally)
// #define ALLPRINT_WIN
// add constant state option to top level menu if defined
// #define CONSTANT_TEMP
// Review options below for proper configuration of constant state
// Simulator input - Must add one data set to use
// Sims will end either when an error detected, a manual abort is done, or it hits last value which is set to max temp
// #define SIM
// #define SIM_DATA_OK_1C
// #define SIM_DATA_FAIL_30
// #define SIM_DATA_OK_30
// #define SIM_DATA_FAIL_60
// #define SIM_DATA_OK_60
// #define SIM_DATA_FAIL_149
// #define SIM_DATA_OK_150
// #define SIM_DATA_OK_SOAK
// #define SIM_DATA_OK_SOAK_FAST
// #define SIM_DATA_FAIL_SOAK_FAST_SLOW_PEAK
// #define SIM_DATA_FAIL_SOAK_FAST_MISS_END
// EEPROM
// #define EEPROM_DUMP
// #define EEPROM_RESET
//
// ***** TYPE DEFINITIONS *****
//
typedef enum REFLOW_STATE
{
REFLOW_STATE_IDLE,
REFLOW_STATE_IDLE_LEAD,
REFLOW_STATE_IDLE_SUMMARY,
REFLOW_STATE_PREHEAT,
REFLOW_STATE_SOAK,
REFLOW_STATE_REFLOW,
REFLOW_STATE_REFLOW_2,
REFLOW_STATE_COOL,
REFLOW_STATE_COMPLETE,
REFLOW_STATE_TOO_HOT,
REFLOW_STATE_ERROR,
REFLOW_STATE_IDLE_CONSTANT,
REFLOW_STATE_RUN_CONSTANT,
REFLOW_STATE_PREHEAT_ERROR,
REFLOW_STATE_SOAK_ERROR,
REFLOW_STATE_REFLOW_ERROR,
REFLOW_STATE_CYCLE_ERROR,
REFLOW_STATE_CANCEL
} reflowState_t;
typedef enum REFLOW_STATUS
{
REFLOW_STATUS_OFF,
REFLOW_STATUS_ON
} reflowStatus_t;
typedef enum SWITCH
{
SWITCH_NONE,
SWITCH_1,
SWITCH_2
} switch_t;
typedef enum DEBOUNCE_STATE
{
DEBOUNCE_STATE_IDLE,
DEBOUNCE_STATE_CHECK,
DEBOUNCE_STATE_RELEASE
} debounceState_t;
typedef enum SWITCH_LONG_PRESS_FLAG
{
SWITCH_LONG_PRESS_FLAG_NO,
SWITCH_LONG_PRESS_FLAG_YES
} switchLongPressFlag_t;
typedef enum PROFILE_LEAD
{
PROFILE_LEAD_YES,
PROFILE_LEAD_FREE
} profileLead_t;
typedef enum RUN_COMPLETION_STATUS
{
RUN_COMPLETION_STATUS_NEVERRUN,
RUN_COMPLETION_STATUS_INPROCESS,
RUN_COMPLETION_STATUS_OK,
RUN_COMPLETION_STATUS_CANCEL,
RUN_COMPLETION_STATUS_PREHEAT_ERROR,
RUN_COMPLETION_STATUS_SOAK_ERROR,
RUN_COMPLETION_STATUS_REFLOW_ERROR,
RUN_COMPLETION_STATUS_CYCLE_ERROR
} runCompletionStatus_t;
//
// ***** SETPOINT CONSTANTS FOR LEAD AND NONLEAD PROFILES *****
//
#define TEMPERATURE_ROOM 50
#define TEMPERATURE_SOAK_MIN 150
#define TEMPERATURE_SOAK_MAX 200
#define TEMPERATURE_SOAK_MAX_LEAD 180
#define TEMPERATURE_REFLOW_MAX 245
#define TEMPERATURE_REFLOW_MAX_LEAD 220
#define TEMPERATURE_COOL_MIN 100
//
// ***** THERMOCOUPLE *****
//
// How often to read the thermocouple, changed to be faster than
// display or PID loop and actually not in phase at 12.5Hz
#define SENSOR_SAMPLING_TIME 80
// 3 way moving average for input if defined
#define MOVING_AVERAGE
// use 3 way median filter instead - IMPORTANT, MUST define MOVING_AVERAGE to use this!
#define MEDIAN_AVERAGE
//
// ***** SWITCH *****
//
#define DEBOUNCE_PERIOD_MIN 50
// Minimum time in mS to depress switch to start cycle
#define LONG_SWITCH_PRESS 2000
//
// ***** SOUND *****
//
// Number of on/off cycles to chirp (must be even) and mS of time for each
#define BUZZER_CHIRP_NORM_STEPS 6
#define BUZZER_CHIRP_NORM_TIME 50
#define BUZZER_CHIRP_ERROR_STEPS 10
#define BUZZER_CHIRP_ERROR_TIME 500
//
// ***** PID TUNING PARAMETERS *****
//
// One load of PID parameters (PREHEAT only) if defined across PREHEAT, SOAK, REFLOW states
// #define ONEPIDSET
// If defined, will use the second set of parameters when within the window during preheat
#define PID_NEAR_FAR_PREHEAT
#define PID_NEAR_FAR_TEMP_PREHEAT 20
// If defined, will use the second set of parameters when within the window during reflow
#define PID_NEAR_FAR_REFLOW
#define PID_NEAR_FAR_TEMP_REFLOW 20
// ***** PRE-HEAT STAGE *****
#define PID_KP_PREHEAT 50
#define PID_KI_PREHEAT 0.0
#define PID_KD_PREHEAT 0
// ***** PRE-HEAT STAGE 2 *****
#define PID_KP_PREHEAT_2 25
#define PID_KI_PREHEAT_2 1.0
#define PID_KD_PREHEAT_2 0
// ***** SOAKING STAGE *****
#define PID_KP_SOAK 100
#define PID_KI_SOAK 2.0
#define PID_KD_SOAK 0
// ***** REFLOW STAGE *****
#define PID_KP_REFLOW 50
#define PID_KI_REFLOW 0.0
#define PID_KD_REFLOW 0
// ***** REFLOW STAGE 2 *****
#define PID_KP_REFLOW_2 25
#define PID_KI_REFLOW_2 0.1
#define PID_KD_REFLOW_2 0
// The temperature increment and how often to change during the soak period
// Also note that this results in 10 steps at 9 seconds apiece or 90 seconds for Pb Free.
// A better approach would be functional driven
#define SOAK_TEMPERATURE_STEP 5
#define SOAK_MICRO_PERIOD 9000
// The PID will revaluate every 1 sec
#define PID_SAMPLE_TIME 1000
// The output value of the PID is 0 to windowsize
// Set to 1000 since everything is based on 1 sec and mS units
// In retrospect, PID_SAMPLE_TIME and PID_WINDOW_SIZE should use the same define
#define PID_WINDOW_SIZE 1000
//
// ***** RAMP RATE TESTING AND SAFETY STUFF *****
//
// These are for checking oven functionality and safety stops in the event performance is not good enough
// This is number of seconds from start of run until start of soak.
// If not met, oven will abort and turn off with Abort PH error
// To defeat, set to a large number
// There is a SIM for this. Another way is to set PreHeat PID parms to Ki=0 and Kp low (10 or something)
// Use SIM_DATA_FAIL_149 SIM_DATA_OK_150
#define PREHEAT_STAGE_MAX_TIME_TO_SOAK 150
// This consists of a ramprate test done at start of run + 30 sec. Oven temp must increase by the PREHEAT_STAGE_MIN_INCREASE_INIT
// If not met, oven will abort and turn off with Abort PH error
// To defeat, set increase to 0
// Use SIM_DATA_FAIL_30 and SIM_DATA_OK_30 to verify
#define PREHEAT_STAGE_ERROR_CHECK_TIME_INIT 30
#define PREHEAT_STAGE_MIN_INCREASE_INIT 5
// This consists of two more checks done at run + init + 30 and run + init + 2 * 30.
// Oven temp must increase by 30 at each checkpoint
// If not met, oven will abort and turn off with Abort PH error
// To defeat, set increase to 0
// Use SIM_DATA_FAIL_60 and SIM_DATA_OK_60 to verify
#define PREHEAT_STAGE_ERROR_CHECK_TIME 30
#define PREHEAT_STAGE_MIN_INCREASE 30
// This test is maximum time from run start to start of reflow.
// If not met, oven will abort and turn off with Abort SK error
// To defeat, set to a large number
// Please note that this is actually a code error check since the above test for 150 max time + 10 steps at 9 seconds meets the 240 always.
// Use SIM_DATA_OK_SOAK
// For SIM testing, decrease time to 239 or less
#define PREHEAT_STAGE_MAX_TIME_TO_REFLOW 240
// Temp must be within this temp window of SOAK end temp at the end of SOAK
// If not met, oven will abort and turn off with Abort SK error
// To defeat, set to a large number
// Use SIM_DATA_FAIL_SOAK_FAST_MISS_END
#define SOAK_STAGE_TEMP_WITHIN_MAX_ERROR 10
// This is a safety shutoff, we do not want to hit these as the reflow may be screwed up
// Use SLOW_PEAK SIM array and adjust number below to 155
// If not met, oven will abort and turn off with Abort RF error
// To defeat, set to a large number
#define PREHEAT_STAGE_MAX_TIME_TO_PEAK 330
// If the reflow is not ramping up at 3 C / 10 sec, then switch to full on to try and save the run
// Reflow stage switch to bang bang criteria
// To defeat, set MIN increase to 0
// There is a SIM for this. Another way is to set Reflow PID parms to Ki=0 and Kp low (10 or something)
// Use SIM_DATA_FAIL_SOAK_FAST_SLOW_PEAK
#define REFLOW_STAGE_MIN_INCREASE 3
#define REFLOW_STAGE_ERROR_CHECK_TIME 10
// Cycle error
// To test temp, use any SIM input with a 275 in the array for the max temp or use constant temp and decrease the number
// To test max time, recommend reduce the max time to something like 15 and observe with any sim array
// Max temp will shut the oven off with Abort CY error due to over temp. Time is similar but measured from run start to COMPLETE.
// Please note this currently includes cool down
// To defeat, set to a large number
// Use SIM_DATA_FAIL_SOAK_FAST_MISS_END
#define CYCLE_MAX_TEMP 275
#define CYCLE_MAX_TIME 600
// Please note that there are no tests for maximum ramp rate testing on PreHeat or Reflow assuming the oven limits this per testing
// Please note there are no ramp rate checks in soak because we did a ramp rate test in preheat
//
// ***** CONSTANT TEMP OPTION AND PID PARAMETER SETS *****
//
// The following section only applies if CONSTANT_TEMP is defined
// Primarily meant for oven testing and helping define needed algorithms
// The temp it will run at if the constant state option is enabled
#define TEMPERATURE_CONSTANT 150
// Defeat PID during constant temp until within PID_ON_WHEN_WITHIN degrees
// Please note that the PID appears to have some logic that prevents large sudden
// changes in output when going from manual to auto. In short, having the oven
// in manual at 100% duty cycle and then flipping to auto when getting close
// did not work too well as the PID would only slowly drop the output down.
// Consequently, I chose to leave the PID on during the entire cycle but change
// the parameters (adding in the integral term) when the temp got close to setpoint.
// #define PID_OFF_DURING_PREHEAT
// #define PID_ON_WHEN_WITHIN 20
// if PID_OFF_DURING_PREHEAT is defined, this is the duty cycle used during that time
// #define CONSTANT_OUTPUT_1 1000
// If defined, enable 2 phase PID during constant temp
// Intended use is for oven testing to determine if changing the PID parameters makes a difference
// If not defined, only 1 phase PID during constant temp (FAR parameters)
#define PID_NEAR_FAR
// these are used for constant temp operation for oven testing.
// If the temp is greater than PID_NEAR_FAR_TEMP away from setpoint, the FAR parms are used.
#define PID_KP_CONSTANT_FAR 50
#define PID_KI_CONSTANT_FAR 0.0
#define PID_KD_CONSTANT_FAR 0
#define PID_KP_CONSTANT_NEAR 25
#define PID_KI_CONSTANT_NEAR 0.1
#define PID_KD_CONSTANT_NEAR 0
#define PID_NEAR_FAR_TEMP 20
//
// ***** DISPLAY *****
//
// Amount of time to display messages in ms
// such as the signon message and messages in the summary screen
#define DISPLAY_DELAY 2000
// how much must temp drop below max before cueing starts
#define SLOWFAST_TEMP_WINDOW 3
// ***** LCD MESSAGES *****
const char* lcdMessagesReflowStatus[] = {
"OK noPb",
"OK Pb",
"Summary",
"Pre-heat",
"Soak",
"Reflow",
"RF2",
"Cool",
"Complete",
"Wait,hot",
"Error",
"OK Const",
"Constant",
"Abort PH",
"Abort SK",
"Abort RF",
"Abort CY",
"Cancel"
};
// ***** DEGREE SYMBOL FOR LCD *****
unsigned char degree[8] = {140,146,146,140,128,128,128,128};
// ***** PIN ASSIGNMENT *****
int ssrPin = 5;
int thermocoupleSOPin = A3;
int thermocoupleCSPin = A2;
int thermocoupleCLKPin = A1;
int lcdRsPin = 7;
int lcdEPin = 8;
int lcdD4Pin = 9;
int lcdD5Pin = 10;
int lcdD6Pin = 11;
int lcdD7Pin = 12;
int ledRedPin = 4;
int buzzerPin = 6;
int switchPin = A0;
//
// ***** PID CONTROL VARIABLES *****
//
// Current desired temperature by the PID loop
double setpoint;
// Current temp measured (it is input to the PID loop)
// and previous temp for ramp rate calculation printed out in the serial output
double input;
double lastinput;
// Sliding temp record for deciding if to put into bang bang
double bangbangInput;
unsigned long bangbangTime;
// Moving average values for last 3 temps, a pointer for current one, and a working location
double input0;
double input1;
double input2;
int inputPtr;
double inputtmp;
// Output of PID control loop (subject to windowSize)
// and lastoutput is prior used to determine if a new value has been calculated.
// Note: I learned that the PIDCompute function does return a value indicating a new output
// If correct, there is no need for lastoutput
double output;
double lastoutput;
// PID cooefficients
double kp = PID_KP_PREHEAT;
double ki = PID_KI_PREHEAT;
double kd = PID_KD_PREHEAT;
// Input to the PID and it is now hardwired to 1 seconds
int windowSize;
// windowStartTime is used to determine when to adjust the output to the SSR frame
unsigned long windowStartTime;
// Records last time we had a PID output change
// Doesn't do anything except provide information in the print loop for when the current PID changes
// This should always be 1 sec increments
// This was useful to debug issues regarding time frame synch between the sliding window and the PID
long lastPIDChange;
//
// ***** DISPLAY *****
//
// nextCheck used to determine if to update LCD display
// Hardwired to 1 sec updates
unsigned long nextCheck;
// Temperature cue for cooling
double slowFastTemp;
// nextCheck used to determine if to read thermocouple
unsigned long nextRead;
// timerSoak used to determine if the current microPeriod has expired
// If it has, then the temp will be incremented
unsigned long timerSoak;
// Seconds timer
// Is used only for displaying time to serial port
int timerSeconds;
//
// ***** SOUND *****
//
// buzzerPeriod used to determine if time to turn off the buzzer
unsigned long buzzerPeriod;
// Number of counts to chirp buzzer for door open, must be even
int buzzerChirp;
//
// ***** OVEN STATE MACHINE *****
//
// Reflow oven controller state machine state variable
reflowState_t reflowState;
// Reflow oven controller status
reflowStatus_t reflowStatus;
//
// ***** SWITCH *****
//
// Switch debounce state machine state variable
debounceState_t debounceState;
// Switch debounce timer
// variable set to current time when switch first detected depressed
long lastDebounceTime;
// Switch press status
switch_t switchStatus;
// Switch Long Press Detection timer in mS, starts at zero
long switchLongPressTimer;
// Switch Long Press Flag
switchLongPressFlag_t switchLongPressFlag;
//
// ***** PROFILE AND METRICS *****
//
// Leaded Profile Flag
profileLead_t profileLead;
// Metrics on Last Run
long runStartTime;
double runStartTemp;
long runPreHeatEndTime;
double runPreHeatEndTemp;
long runSoakEndTime;
double runSoakEndTemp;
long runPeakTime;
double runPeakTemp;
long runReflowEndTime;
long runEndTime;
double runEndTemp;
// indicates if last run was successful or type of error
runCompletionStatus_t runCompletionStatus;
// Loop counter to simply count iterations of loop for debug purposes
long loopIteration;
// EEPROM interface
// In future rev, I will get more clever and put in place real structures in the EEPROM
// But for now, this is very simple. Everything is in words
// Byte 0,1 - Key - 0xFFFF, never programmed, 0xAA55 programmed once
// Byte 2,3 - Version of structure same as version of code
// Byte 4,5 - Number of cycles attempted (word)
// Byte 6,7 - Number of cycles completed (word)
// ...and so on
// The following is V2.8 format for EEPROM
#define EEPROM_KEY 0
#define EEPROM_VERSION 2
#define EEPROM_CYCLES_ATTEMPTED 4
#define EEPROM_CYCLES_COMPLETED 6
#define EEPROM_CYCLES_CANCELLED 8
#define EEPROM_CYCLES_ABORTEDPH 10
#define EEPROM_CYCLES_ABORTEDSK 12
#define EEPROM_CYCLES_ABORTEDRF 14
#define EEPROM_CYCLES_ABORTEDCY 16
#define EEPROM_RESERVED 18
// Length of pseudo structure
#define EEPROM_LENGTH 20
// access variables
int address = 0;
word value;
word tmpvalue;
// ***** SIMULATOR *****
#ifdef SIM_DATA_OK_1C
// incremented 1C / sec model
unsigned int simInput[] =
{20,20,20,20,20,20,20,20,20,20,
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,