diff --git a/libs/oven_control.cpp b/libs/oven_control.cpp new file mode 100644 index 0000000..8602e14 --- /dev/null +++ b/libs/oven_control.cpp @@ -0,0 +1,428 @@ +#include "oven_control.h" +#include +#include +#include "profile.h" + +//Pin assignments for SainSmart LCD Keypad Shield +LiquidCrystal _lcd(8, 9, 4, 5, 6, 7); +DFR_Key _keypad; +Profile _profile; + +OvenCtl::OvenCtl() { + + time = 0; + temperature = 25; + last_temperature = 25; + actual_dt = 0; + // timestamps of event beginnings/ends + Ts_time_start = 0; + Ts_time_end = 0; + Tl_time_start = 0; + Tl_time_end = 0; + Tp_time_start = 0; + Tp_time_end = 0; + + // thermostat + set_min = 0; + set_max = 0; + set_dt_min = 0; + set_dt_max = 0; + + state = 0; + error_condition = 0; + is_oven_heating = false; + + // ui stuff + led_on = false; + disable_checks = true; + lcd = &_lcd; + keypad = &_keypad; + profile = &_profile; + lcd->begin(16, 2); +} + +void OvenCtl::send_state() { + Serial.write(time); + Serial.write(temperature); + Serial.write(last_temperature); + Serial.write(state); + Serial.write(error_condition); +} + +void OvenCtl::send_config() { + int tmp; + for (int i=0;i < PI_END; i++) + { + tmp = profile->data[i]; + Serial.write(tmp & 0xff); + Serial.write((tmp >> 8 ) & 0xff); + } +// Serial.print("\n"); +} + +void OvenCtl::handle_states() { + if (state > 0) + time++; + get_temp(); + check_dt(); + + // if (!error_condition) { + // print_debug(); + // } + // else { + // set_error_state(); + // } + // + switch (state) { + case CONFIG_STATE: + if (profile->handle_config_state(lcd, keypad)) + set_start_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(); + if (state > 0) { + print_status(); + send_state(); + delay(1000); + } +} + + +void OvenCtl::print_profile_state() { + +} + + +void OvenCtl::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); + + tmp = "Profile: "; + tmp += state; + tmp += "/"; + tmp += END_STATE; + lcd->setCursor(0, 1); + lcd->print(tmp); + lcd->setCursor(13, 1); + if (is_oven_heating) + lcd->print("on "); + else + lcd->print("off"); +} + + +void OvenCtl::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 OvenCtl::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 OvenCtl::get_temp() { + last_temperature = temperature; + temperature = int(float(analogRead(2)) * 0.2929); + actual_dt = temperature - last_temperature; +} + +void OvenCtl::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 OvenCtl::check_max_duration() { + if (disable_checks) + return; + Serial.println(time); + if (time > profile->data[PI_TIME_MAX]) { + error_condition |= E_TIME_MAX; + } + Serial.println(time); +} + +void OvenCtl::check_Ts_duration_min() { + if (disable_checks) + return; + Tl_time_end = time; + if (time - Tl_time_start < profile->data[PI_TL_DURATION_MIN]) { + error_condition |= E_TL_TOO_SHORT; + } +} + +void OvenCtl::check_Ts_duration_max() { + if (disable_checks) + return; + if (time - Ts_time_start > profile->data[PI_TL_DURATION_MAX]) { + error_condition |= E_TS_TOO_LONG; + } +} + + +void OvenCtl::check_Tl_duration_min() { + if (disable_checks) + return; + Tl_time_end = time; + if (time - Tl_time_start < profile->data[PI_TL_DURATION_MIN]) { + error_condition |= E_TL_TOO_SHORT; + } +} + +void OvenCtl::check_Tl_duration_max() { + if (disable_checks) + return; + if (time - Tl_time_start > profile->data[PI_TL_DURATION_MAX]) { + error_condition |= E_TL_TOO_LONG; + } +} + +void OvenCtl::check_Tp_duration_min() { + Tp_time_end = time; + if (time - Tp_time_start < profile->data[PI_TP_DURATION_MIN]) { + error_condition |= E_TP_TOO_SHORT; + } +} + +void OvenCtl::check_Tp_duration_max() { + if (disable_checks) + return; + if (time - Tp_time_start > profile->data[PI_TP_DURATION_MAX]) { + error_condition |= E_TP_TOO_LONG; + } +} + +void OvenCtl::set_config_state() { + send_config(); + profile->print_config_state_0(lcd); +} + +void OvenCtl::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(profile->data[PI_TP]-5, profile->data[PI_TP], 0, profile->data[PI_RAMP_UP_MAX]); + lcd->clear(); +} + + +void OvenCtl::set_preheat_state() { + Serial.println("Changing state to PREHEAT_STATE"); + state++; +} + + +void OvenCtl::set_ramp_up_state() { + Serial.println("Changed state to RAMP_UP_STATE"); + state++; +} + + +void OvenCtl::set_tal_first_state() { + Serial.println("Changed state to TAL_FIRST_STATE"); + state++; +} + + +void OvenCtl::set_peak_state() { + Serial.println("Changed state to PEAK_STATE"); + state++; +} + + +void OvenCtl::set_tal_second_state() { + Serial.println("Changed state to TAL_SECOND_STATE"); + set_temp(25, 25, -3, -6); + state++; +} + + +void OvenCtl::set_ramp_down_state() { + Serial.println("Changed state to RAMP_DOWN_STATE"); + state++; +} + + +void OvenCtl::set_end_state() { + Serial.println("Changed state to END_STATE"); + state++; +} + + +void OvenCtl::set_error_state() { + if (state != ERROR_STATE) { + Serial.println("Changed state to ERROR_STATE"); + set_temp(0, 0, 0, 0); + state = ERROR_STATE; + } +} + + +void OvenCtl::handle_config_state() { + if (profile->handle_config_state(lcd, keypad)) + state++; +} + +void OvenCtl::handle_start_state() { + check_max_duration(); + Serial.println(time); + if (temperature > profile->data[PI_TS_MIN]) { + Ts_time_start = time; + set_preheat_state(); + } +} + + +void OvenCtl::handle_preheat_state() { + check_Ts_duration_max(); + check_max_duration(); + if (temperature > profile->data[PI_TS_MAX]) { + check_Ts_duration_min(); + set_ramp_up_state(); + } +} + + +void OvenCtl::handle_ramp_up_state() { + check_max_duration(); + if (temperature > profile->data[PI_TL]) { + Tl_time_start = time; + set_tal_first_state(); + } +} + + +void OvenCtl::handle_tal_first_state() { + check_max_duration(); + check_Tl_duration_max(); + if (temperature > profile->data[PI_TP] - 5) { + Tp_time_start = time; + set_peak_state(); + } +} + + +void OvenCtl::handle_peak_state() { + check_Tl_duration_max(); + check_Tp_duration_max(); + if (time - Tp_time_start > profile->data[PI_TP_DURATION_MAX]) { + check_Tp_duration_min(); + set_tal_second_state(); + } +} + + +void OvenCtl::handle_tal_second_state() { + check_Tl_duration_max(); + if (temperature < profile->data[PI_TL]) { + check_Tl_duration_min(); + set_ramp_down_state(); + } +} + +void OvenCtl::handle_ramp_down_state() { + if (temperature < profile->data[PI_TS_MIN]) { + set_end_state(); + } +} + + +void OvenCtl::handle_end_state() { +// while(true) { +// continue; +// } +} + + + +void OvenCtl::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(PSTR("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"); +} diff --git a/libs/oven_control.h b/libs/oven_control.h new file mode 100644 index 0000000..7a185b2 --- /dev/null +++ b/libs/oven_control.h @@ -0,0 +1,116 @@ +#ifndef _H_OVEN_CTL +#define _H_OVEN_CTL + +// +// +// 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 +#define E_CONFIG 512 // error happened in config state + +#include + +class LiquidCrystal; +class DFR_Key; +class Profile; + +class OvenCtl { +public: + // system time, timestamps and temperatures from sensors + int time; // profile seconds + int temperature; // actual oven temp + int last_temperature; // last oven temp + int actual_dt; // actual difference from last to actual temperatur + + // timestamps of event beginnings/ends + int Ts_time_start; + int Ts_time_end; + int Tl_time_start; + int Tl_time_end; + int Tp_time_start; + int Tp_time_end; + + // thermostat + int set_min; + int set_max; + int set_dt_min; + int set_dt_max; + + // ui stuff + boolean led_on; + boolean disable_checks; + + // state machine + unsigned int error_condition; + unsigned int state; + boolean is_oven_heating; + + LiquidCrystal * lcd; + DFR_Key * keypad; + Profile * profile; + + OvenCtl(); + void handle_states(); + + void print_profile_state(); + void print_status(); + void control_oven(); + void set_temp(int, int, int, int); + void get_temp(); + void check_dt(); + void check_max_duration(); + + void set_config_state(); + void set_start_state(); + void set_preheat_state(); + void set_tal_first_state(); + void set_ramp_up_state(); + void set_peak_state(); + void set_tal_second_state(); + void set_ramp_down_state(); + void set_end_state(); + void set_error_state(); + + void handle_config_state(); + void handle_start_state(); + void handle_ramp_up_state(); + void handle_preheat_state(); + void handle_tal_first_state(); + void handle_peak_state(); + void handle_tal_second_state(); + void handle_ramp_down_state(); + void handle_end_state(); + void handle_error_state(); + + void check_Ts_duration_min(); + void check_Ts_duration_max(); + void check_Tl_duration_min(); + void check_Tl_duration_max(); + void check_Tp_duration_min(); + void check_Tp_duration_max(); + + void send_state(); + void send_config(); +}; + +#endif + diff --git a/libs/profile.cpp b/libs/profile.cpp new file mode 100644 index 0000000..1db9e35 --- /dev/null +++ b/libs/profile.cpp @@ -0,0 +1,191 @@ +#include "profile.h" +#include "oven_control.h" +#include +#include + +#define P_TS_MIN 0 +#define P_TS_MAX 1 +#define P_TL 2 +#define P_TP 3 +#define P_TIME_MAX 4 + +// PROFILE TEMP PER SECOND RATES +#define P_RAMP_UP_RATE_MIN 5 +#define P_RAMP_UP_RATE_MAX 6 +#define P_RAMP_DOWN_MAX 7 +#define P_RAMP_DOWN_MIN 8 + +// PROFILE TEMP DURATIONS +#define P_TS_DURATION_MIN 9 +#define P_TS_DURATION_MAX 10 +#define P_TL_DURATION_MIN 11 +#define P_TL_DURATION_MAX 12 +#define P_TP_DURATION_MIN 13 +#define P_TP_DURATION_MAX 14 +#define P_END 15 + + +Profile::Profile() : + data({150, // °C + 200, // °C + 217, // °C + 260, // 245-260°C + 480, // seconds + + // profile temp per second rates + 0, // not used yet + 50, // 3°C/second + -2, // 2°C/seconds min + -6, // 6°C/seconds max + + // profile temp durations + 60, + 180, + 60, + 150, + 20, + 40}), + config_index(0), + config_state(0), + key(NO_KEY) {} + +boolean Profile::handle_config_state(LiquidCrystal * lcd, DFR_Key * keypad) { + boolean changed = false; + + key = keypad->getKey(); + + switch (config_state) { + case 0: + if (key == SELECT_KEY) { + config_state = 2; + } + else if (key > 0) { + config_state++; + print_config_state(lcd); + } + break; + case 1: + switch (key) { + case LEFT_KEY: + config_index = (config_index-1) % P_END; + changed = true; + break; + case RIGHT_KEY: + config_index = (config_index+1) % P_END; + changed = true; + break; + case UP_KEY: + data[config_index]++; + changed = true; + break; + case DOWN_KEY: + data[config_index]--; + changed = true; + break; + case SELECT_KEY: + config_state = 2; + break; + default: + ; + } + if (changed) + print_config_state(lcd); + break; + case 2: + return true; + } + return false; +} + +void Profile::print_config_state_0(LiquidCrystal * lcd) { + lcd->clear(); + lcd->setCursor(0, 0); + lcd->print("start | config"); + lcd->setCursor(0, 1); + lcd->print("[sel] | [other]"); +} + +void Profile::print_config_state(LiquidCrystal * lcd) { + lcd->clear(); + switch (config_index) { + case P_TS_MIN: + lcd->setCursor(0,0); + lcd->print("P_TS_MIN: "); + lcd->print(data[PI_TS_MIN]); + break; + case P_TS_MAX: + lcd->setCursor(0,0); + lcd->print("P_TS_MAX: "); + lcd->print(data[PI_TS_MAX]); + break; + case P_TL: + lcd->setCursor(0,0); + lcd->print("Tl: "); + lcd->print(data[PI_TL]); + break; + case P_TP: + lcd->setCursor(0,0); + lcd->print("Tp: "); + lcd->print(data[PI_TP]); + break; + case P_TIME_MAX: + lcd->setCursor(0,0); + lcd->print("time_max: "); + lcd->print(data[PI_TIME_MAX]); + break; + + // PROFILE TEMP PER SECOND RATES + case P_RAMP_UP_RATE_MIN: + lcd->setCursor(0,0); + lcd->print("ramp_up_min: "); + lcd->print(data[PI_RAMP_UP_MIN]); + break; + case P_RAMP_UP_RATE_MAX: + lcd->setCursor(0,0); + lcd->print("ramp_up_max: "); + lcd->print(data[PI_RAMP_UP_MAX]); + break; + case P_RAMP_DOWN_MAX: + lcd->setCursor(0,0); + lcd->print("ramp_down_min: "); + lcd->print(data[PI_RAMP_DOWN_MIN]); + break; + case P_RAMP_DOWN_MIN: + lcd->setCursor(0,0); + lcd->print("ramp_down_max: "); + lcd->print(data[PI_RAMP_DOWN_MAX]); + break; + + // PROFILE TEMP DURATIONS + case P_TS_DURATION_MIN: + lcd->setCursor(0,0); + lcd->print("Ts_duration_min: "); + lcd->print(data[PI_TS_DURATION_MIN]); + break; + case P_TS_DURATION_MAX: + lcd->setCursor(0,0); + lcd->print("Ts_duration_max: "); + lcd->print(data[PI_TS_DURATION_MAX]); + break; + case P_TL_DURATION_MIN: + lcd->setCursor(0,0); + lcd->print("Tl_duration_min: "); + lcd->print(data[PI_TL_DURATION_MIN]); + break; + case P_TL_DURATION_MAX: + lcd->setCursor(0,0); + lcd->print("Tl_duration_max: "); + lcd->print(data[PI_TL_DURATION_MAX]); + break; + case P_TP_DURATION_MIN: + lcd->setCursor(0,0); + lcd->print("Tp_duration_min: "); + lcd->print(data[PI_TP_DURATION_MIN]); + break; + case P_TP_DURATION_MAX: + lcd->setCursor(0,0); + lcd->print("Tp_duration_max: "); + lcd->print(data[PI_TP_DURATION_MAX]); + break; + } +} \ No newline at end of file diff --git a/libs/profile.h b/libs/profile.h new file mode 100644 index 0000000..34e0175 --- /dev/null +++ b/libs/profile.h @@ -0,0 +1,45 @@ +#ifndef _H_PROFILE +#define _H_PROFILE + +#include + +class LiquidCrystal; +class DFR_Key; + +#define PI_TS_MIN 0 +#define PI_TS_MAX 1 +#define PI_TL 2 +#define PI_TP 3 +#define PI_TIME_MAX 4 + +// profile temp per second rates +#define PI_RAMP_UP_MIN 5 +#define PI_RAMP_UP_MAX 6 +#define PI_RAMP_DOWN_MIN 7 +#define PI_RAMP_DOWN_MAX 8 + +// profile temp durations +#define PI_TS_DURATION_MIN 9 +#define PI_TS_DURATION_MAX 10 +#define PI_TL_DURATION_MIN 11 +#define PI_TL_DURATION_MAX 12 +#define PI_TP_DURATION_MIN 13 +#define PI_TP_DURATION_MAX 14 +#define PI_END 15 + + +class Profile { +public: + int data[15]; + + unsigned int config_index; + int config_state; + int key; + + Profile(); + boolean handle_config_state(LiquidCrystal * lcd, DFR_Key * keypad); + void print_config_state(LiquidCrystal * lcd); + void print_config_state_0(LiquidCrystal * lcd); +}; + +#endif diff --git a/libs/ui.h b/libs/ui.h new file mode 100644 index 0000000..188fb82 --- /dev/null +++ b/libs/ui.h @@ -0,0 +1,35 @@ +// #ifndef _H_UI +// #define _H_UI +// +// #include +// #include +// +// class Profile { +// public: +// int Ts_min; +// int Ts_max; +// int Tl; +// int Tp; +// int time_max; +// +// // profile temp per second rates +// int ramp_up_min; +// int ramp_up_max; +// int ramp_down_max; +// int ramp_down_min; +// +// // profile temp durations +// int Ts_duration_min; +// int Ts_duration_max; +// int Tl_duration_min; +// int Tl_duration_max; +// int Tp_duration_min; +// int Tp_duration_max; +// int config_index; +// +// Profile(); +// void handle_config_state(LiquidCrystal & lcd, DFR_Key & keypad); +// void print_config_state(LiquidCrystal & lcd); +// }; +// +// #endif