// include the library code: #include #include // states #define CONFIG_STATE 0 #define START_STATE 1 #define PREHEAT_STATE 2 #define RAMP_UP_STATE 3 #define TAL_FIRST_STATE 4 #define PEAK_STATE 5 #define TAL_SECOND_STATE 6 #define RAMP_DOWN_STATE 7 #define END_STATE 8 #define ERROR_STATE 9 // error conditions #define E_DT_MIN 1 // temperature dt too small #define E_DT_MAX 2 // temperature dt too big #define E_TIME_MAX 4 // reflow process does take too long #define E_TS_TOO_SHORT 8 // Ts duration too short #define E_TS_TOO_LONG 16 // Ts duration too long #define E_TL_TOO_SHORT 32 // Tl duration too short #define E_TL_TOO_LONG 64 // Tl duration too long #define E_TP_TOO_SHORT 128 // Tp duration too short #define E_TP_TOO_LONG 256 // Tp duration too long // system time, timestamps and temperatures from sensors unsigned int time = 0; // profile seconds unsigned int temperature = 25; // actual oven temp unsigned int last_temperature = 25; // last oven temp int actual_dt = 0; // actual difference from last to actual temperatur typedef struct { // profile temperatures unsigned int Ts_min; unsigned int Ts_max; unsigned int Tl; unsigned int Tp; unsigned int time_max; // profile temp per second rates int ramp_up_rate_min; int ramp_up_rate_max; int ramp_down_max; int ramp_down_min; // profile temp durations unsigned int Ts_duration_min; unsigned int Ts_duration_max; unsigned int Tl_duration_min; unsigned int Tl_duration_max; unsigned int Tp_duration_min; unsigned int Tp_duration_max; } Profile; struct Profile profile {150, // °C unsigned int Ts_max = 200; // °C unsigned int Tl = 217; // 217°C unsigned int Tp = 260; // 245-260°C unsigned int time_max = 480; // 8*60s max // profile temp per second rates int ramp_up_rate_min = 0; // not used yet unsigned int ramp_up_rate_max = 50; // 3°C/s unsigned int ramp_down_max = 6; // 6°C/s max unsigned int ramp_down_min = 2; // 2°C/s max // profile temp durations unsigned int Ts_duration_min = 60; // s unsigned int Ts_duration_max = 180; // s unsigned int Tl_duration_min = 60; // 60-150s unsigned int Tl_duration_max = 150; // 60-150s unsigned int Tp_duration_min = 20; // 20-40s unsigned int Tp_duration_max = 40; // 20-40s } Profile; // timestamps of event beginnings/ends unsigned int Ts_time_start = 0; unsigned int Ts_time_end = 0; unsigned int Tl_time_start = 0; unsigned int Tl_time_end = 0; unsigned int Tp_time_start = 0; unsigned int Tp_time_end = 0; // thermostat static unsigned int set_min = 0; static unsigned int set_max = 0; static int set_dt_min = 0; static int set_dt_max = 0; // state machine static unsigned int error_condition = 0; static byte state = 0; static boolean is_oven_heating = false; // ui stuff static boolean led_on = false; static boolean disable_checks = true; //Pin assignments for SainSmart LCD Keypad Shield static LiquidCrystal lcd(8, 9, 4, 5, 6, 7); static DFR_Key keypad; void setup() { Serial.begin(9600); delay(300); pinMode(13, OUTPUT); pinMode(2, INPUT); set_start_state(); lcd.begin(16, 2); lcd.clear(); keypad.setRate(10); } void print_profile_state() { String tmp("Profile: "); tmp += state; tmp += "/"; tmp += END_STATE; lcd.setCursor(0, 1); lcd.print(tmp); } void print_status() { String tmp("T: "); if (time < 10) tmp += "00"; else if (time < 100) tmp += "0"; tmp += time; tmp += " Tmp: "; if (temperature < 10) tmp += "00"; else if (temperature < 100) tmp += "0"; tmp += temperature; lcd.setCursor(0, 0); lcd.print(tmp); } void print_config_state() { for (;;) { } } /* led edit mode procedures * global menu (up/down) -> select * start * edit profile * select value (up/down) -> select | left * value (up/down) -> select | left * */ void get_time() { if (state>0) time++; } void control_oven() { if (temperature < set_min && !is_oven_heating) { is_oven_heating = true; Serial.println("Oven turned on"); } else if (temperature > set_min && is_oven_heating) { is_oven_heating = false; Serial.println("Oven turned off"); } } void set_temp(int min, int max, int dt_min, int dt_max) { set_min = min; set_max = max; set_dt_min = dt_min; set_dt_max = dt_max; } void get_temp() { last_temperature = temperature; temperature = int(float(analogRead(2)) * 0.2929); actual_dt = temperature - last_temperature; } void check_dt() { if (disable_checks) return; if (actual_dt > set_dt_max) { error_condition |= E_DT_MAX; } if (actual_dt < set_dt_min) { error_condition |= E_DT_MIN; } } void print_debug() { Serial.print("Time: "); Serial.print(time); Serial.print(", temperatur: "); Serial.print(temperature); Serial.print(", last_temperatur: "); Serial.print(last_temperature); Serial.print(", state: "); Serial.print(state); Serial.print(", Error: "); Serial.println(error_condition); } void check_max_duration() { if (disable_checks) return; Serial.println(time); if (time > time_max) { error_condition |= E_TIME_MAX; } Serial.println(time); } void check_Ts_duration_min() { if (disable_checks) return; Tl_time_end = time; if (time - Tl_time_start < Tl_duration_min) { error_condition |= E_TL_TOO_SHORT; } } void check_Ts_duration_max() { if (disable_checks) return; if (time - Ts_time_start > Tl_duration_max) { error_condition |= E_TS_TOO_LONG; } } void check_Tl_duration_min() { if (disable_checks) return; Tl_time_end = time; if (time - Tl_time_start < Tl_duration_min) { error_condition |= E_TL_TOO_SHORT; } } void check_Tl_duration_max() { if (disable_checks) return; if (time - Tl_time_start > Tl_duration_max) { error_condition |= E_TL_TOO_LONG; } } void check_Tp_duration_min() { Tp_time_end = time; if (time - Tp_time_start < Tp_duration_min) { error_condition |= E_TP_TOO_SHORT; } } void check_Tp_duration_max() { if (disable_checks) return; if (time - Tp_time_start > Tp_duration_max) { error_condition |= E_TP_TOO_LONG; } } void set_start_state() { led_on = false; digitalWrite(13, LOW); error_condition = 0; state = START_STATE; get_temp(); last_temperature = temperature; actual_dt = temperature - last_temperature; set_temp(Tp-5, Tp, 0, ramp_up_rate_max); } void set_preheat_state() { Serial.println("Changing state to PREHEAT_STATE"); state++; } void set_ramp_up_state() { Serial.println("Changed state to RAMP_UP_STATE"); state++; } void set_tal_first_state() { Serial.println("Changed state to TAL_FIRST_STATE"); state++; } void set_peak_state() { Serial.println("Changed state to PEAK_STATE"); state++; } void set_tal_second_state() { Serial.println("Changed state to TAL_SECOND_STATE"); set_temp(25, 25, -3, -6); state++; } void set_ramp_down_state() { Serial.println("Changed state to RAMP_DOWN_STATE"); state++; } void set_end_state() { Serial.println("Changed state to END_STATE"); state++; } void set_error_state() { if (state != ERROR_STATE) { Serial.println("Changed state to ERROR_STATE"); set_temp(0, 0, 0, 0); state = ERROR_STATE; } } void handle_start_state() { check_max_duration(); Serial.println(time); if (temperature > Ts_min) { Ts_time_start = time; set_preheat_state(); } } void handle_preheat_state() { check_Ts_duration_max(); check_max_duration(); if (temperature > Ts_max) { check_Ts_duration_min(); set_ramp_up_state(); } } void handle_ramp_up_state() { check_max_duration(); if (temperature > Tl) { Tl_time_start = time; set_tal_first_state(); } } void handle_tal_first_state() { check_max_duration(); check_Tl_duration_max(); if (temperature > Tp - 5) { Tp_time_start = time; set_peak_state(); } } void handle_peak_state() { check_Tl_duration_max(); check_Tp_duration_max(); if (time - Tp_time_start > Tp_duration_max) { check_Tp_duration_min(); set_tal_second_state(); } } void handle_tal_second_state() { check_Tl_duration_max(); if (temperature < Tl) { check_Tl_duration_min(); set_ramp_down_state(); } } void handle_ramp_down_state() { if (temperature < Ts_min) { set_end_state(); } } void handle_end_state() { // while(true) { // continue; // } } void handle_error_state() { if (led_on) { digitalWrite(13, LOW); led_on = false; } else { digitalWrite(13, HIGH); led_on = true; } if (error_condition & E_DT_MIN) Serial.println("Error: delta °K/second too low"); if (error_condition & E_DT_MAX) Serial.println("Error: delta °K/second too big"); if (error_condition & E_TIME_MAX) Serial.println("Error: reflow process does take too long"); if (error_condition & E_TS_TOO_SHORT) Serial.println("Error: heatup duration was too short"); if (error_condition & E_TS_TOO_LONG) Serial.println("Error: heatup duration was too long"); if (error_condition & E_TL_TOO_SHORT) Serial.println("Error: temperature above liquidus duration was too short"); if (error_condition & E_TL_TOO_LONG) Serial.println("Error: temperature above liquidus duration was too long"); if (error_condition & E_TP_TOO_LONG) Serial.println("Error: peak temperature duration was too short"); if (error_condition & E_TP_TOO_SHORT) Serial.println("Error: peak temperature duration was too long"); } void loop() { get_time(); get_temp(); check_dt(); if (!error_condition) { print_debug(); } else { set_error_state(); } switch (state) { case CONFIG_STATE: handle_config_state(); print_config_state() break; case START_STATE: handle_start_state(); break; case PREHEAT_STATE: handle_preheat_state(); break; case RAMP_UP_STATE: handle_ramp_up_state(); break; case TAL_FIRST_STATE: handle_tal_first_state(); break; case PEAK_STATE: handle_peak_state(); break; case TAL_SECOND_STATE: Tl_time_end = time; handle_tal_second_state(); break; case RAMP_DOWN_STATE: handle_ramp_down_state(); break; case END_STATE: handle_end_state(); break; case ERROR_STATE: handle_error_state(); break; default: break; } control_oven(); // lcd.clear(); print_debug(); print_status(); print_profile_state(); delay(1000); }