|
@@ -2,14 +2,12 @@
|
|
|
#include <U8g2lib.h>
|
|
#include <U8g2lib.h>
|
|
|
#include <PID_v1.h>
|
|
#include <PID_v1.h>
|
|
|
#include <MAX6675.h>
|
|
#include <MAX6675.h>
|
|
|
-#include <SPI.h>
|
|
|
|
|
-
|
|
|
|
|
-#define THERMOCOUPLE_CS_PIN 7
|
|
|
|
|
|
|
+#include <EasyButton.h>
|
|
|
|
|
|
|
|
#define DISPLAY_RESET_PIN 10
|
|
#define DISPLAY_RESET_PIN 10
|
|
|
#define DISPLAY_DC_PIN 9
|
|
#define DISPLAY_DC_PIN 9
|
|
|
#define DISPLAY_CS_PIN 8
|
|
#define DISPLAY_CS_PIN 8
|
|
|
-
|
|
|
|
|
|
|
+#define THERMOCOUPLE_CS_PIN 7
|
|
|
#define DONE_LED_PIN 5
|
|
#define DONE_LED_PIN 5
|
|
|
#define SSR_PIN 4
|
|
#define SSR_PIN 4
|
|
|
#define BUTTON_PIN 3
|
|
#define BUTTON_PIN 3
|
|
@@ -41,25 +39,24 @@ typedef enum REFLOW_STATUS
|
|
|
REFLOW_STATUS_ON
|
|
REFLOW_STATUS_ON
|
|
|
} reflowStatus_t;
|
|
} reflowStatus_t;
|
|
|
|
|
|
|
|
-typedef enum DEBOUNCE_STATE
|
|
|
|
|
-{
|
|
|
|
|
- DEBOUNCE_STATE_IDLE,
|
|
|
|
|
- DEBOUNCE_STATE_CHECK,
|
|
|
|
|
- DEBOUNCE_STATE_RELEASE
|
|
|
|
|
-} debounceState_t;
|
|
|
|
|
|
|
|
|
|
-typedef enum SWITCH
|
|
|
|
|
-{
|
|
|
|
|
- SWITCH_NONE,
|
|
|
|
|
- SWITCH_1
|
|
|
|
|
-} switch_t;
|
|
|
|
|
|
|
+struct reflowProfile{
|
|
|
|
|
+ const char* profileName;
|
|
|
|
|
+ int soakTemp;
|
|
|
|
|
+ unsigned long soakPeriodMS;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+#define NUM_REFLOW_PROFILES 3
|
|
|
|
|
+reflowProfile profiles[NUM_REFLOW_PROFILES] = {
|
|
|
|
|
+ {"Sanitize Masks", 70, 1800000}, // 30*60*1000 30 minutes
|
|
|
|
|
+ {"Dry PLA", 45, 1800000*8},
|
|
|
|
|
+ {"Dry PETG", 70, 1800000*4}
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
// ***** CONSTANTS *****
|
|
// ***** CONSTANTS *****
|
|
|
-#define TEMPERATURE_ROOM 70
|
|
|
|
|
-#define TEMPERATURE_SOAK 70
|
|
|
|
|
|
|
+#define TEMPERATURE_ROOM 45
|
|
|
#define TEMPERATURE_COOL_MIN 50
|
|
#define TEMPERATURE_COOL_MIN 50
|
|
|
#define SENSOR_SAMPLING_TIME 1000
|
|
#define SENSOR_SAMPLING_TIME 1000
|
|
|
-#define SOAK_PERIOD_MS 1800000 // 30*60*1000 30 minutes
|
|
|
|
|
#define DEBOUNCE_PERIOD_MIN 50
|
|
#define DEBOUNCE_PERIOD_MIN 50
|
|
|
|
|
|
|
|
// ***** PID PARAMETERS *****
|
|
// ***** PID PARAMETERS *****
|
|
@@ -82,7 +79,7 @@ double output;
|
|
|
double kp = PID_KP_PREHEAT;
|
|
double kp = PID_KP_PREHEAT;
|
|
|
double ki = PID_KI_PREHEAT;
|
|
double ki = PID_KI_PREHEAT;
|
|
|
double kd = PID_KD_PREHEAT;
|
|
double kd = PID_KD_PREHEAT;
|
|
|
-int windowSize;
|
|
|
|
|
|
|
+unsigned int windowSize;
|
|
|
unsigned long now;
|
|
unsigned long now;
|
|
|
unsigned long windowStartTime;
|
|
unsigned long windowStartTime;
|
|
|
unsigned long nextRead;
|
|
unsigned long nextRead;
|
|
@@ -93,23 +90,20 @@ unsigned long completePeriod;
|
|
|
reflowState_t reflowState = REFLOW_STATE_IDLE;
|
|
reflowState_t reflowState = REFLOW_STATE_IDLE;
|
|
|
// Reflow oven controller status
|
|
// Reflow oven controller status
|
|
|
reflowStatus_t reflowStatus = REFLOW_STATUS_OFF;
|
|
reflowStatus_t reflowStatus = REFLOW_STATUS_OFF;
|
|
|
-// Switch debounce state machine state variable
|
|
|
|
|
-debounceState_t debounceState;
|
|
|
|
|
-// Switch debounce timer
|
|
|
|
|
-long lastDebounceTime;
|
|
|
|
|
-// Switch press status
|
|
|
|
|
-switch_t switchStatus;
|
|
|
|
|
// did encounter a thermocouple error?
|
|
// did encounter a thermocouple error?
|
|
|
int tcErrorCount = 0;
|
|
int tcErrorCount = 0;
|
|
|
bool TCError = false;
|
|
bool TCError = false;
|
|
|
|
|
+bool startReflow = false;
|
|
|
|
|
+int activeReflowProfile = 0;
|
|
|
|
|
|
|
|
MAX6675 tcouple(THERMOCOUPLE_CS_PIN);
|
|
MAX6675 tcouple(THERMOCOUPLE_CS_PIN);
|
|
|
U8G2_SSD1305_128X64_ADAFRUIT_F_4W_HW_SPI u8g2(U8G2_R0, DISPLAY_CS_PIN, DISPLAY_DC_PIN, DISPLAY_RESET_PIN);
|
|
U8G2_SSD1305_128X64_ADAFRUIT_F_4W_HW_SPI u8g2(U8G2_R0, DISPLAY_CS_PIN, DISPLAY_DC_PIN, DISPLAY_RESET_PIN);
|
|
|
PID reflowOvenPID(&inputTemp, &output, &setpoint, kp, ki, kd, DIRECT);
|
|
PID reflowOvenPID(&inputTemp, &output, &setpoint, kp, ki, kd, DIRECT);
|
|
|
|
|
+EasyButton button(BUTTON_PIN);
|
|
|
|
|
|
|
|
// Called once in setup, sets global display attributes.
|
|
// Called once in setup, sets global display attributes.
|
|
|
-void u8g2_prepare(void) {
|
|
|
|
|
- u8g2.setFont(u8g2_font_8x13_tf);
|
|
|
|
|
|
|
+inline void u8g2_prepare(void) {
|
|
|
|
|
+ u8g2.setFont(u8g2_font_8x13_tr);
|
|
|
u8g2.setFontRefHeightExtendedText();
|
|
u8g2.setFontRefHeightExtendedText();
|
|
|
u8g2.setDrawColor(1);
|
|
u8g2.setDrawColor(1);
|
|
|
u8g2.setFontPosTop();
|
|
u8g2.setFontPosTop();
|
|
@@ -137,66 +131,35 @@ void readTemp(void) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void handleSwitch(void) {
|
|
|
|
|
- // If switch 1 is pressed
|
|
|
|
|
- if (switchStatus == SWITCH_1)
|
|
|
|
|
- {
|
|
|
|
|
- // If currently reflow process is on going
|
|
|
|
|
- if (reflowStatus == REFLOW_STATUS_ON)
|
|
|
|
|
- {
|
|
|
|
|
- // Button press is for cancelling
|
|
|
|
|
- // Turn off reflow process
|
|
|
|
|
- reflowStatus = REFLOW_STATUS_OFF;
|
|
|
|
|
- // Reinitialize state machine
|
|
|
|
|
- reflowState = REFLOW_STATE_IDLE;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Simple switch debounce state machine (for switch #1 (both analog & digital
|
|
|
|
|
- // switch supported))
|
|
|
|
|
- switch (debounceState)
|
|
|
|
|
|
|
+void buttonPressed(void) {
|
|
|
|
|
+ // If currently reflow process is on going
|
|
|
|
|
+ if (reflowStatus == REFLOW_STATUS_ON)
|
|
|
{
|
|
{
|
|
|
- case DEBOUNCE_STATE_IDLE:
|
|
|
|
|
- // No valid switch press
|
|
|
|
|
- switchStatus = SWITCH_NONE;
|
|
|
|
|
- // If switch #1 is pressed
|
|
|
|
|
- if (digitalRead(BUTTON_PIN) == LOW)
|
|
|
|
|
- {
|
|
|
|
|
- // Intialize debounce counter
|
|
|
|
|
- lastDebounceTime = millis();
|
|
|
|
|
- // Proceed to check validity of button press
|
|
|
|
|
- debounceState = DEBOUNCE_STATE_CHECK;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case DEBOUNCE_STATE_CHECK:
|
|
|
|
|
- // If switch #1 is still pressed
|
|
|
|
|
- if (digitalRead(BUTTON_PIN) == LOW)
|
|
|
|
|
- {
|
|
|
|
|
- // If minimum debounce period is completed
|
|
|
|
|
- if ((millis() - lastDebounceTime) > DEBOUNCE_PERIOD_MIN)
|
|
|
|
|
- {
|
|
|
|
|
- // Proceed to wait for button release
|
|
|
|
|
- debounceState = DEBOUNCE_STATE_RELEASE;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // False trigger
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- // Reinitialize button debounce state machine
|
|
|
|
|
- debounceState = DEBOUNCE_STATE_IDLE;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ // Button press is for cancelling
|
|
|
|
|
+ // Turn off reflow process
|
|
|
|
|
+ reflowStatus = REFLOW_STATUS_OFF;
|
|
|
|
|
+ // Reinitialize state machine
|
|
|
|
|
+ reflowState = REFLOW_STATE_IDLE;
|
|
|
|
|
+ activeReflowProfile = 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ startReflow = true;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- case DEBOUNCE_STATE_RELEASE:
|
|
|
|
|
- if (digitalRead(BUTTON_PIN) == HIGH)
|
|
|
|
|
- {
|
|
|
|
|
- // Valid switch 1 press
|
|
|
|
|
- switchStatus = SWITCH_1;
|
|
|
|
|
- // Reinitialize button debounce state machine
|
|
|
|
|
- debounceState = DEBOUNCE_STATE_IDLE;
|
|
|
|
|
|
|
+void buttonHeld(void) {
|
|
|
|
|
+ // If currently reflow process is on going
|
|
|
|
|
+ if (reflowStatus == REFLOW_STATUS_ON)
|
|
|
|
|
+ {// Button press is for cancelling
|
|
|
|
|
+ // Turn off reflow process
|
|
|
|
|
+ reflowStatus = REFLOW_STATUS_OFF;
|
|
|
|
|
+ // Reinitialize state machine
|
|
|
|
|
+ reflowState = REFLOW_STATE_IDLE;
|
|
|
|
|
+ activeReflowProfile = 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ activeReflowProfile++;
|
|
|
|
|
+ if (activeReflowProfile >= NUM_REFLOW_PROFILES) {
|
|
|
|
|
+ activeReflowProfile = 0;
|
|
|
}
|
|
}
|
|
|
- break;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -211,16 +174,16 @@ void handleReflowState(void) {
|
|
|
reflowState = REFLOW_STATE_TOO_HOT;
|
|
reflowState = REFLOW_STATE_TOO_HOT;
|
|
|
}
|
|
}
|
|
|
// If switch is pressed, start reflow process
|
|
// If switch is pressed, start reflow process
|
|
|
- else if (switchStatus == SWITCH_1)
|
|
|
|
|
|
|
+ else if (startReflow)
|
|
|
{
|
|
{
|
|
|
// Turn off done LED if it was on from a previous cycle.
|
|
// Turn off done LED if it was on from a previous cycle.
|
|
|
digitalWrite(DONE_LED_PIN, LOW);
|
|
digitalWrite(DONE_LED_PIN, LOW);
|
|
|
// Reset switch state to prevent triggering later code erroneously.
|
|
// Reset switch state to prevent triggering later code erroneously.
|
|
|
- switchStatus = SWITCH_NONE;
|
|
|
|
|
|
|
+ startReflow = false;
|
|
|
// Initialize PID control window starting time
|
|
// Initialize PID control window starting time
|
|
|
windowStartTime = millis();
|
|
windowStartTime = millis();
|
|
|
// Ramp up to minimum soaking temperature
|
|
// Ramp up to minimum soaking temperature
|
|
|
- setpoint = TEMPERATURE_SOAK;
|
|
|
|
|
|
|
+ setpoint = profiles[activeReflowProfile].soakTemp;
|
|
|
// Tell the PID to range between 0 and the full window size
|
|
// Tell the PID to range between 0 and the full window size
|
|
|
reflowOvenPID.SetOutputLimits(0, windowSize);
|
|
reflowOvenPID.SetOutputLimits(0, windowSize);
|
|
|
reflowOvenPID.SetSampleTime(PID_SAMPLE_TIME);
|
|
reflowOvenPID.SetSampleTime(PID_SAMPLE_TIME);
|
|
@@ -234,11 +197,11 @@ void handleReflowState(void) {
|
|
|
case REFLOW_STATE_PREHEAT:
|
|
case REFLOW_STATE_PREHEAT:
|
|
|
reflowStatus = REFLOW_STATUS_ON;
|
|
reflowStatus = REFLOW_STATUS_ON;
|
|
|
// If minimum soak temperature is achieved.
|
|
// If minimum soak temperature is achieved.
|
|
|
- if (inputTemp >= TEMPERATURE_SOAK)
|
|
|
|
|
|
|
+ if (inputTemp >= profiles[activeReflowProfile].soakTemp)
|
|
|
{
|
|
{
|
|
|
soakStartTime = millis();
|
|
soakStartTime = millis();
|
|
|
// Chop soaking period into smaller sub-period
|
|
// Chop soaking period into smaller sub-period
|
|
|
- timerSoak = soakStartTime + SOAK_PERIOD_MS;
|
|
|
|
|
|
|
+ timerSoak = soakStartTime + profiles[activeReflowProfile].soakPeriodMS;
|
|
|
// Set less agressive PID parameters for soaking ramp
|
|
// Set less agressive PID parameters for soaking ramp
|
|
|
reflowOvenPID.SetTunings(PID_KP_SOAK, PID_KI_SOAK, PID_KD_SOAK);
|
|
reflowOvenPID.SetTunings(PID_KP_SOAK, PID_KI_SOAK, PID_KD_SOAK);
|
|
|
// Proceed to soaking state
|
|
// Proceed to soaking state
|
|
@@ -250,7 +213,7 @@ void handleReflowState(void) {
|
|
|
// If micro soak temperature is achieved
|
|
// If micro soak temperature is achieved
|
|
|
if (millis() > timerSoak)
|
|
if (millis() > timerSoak)
|
|
|
{
|
|
{
|
|
|
- timerSoak = millis() + SOAK_PERIOD_MS;
|
|
|
|
|
|
|
+ timerSoak = millis() + profiles[activeReflowProfile].soakPeriodMS;
|
|
|
reflowStatus = REFLOW_STATUS_OFF;
|
|
reflowStatus = REFLOW_STATUS_OFF;
|
|
|
reflowState = REFLOW_STATE_COOL;
|
|
reflowState = REFLOW_STATE_COOL;
|
|
|
}
|
|
}
|
|
@@ -274,6 +237,7 @@ void handleReflowState(void) {
|
|
|
{
|
|
{
|
|
|
// Reflow process ended
|
|
// Reflow process ended
|
|
|
reflowState = REFLOW_STATE_IDLE;
|
|
reflowState = REFLOW_STATE_IDLE;
|
|
|
|
|
+ activeReflowProfile = 0;
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
@@ -340,13 +304,8 @@ void drawScreen(void) {
|
|
|
u8g2.drawStr( 100, rowOffset, "C");
|
|
u8g2.drawStr( 100, rowOffset, "C");
|
|
|
rowOffset += rowSize;
|
|
rowOffset += rowSize;
|
|
|
|
|
|
|
|
- // Thermocouple Status row
|
|
|
|
|
- u8g2.drawStr( 0, rowOffset, "tc_err: ");
|
|
|
|
|
- if (TCError) {
|
|
|
|
|
- u8g2.drawStr( 60, rowOffset, "true");
|
|
|
|
|
- } else {
|
|
|
|
|
- u8g2.drawStr( 60, rowOffset, "false");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Current Profile
|
|
|
|
|
+ u8g2.drawStr( 0, rowOffset, profiles[activeReflowProfile].profileName);
|
|
|
rowOffset += rowSize;
|
|
rowOffset += rowSize;
|
|
|
|
|
|
|
|
// General status row
|
|
// General status row
|
|
@@ -372,7 +331,10 @@ void setup(void) {
|
|
|
pinMode(SSR_PIN, OUTPUT);
|
|
pinMode(SSR_PIN, OUTPUT);
|
|
|
|
|
|
|
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
|
|
-
|
|
|
|
|
|
|
+ button.begin();
|
|
|
|
|
+ button.onPressed(buttonPressed);
|
|
|
|
|
+ button.onPressedFor(1000, buttonHeld);
|
|
|
|
|
+
|
|
|
digitalWrite(DONE_LED_PIN, LOW);
|
|
digitalWrite(DONE_LED_PIN, LOW);
|
|
|
pinMode(DONE_LED_PIN, OUTPUT);
|
|
pinMode(DONE_LED_PIN, OUTPUT);
|
|
|
|
|
|
|
@@ -393,7 +355,7 @@ void loop(void) {
|
|
|
|
|
|
|
|
handleReflowState();
|
|
handleReflowState();
|
|
|
|
|
|
|
|
- handleSwitch();
|
|
|
|
|
|
|
+ button.read();
|
|
|
|
|
|
|
|
handleSSR();
|
|
handleSSR();
|
|
|
|
|
|