main.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #include <Arduino.h>
  2. #include <U8g2lib.h>
  3. #include <PID_v1.h>
  4. #include <MAX6675.h>
  5. #include <SPI.h>
  6. #define THERMOCOUPLE_CS_PIN 7
  7. #define DISPLAY_RESET_PIN 10
  8. #define DISPLAY_DC_PIN 9
  9. #define DISPLAY_CS_PIN 8
  10. #define DONE_LED_PIN 5
  11. #define SSR_PIN 4
  12. #define BUTTON_PIN 3
  13. const char* lcdMessagesReflowStatus[] = {
  14. "Ready",
  15. "Heating",
  16. "Holding temp",
  17. "Cool",
  18. "Complete",
  19. "Wait,hot",
  20. "Error"
  21. };
  22. typedef enum REFLOW_STATE
  23. {
  24. REFLOW_STATE_IDLE,
  25. REFLOW_STATE_PREHEAT,
  26. REFLOW_STATE_SOAK,
  27. REFLOW_STATE_COOL,
  28. REFLOW_STATE_COMPLETE,
  29. REFLOW_STATE_TOO_HOT,
  30. REFLOW_STATE_ERROR
  31. } reflowState_t;
  32. typedef enum REFLOW_STATUS
  33. {
  34. REFLOW_STATUS_OFF,
  35. REFLOW_STATUS_ON
  36. } reflowStatus_t;
  37. typedef enum DEBOUNCE_STATE
  38. {
  39. DEBOUNCE_STATE_IDLE,
  40. DEBOUNCE_STATE_CHECK,
  41. DEBOUNCE_STATE_RELEASE
  42. } debounceState_t;
  43. typedef enum SWITCH
  44. {
  45. SWITCH_NONE,
  46. SWITCH_1
  47. } switch_t;
  48. // ***** CONSTANTS *****
  49. #define TEMPERATURE_ROOM 70
  50. #define TEMPERATURE_SOAK 70
  51. #define TEMPERATURE_COOL_MIN 50
  52. #define SENSOR_SAMPLING_TIME 1000
  53. #define SOAK_PERIOD_MS 1800000 // 30*60*1000 30 minutes
  54. #define DEBOUNCE_PERIOD_MIN 50
  55. // ***** PID PARAMETERS *****
  56. // ***** PRE-HEAT STAGE *****
  57. #define PID_KP_PREHEAT 50 // default 100
  58. #define PID_KI_PREHEAT 0.025 // default 0.025
  59. #define PID_KD_PREHEAT 50 // default 20
  60. // ***** SOAKING STAGE *****
  61. #define PID_KP_SOAK 300 // default 300
  62. #define PID_KI_SOAK 0.05 // default 0.05
  63. #define PID_KD_SOAK 250 // default 250
  64. #define PID_SAMPLE_TIME 1000 //default 1000
  65. // ***** PID CONTROL VARIABLES *****
  66. double setpoint;
  67. double inputTemp;
  68. double temporaryInputVar;
  69. double output;
  70. double kp = PID_KP_PREHEAT;
  71. double ki = PID_KI_PREHEAT;
  72. double kd = PID_KD_PREHEAT;
  73. int windowSize;
  74. unsigned long now;
  75. unsigned long windowStartTime;
  76. unsigned long nextRead;
  77. unsigned long soakStartTime;
  78. unsigned long timerSoak;
  79. unsigned long completePeriod;
  80. // Reflow oven controller state machine state variable
  81. reflowState_t reflowState = REFLOW_STATE_IDLE;
  82. // Reflow oven controller status
  83. reflowStatus_t reflowStatus = REFLOW_STATUS_OFF;
  84. // Switch debounce state machine state variable
  85. debounceState_t debounceState;
  86. // Switch debounce timer
  87. long lastDebounceTime;
  88. // Switch press status
  89. switch_t switchStatus;
  90. // did encounter a thermocouple error?
  91. int tcErrorCount = 0;
  92. bool TCError = false;
  93. MAX6675 tcouple(THERMOCOUPLE_CS_PIN);
  94. U8G2_SSD1305_128X64_ADAFRUIT_F_4W_HW_SPI u8g2(U8G2_R0, DISPLAY_CS_PIN, DISPLAY_DC_PIN, DISPLAY_RESET_PIN);
  95. PID reflowOvenPID(&inputTemp, &output, &setpoint, kp, ki, kd, DIRECT);
  96. // Called once in setup, sets global display attributes.
  97. void u8g2_prepare(void) {
  98. u8g2.setFont(u8g2_font_8x13_tf);
  99. u8g2.setFontRefHeightExtendedText();
  100. u8g2.setDrawColor(1);
  101. u8g2.setFontPosTop();
  102. u8g2.setFontDirection(0);
  103. }
  104. // Handle reading temperature from max6675.
  105. // Also contains logic to protect against single/double read errors.
  106. // Requires three read errors in a row to go into error state.
  107. void readTemp(void) {
  108. temporaryInputVar = tcouple.readTempC();
  109. // inputTemp = tcouple.readTempC();
  110. if(temporaryInputVar == 0.00 || temporaryInputVar == -1.00 ) {
  111. if(tcErrorCount >= 3) {
  112. TCError = true;
  113. reflowState = REFLOW_STATE_ERROR;
  114. reflowStatus = REFLOW_STATUS_OFF;
  115. } else {
  116. tcErrorCount++;
  117. }
  118. } else {
  119. inputTemp = temporaryInputVar;
  120. tcErrorCount = 0;
  121. TCError = false;
  122. }
  123. }
  124. void handleSwitch(void) {
  125. // If switch 1 is pressed
  126. if (switchStatus == SWITCH_1)
  127. {
  128. // If currently reflow process is on going
  129. if (reflowStatus == REFLOW_STATUS_ON)
  130. {
  131. // Button press is for cancelling
  132. // Turn off reflow process
  133. reflowStatus = REFLOW_STATUS_OFF;
  134. // Reinitialize state machine
  135. reflowState = REFLOW_STATE_IDLE;
  136. }
  137. }
  138. // Simple switch debounce state machine (for switch #1 (both analog & digital
  139. // switch supported))
  140. switch (debounceState)
  141. {
  142. case DEBOUNCE_STATE_IDLE:
  143. // No valid switch press
  144. switchStatus = SWITCH_NONE;
  145. // If switch #1 is pressed
  146. if (digitalRead(BUTTON_PIN) == LOW)
  147. {
  148. // Intialize debounce counter
  149. lastDebounceTime = millis();
  150. // Proceed to check validity of button press
  151. debounceState = DEBOUNCE_STATE_CHECK;
  152. }
  153. break;
  154. case DEBOUNCE_STATE_CHECK:
  155. // If switch #1 is still pressed
  156. if (digitalRead(BUTTON_PIN) == LOW)
  157. {
  158. // If minimum debounce period is completed
  159. if ((millis() - lastDebounceTime) > DEBOUNCE_PERIOD_MIN)
  160. {
  161. // Proceed to wait for button release
  162. debounceState = DEBOUNCE_STATE_RELEASE;
  163. }
  164. }
  165. // False trigger
  166. else
  167. {
  168. // Reinitialize button debounce state machine
  169. debounceState = DEBOUNCE_STATE_IDLE;
  170. }
  171. break;
  172. case DEBOUNCE_STATE_RELEASE:
  173. if (digitalRead(BUTTON_PIN) == HIGH)
  174. {
  175. // Valid switch 1 press
  176. switchStatus = SWITCH_1;
  177. // Reinitialize button debounce state machine
  178. debounceState = DEBOUNCE_STATE_IDLE;
  179. }
  180. break;
  181. }
  182. }
  183. // Reflow oven controller state machine
  184. void handleReflowState(void) {
  185. switch (reflowState)
  186. {
  187. case REFLOW_STATE_IDLE:
  188. // If oven temperature is still above room temperature
  189. if (inputTemp >= TEMPERATURE_ROOM)
  190. {
  191. reflowState = REFLOW_STATE_TOO_HOT;
  192. }
  193. // If switch is pressed, start reflow process
  194. else if (switchStatus == SWITCH_1)
  195. {
  196. // Turn off done LED if it was on from a previous cycle.
  197. digitalWrite(DONE_LED_PIN, LOW);
  198. // Reset switch state to prevent triggering later code erroneously.
  199. switchStatus = SWITCH_NONE;
  200. // Initialize PID control window starting time
  201. windowStartTime = millis();
  202. // Ramp up to minimum soaking temperature
  203. setpoint = TEMPERATURE_SOAK;
  204. // Tell the PID to range between 0 and the full window size
  205. reflowOvenPID.SetOutputLimits(0, windowSize);
  206. reflowOvenPID.SetSampleTime(PID_SAMPLE_TIME);
  207. // Turn the PID on
  208. reflowOvenPID.SetMode(AUTOMATIC);
  209. // Proceed to preheat stage
  210. reflowState = REFLOW_STATE_PREHEAT;
  211. }
  212. break;
  213. case REFLOW_STATE_PREHEAT:
  214. reflowStatus = REFLOW_STATUS_ON;
  215. // If minimum soak temperature is achieved.
  216. if (inputTemp >= TEMPERATURE_SOAK)
  217. {
  218. soakStartTime = millis();
  219. // Chop soaking period into smaller sub-period
  220. timerSoak = soakStartTime + SOAK_PERIOD_MS;
  221. // Set less agressive PID parameters for soaking ramp
  222. reflowOvenPID.SetTunings(PID_KP_SOAK, PID_KI_SOAK, PID_KD_SOAK);
  223. // Proceed to soaking state
  224. reflowState = REFLOW_STATE_SOAK;
  225. }
  226. break;
  227. case REFLOW_STATE_SOAK:
  228. // If micro soak temperature is achieved
  229. if (millis() > timerSoak)
  230. {
  231. timerSoak = millis() + SOAK_PERIOD_MS;
  232. reflowStatus = REFLOW_STATUS_OFF;
  233. reflowState = REFLOW_STATE_COOL;
  234. }
  235. break;
  236. case REFLOW_STATE_COOL:
  237. // If minimum cool temperature is achieve
  238. if (inputTemp <= TEMPERATURE_COOL_MIN)
  239. {
  240. digitalWrite(DONE_LED_PIN, HIGH);
  241. completePeriod = millis() + 5000;
  242. // Turn off reflow process
  243. reflowStatus = REFLOW_STATUS_OFF;
  244. // Proceed to reflow Completion state
  245. reflowState = REFLOW_STATE_COMPLETE;
  246. }
  247. break;
  248. case REFLOW_STATE_COMPLETE:
  249. if (millis() > completePeriod)
  250. {
  251. // Reflow process ended
  252. reflowState = REFLOW_STATE_IDLE;
  253. }
  254. break;
  255. case REFLOW_STATE_TOO_HOT:
  256. // If oven temperature drops below room temperature
  257. if (inputTemp < TEMPERATURE_ROOM)
  258. {
  259. // Ready to reflow
  260. reflowState = REFLOW_STATE_IDLE;
  261. }
  262. break;
  263. case REFLOW_STATE_ERROR:
  264. // If thermocouple problem is still present
  265. if (isnan(inputTemp))
  266. {
  267. // Wait until thermocouple wire is connected
  268. reflowState = REFLOW_STATE_ERROR;
  269. }
  270. else
  271. {
  272. // Clear to perform reflow process
  273. reflowState = REFLOW_STATE_IDLE;
  274. }
  275. break;
  276. }
  277. }
  278. // PID computation and SSR control
  279. void handleSSR(void) {
  280. if (reflowStatus == REFLOW_STATUS_ON)
  281. {
  282. now = millis();
  283. reflowOvenPID.Compute();
  284. if((now - windowStartTime) > windowSize)
  285. {
  286. // Time to shift the Relay Window
  287. windowStartTime += windowSize;
  288. }
  289. if(output > (now - windowStartTime)) {
  290. digitalWrite(SSR_PIN, HIGH);
  291. } else {
  292. digitalWrite(SSR_PIN, LOW);
  293. }
  294. } else {
  295. // Reflow oven process is off, ensure oven is off
  296. digitalWrite(SSR_PIN, LOW);
  297. }
  298. }
  299. void drawScreen(void) {
  300. u8g2.clearBuffer();
  301. short rowOffset = 0;
  302. const short rowSize = 12;
  303. // Temperature Row
  304. char temperatureStr[8];
  305. dtostrf(inputTemp, 4, 2, temperatureStr);
  306. u8g2.drawStr( 0, rowOffset, "Temp: ");
  307. u8g2.drawStr( 60, rowOffset, temperatureStr);
  308. u8g2.drawStr( 100, rowOffset, "C");
  309. rowOffset += rowSize;
  310. // Thermocouple Status row
  311. u8g2.drawStr( 0, rowOffset, "tc_err: ");
  312. if (TCError) {
  313. u8g2.drawStr( 60, rowOffset, "true");
  314. } else {
  315. u8g2.drawStr( 60, rowOffset, "false");
  316. }
  317. rowOffset += rowSize;
  318. // General status row
  319. u8g2.drawStr( 0, rowOffset, lcdMessagesReflowStatus[reflowState]);
  320. rowOffset += rowSize;
  321. // Timer for Heat cycle row
  322. if(reflowState == REFLOW_STATE_SOAK) {
  323. char soakSecondsStr[8];
  324. itoa((millis() - soakStartTime)/1000, soakSecondsStr, 10);
  325. u8g2.drawStr( 0, rowOffset, "Time: ");
  326. u8g2.drawStr( 40, rowOffset, soakSecondsStr);
  327. u8g2.drawStr( 80, rowOffset, "/1800");
  328. }
  329. rowOffset += rowSize;
  330. u8g2.sendBuffer();
  331. }
  332. void setup(void) {
  333. // Turn off SSR.
  334. digitalWrite(SSR_PIN, LOW);
  335. pinMode(SSR_PIN, OUTPUT);
  336. pinMode(BUTTON_PIN, INPUT_PULLUP);
  337. digitalWrite(DONE_LED_PIN, LOW);
  338. pinMode(DONE_LED_PIN, OUTPUT);
  339. u8g2.begin();
  340. u8g2.clearBuffer();
  341. u8g2_prepare();
  342. // Set window size
  343. windowSize = 2000;
  344. // Initialize thermocouple reading variable
  345. nextRead = millis();
  346. }
  347. void loop(void) {
  348. if (millis() > nextRead) {
  349. readTemp();
  350. }
  351. handleReflowState();
  352. handleSwitch();
  353. handleSSR();
  354. drawScreen();
  355. }