#include <LiquidCrystal_I2C.h>
#include <max6675.h>
#include <EEPROM.h>
#include <PID_v1.h>
// Pin tanımlamaları
#define THERMOCOUPLE_SCK_PIN 2
#define THERMOCOUPLE_CS_PIN 3
#define THERMOCOUPLE_SO_PIN 4
#define SSR_PIN 5
#define BUZZER_PIN 6
#define START_BUTTON_PIN 7
#define SETTING_BUTTON_PIN 8
#define UP_BUTTON_PIN 9
#define DOWN_BUTTON_PIN 10
// LCD ve sensör nesneleri
LiquidCrystal_I2C lcd(0x27, 20, 4);
MAX6675 thermocouple(THERMOCOUPLE_SCK_PIN, THERMOCOUPLE_CS_PIN, THERMOCOUPLE_SO_PIN);
// PID değişkenleri (kullanılmayacak ama kod uyumluluğu için bırakıldı)
double setpoint, input, output;
double Kp = 2.0, Ki = 5.0, Kd = 1.0;
PID myPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
// SSR Timing Kontrolü için yeni değişkenler
unsigned long ssrOnTime = 0;
unsigned long ssrOffTime = 0;
unsigned long ssrLastToggle = 0;
bool ssrState = false;
unsigned long ssrOnDuration = 0;
unsigned long ssrOffDuration = 2000; // 2 saniye OFF süresi sabit
// Sistem durumu
enum SystemState {
MAIN_SCREEN,
SETTING_MODE,
RUNNING_PROCESS,
THERMOCOUPLE_ERROR,
TOO_HOT_WARNING
};
SystemState currentState = MAIN_SCREEN;
// Profil yapısı
struct ProfileStep {
int temperature;
int duration; // saniye cinsinden
};
ProfileStep profile[10];
int currentStep = 0;
bool processRunning = false;
int settingStep = 0;
bool settingTemperature = true; // true: sıcaklık ayarı, false: süre ayarı
// Zaman değişkenleri
unsigned long previousMillis = 0;
unsigned long stepStartTime = 0;
unsigned long stepElapsedTime = 0;
unsigned long buzzerPreviousMillis = 0;
unsigned long warningStartTime = 0;
unsigned long processEndTime = 0;
unsigned long screenUpdateTimer = 0;
bool buzzerState = false;
bool processCompleted = false;
int finalBuzzerCount = 0;
// LCD güncelleme kontrolleri
bool screenNeedsUpdate = true;
double lastDisplayedTemp = -999;
int lastDisplayedStep = -1;
unsigned long lastDisplayedTime = 0;
int lastDisplayedTarget = -1;
SystemState lastState = MAIN_SCREEN;
// Buton durumları
bool startPressed = false;
bool settingPressed = false;
bool upPressed = false;
bool downPressed = false;
// EEPROM adresleri
const int EEPROM_BASE_ADDR = 0;
// SSR timing hesaplama fonksiyonu
void calculateSSRTiming(double currentTemp, int targetTemp) {
// Sıcaklık hedefin üstündeyse tamamen kapat
if (currentTemp >= targetTemp) {
ssrOnDuration = 0;
return;
}
// Hedef sıcaklık farkı
double tempDiff = targetTemp - currentTemp;
// Sıcaklık aralıklarına göre ON süresi belirleme
if (tempDiff >= 30) {
ssrOnDuration = 500; // 500ms ON
} else if (tempDiff >= 20) {
ssrOnDuration = 1000; // 1000ms ON
} else if (tempDiff >= 10) {
ssrOnDuration = 1500; // 1500ms ON
} else if (tempDiff > 0) {
ssrOnDuration = 2000; // 2000ms ON
} else {
ssrOnDuration = 0; // Kapat
}
}
// SSR kontrolü
void controlSSR() {
unsigned long currentMillis = millis();
// SSR timing hesapla
calculateSSRTiming(input, profile[currentStep].temperature);
// SSR tamamen kapalı olacaksa
if (ssrOnDuration == 0) {
digitalWrite(SSR_PIN, LOW);
ssrState = false;
return;
}
// Timing kontrolü
if (!ssrState) {
// SSR kapalı durumda
if (currentMillis - ssrLastToggle >= ssrOffDuration) {
// ON durumuna geç
digitalWrite(SSR_PIN, HIGH);
ssrState = true;
ssrLastToggle = currentMillis;
}
} else {
// SSR açık durumda
if (currentMillis - ssrLastToggle >= ssrOnDuration) {
// OFF durumuna geç
digitalWrite(SSR_PIN, LOW);
ssrState = false;
ssrLastToggle = currentMillis;
}
}
}
void setup() {
Serial.begin(9600);
// Pin modları
pinMode(SSR_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(START_BUTTON_PIN, INPUT_PULLUP);
pinMode(SETTING_BUTTON_PIN, INPUT_PULLUP);
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
// LCD başlatma
lcd.init();
lcd.backlight();
// PID başlatma (kullanılmayacak ama uyumluluk için)
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0, 255);
// EEPROM'dan profil yükleme
loadProfileFromEEPROM();
// SSR başlangıç durumu
digitalWrite(SSR_PIN, LOW);
ssrState = false;
ssrLastToggle = millis();
// Ana ekranı göster
displayMainScreen();
}
void loop() {
unsigned long currentMillis = millis();
// Buton okuma
readButtons();
// Sıcaklık okuma ve thermocouple kontrolü
input = thermocouple.readCelsius();
// Thermocouple hata kontrolü
if (isnan(input) || input < -10 || input > 500) {
if (currentState != THERMOCOUPLE_ERROR) {
currentState = THERMOCOUPLE_ERROR;
digitalWrite(SSR_PIN, LOW); // Güvenlik için ısıtıcıyı kapat
ssrState = false;
screenNeedsUpdate = true;
}
} else if (currentState == THERMOCOUPLE_ERROR) {
// Thermocouple düzeldi, ana ekrana dön
currentState = MAIN_SCREEN;
screenNeedsUpdate = true;
}
// Ekran güncelleme zamanlayıcısı (her 500ms)
if (currentMillis - screenUpdateTimer >= 10) {
screenNeedsUpdate = true;
screenUpdateTimer = currentMillis;
}
// Durum makinesı
switch (currentState) {
case THERMOCOUPLE_ERROR:
handleThermocoupleError();
break;
case TOO_HOT_WARNING:
handleTooHotWarning();
break;
case MAIN_SCREEN:
handleMainScreen();
break;
case SETTING_MODE:
handleSettingMode();
break;
case RUNNING_PROCESS:
handleRunningProcess();
break;
}
delay(100);
}
void readButtons() {
startPressed = !digitalRead(START_BUTTON_PIN);
settingPressed = !digitalRead(SETTING_BUTTON_PIN);
upPressed = !digitalRead(UP_BUTTON_PIN);
downPressed = !digitalRead(DOWN_BUTTON_PIN);
// Basit debounce
if (startPressed || settingPressed || upPressed || downPressed) {
delay(200);
}
}
void handleMainScreen() {
if (startPressed && !processRunning) {
// Sıcaklık kontrolü - 50°C üzerinde uyarı
if (input > 50.0) {
currentState = TOO_HOT_WARNING;
warningStartTime = millis();
screenNeedsUpdate = true;
return;
}
startProcess();
} else if (settingPressed) {
enterSettingMode();
}
displayMainScreen();
}
void handleTooHotWarning() {
unsigned long currentMillis = millis();
if (currentMillis - warningStartTime >= 2000) {
// 2 saniye sonra ana ekrana dön
currentState = MAIN_SCREEN;
screenNeedsUpdate = true;
}
displayTooHotWarning();
}
void handleThermocoupleError() {
displayThermocoupleError();
// Hata durumunda hiçbir butona cevap verme
}
void handleSettingMode() {
if (settingPressed) {
if (settingTemperature) {
settingTemperature = false; // Süre ayarına geç
} else {
settingTemperature = true; // Sıcaklık ayarına geç
settingStep++;
if (settingStep >= 10) {
// Tüm ayarlar tamamlandı, EEPROM'a kaydet
saveProfileToEEPROM();
currentState = MAIN_SCREEN;
settingStep = 0;
screenNeedsUpdate = true;
return;
}
}
screenNeedsUpdate = true;
}
if (upPressed) {
if (settingTemperature) {
profile[settingStep].temperature += 5;
if (profile[settingStep].temperature > 300) {
profile[settingStep].temperature = 300;
}
} else {
profile[settingStep].duration += 10;
if (profile[settingStep].duration > 9999) {
profile[settingStep].duration = 9999;
}
}
screenNeedsUpdate = true;
}
if (downPressed) {
if (settingTemperature) {
profile[settingStep].temperature -= 5;
if (profile[settingStep].temperature < 0) {
profile[settingStep].temperature = 0;
}
} else {
profile[settingStep].duration -= 10;
if (profile[settingStep].duration < 0) {
profile[settingStep].duration = 0;
}
}
screenNeedsUpdate = true;
}
displaySettingScreen();
}
void handleRunningProcess() {
unsigned long currentMillis = millis();
// Proses tamamlandıysa final buzzer kontrolü
if (processCompleted) {
// SSR'yi kapat
digitalWrite(SSR_PIN, LOW);
ssrState = false;
if (currentMillis - processEndTime <= 15000) { // 15 saniye boyunca
if (currentMillis - buzzerPreviousMillis >= 1000) { // 1 saniyede bir
buzzerState = !buzzerState;
digitalWrite(BUZZER_PIN, buzzerState);
buzzerPreviousMillis = currentMillis;
if (buzzerState) finalBuzzerCount++;
}
} else {
// 15 saniye sonra buzzer'ı kapat ve ana ekrana dön
digitalWrite(BUZZER_PIN, LOW);
processCompleted = false;
finalBuzzerCount = 0;
stopProcess();
return;
}
displayRunningScreen();
return;
}
// SSR kontrolü (yeni timing tabanlı sistem)
controlSSR();
// Hedef sıcaklığa ulaşıldı mı kontrol et
setpoint = profile[currentStep].temperature;
if (abs(input - setpoint) <= 2.0) { // ±2°C tolerans
if (stepStartTime == 0) {
stepStartTime = currentMillis;
}
stepElapsedTime = (currentMillis - stepStartTime) / 1000;
// Süre kontrolü
if (stepElapsedTime >= profile[currentStep].duration) {
currentStep++;
stepStartTime = 0;
stepElapsedTime = 0;
screenNeedsUpdate = true;
if (currentStep >= 10) {
// Proses tamamlandı - final buzzer başlat
processCompleted = true;
processEndTime = currentMillis;
buzzerPreviousMillis = currentMillis;
finalBuzzerCount = 0;
digitalWrite(SSR_PIN, LOW);
ssrState = false;
return;
}
}
} else {
// Hedef sıcaklığa ulaşılmadıysa süre sayımını sıfırla
if (stepStartTime != 0) {
stepStartTime = 0;
stepElapsedTime = 0;
screenNeedsUpdate = true;
}
}
// 8. adımda buzzer uyarısı
if (currentStep == 7 && !processCompleted) { // 8. adım (0'dan başladığımız için 7)
if (currentMillis - buzzerPreviousMillis >= 1000) {
buzzerState = !buzzerState;
digitalWrite(BUZZER_PIN, buzzerState);
buzzerPreviousMillis = currentMillis;
}
} else if (currentStep == 8 && !processCompleted) { // 9. adımda uyarı kesilir
digitalWrite(BUZZER_PIN, LOW);
}
// Start butonuna basılırsa proses durdur
if (startPressed) {
stopProcess();
}
displayRunningScreen();
}
void startProcess() {
processRunning = true;
currentState = RUNNING_PROCESS;
currentStep = 0;
stepStartTime = 0;
stepElapsedTime = 0;
myPID.SetMode(AUTOMATIC);
// SSR timing kontrolü başlat
ssrLastToggle = millis();
ssrState = false;
digitalWrite(SSR_PIN, LOW);
screenNeedsUpdate = true;
}
void stopProcess() {
processRunning = false;
processCompleted = false;
currentState = MAIN_SCREEN;
currentStep = 0;
stepStartTime = 0;
stepElapsedTime = 0;
finalBuzzerCount = 0;
// SSR'yi kapat
digitalWrite(SSR_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
ssrState = false;
ssrOnDuration = 0;
myPID.SetMode(MANUAL);
output = 0;
screenNeedsUpdate = true;
// Ekran değişkenlerini sıfırla
lastDisplayedTemp = -999;
lastDisplayedStep = -1;
lastDisplayedTime = 0;
lastDisplayedTarget = -1;
lastState = MAIN_SCREEN;
}
void enterSettingMode() {
currentState = SETTING_MODE;
settingStep = 0;
settingTemperature = true;
screenNeedsUpdate = true;
// Ekran değişkenlerini sıfırla
lastDisplayedTemp = -999;
lastDisplayedStep = -1;
lastDisplayedTime = 0;
lastDisplayedTarget = -1;
lastState = SETTING_MODE;
}
void displayMainScreen() {
// Sadece değişiklik varsa ekranı güncelle
if (!screenNeedsUpdate &&
currentState == lastState &&
abs(input - lastDisplayedTemp) < 1.0) {
return;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("HAZIR");
lcd.setCursor(0, 1);
lcd.print("Sicaklik: ");
lcd.print(input, 1);
lcd.print("C");
lcd.setCursor(0, 2);
lcd.print("START: Baslat");
lcd.setCursor(0, 3);
lcd.print("AYAR: Ayarlar");
lastDisplayedTemp = input;
lastState = currentState;
screenNeedsUpdate = false;
}
void displayRunningScreen() {
// Sadece değişiklik varsa ekranı güncelle
if (!screenNeedsUpdate &&
lastState == currentState &&
abs(input - lastDisplayedTemp) < 1.0 &&
currentStep == lastDisplayedStep &&
stepElapsedTime == lastDisplayedTime &&
profile[currentStep].temperature == lastDisplayedTarget) {
return;
}
lcd.clear();
if (processCompleted) {
lcd.setCursor(0, 0);
lcd.print("PROSES TAMAMLANDI");
lcd.setCursor(0, 1);
lcd.print("Sicaklik: ");
lcd.print(input, 1);
lcd.print("C");
lcd.setCursor(0, 2);
lcd.print("Son Uyari: ");
lcd.print(15 - ((millis() - processEndTime) / 1000));
lcd.print("s");
} else {
lcd.setCursor(0, 0);
lcd.print("Adim ");
lcd.print(currentStep + 1);
lcd.print("/10 Calisiyor");
lcd.setCursor(0, 1);
lcd.print("Sicaklik: ");
lcd.print(input, 1);
lcd.print("C");
lcd.setCursor(0, 2);
lcd.print("Hedef: ");
lcd.print(profile[currentStep].temperature);
lcd.print("C");
lcd.setCursor(0, 3);
if (stepStartTime > 0) {
lcd.print("Sure: ");
lcd.print(stepElapsedTime);
lcd.print("/");
lcd.print(profile[currentStep].duration);
lcd.print("s");
} else {
lcd.print("Hedefe Ulasilmadi");
}
}
lastDisplayedTemp = input;
lastDisplayedStep = currentStep;
lastDisplayedTime = stepElapsedTime;
lastDisplayedTarget = profile[currentStep].temperature;
lastState = currentState;
screenNeedsUpdate = false;
}
void displaySettingScreen() {
if (!screenNeedsUpdate && currentState == lastState) {
return;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ADIM ");
lcd.print(settingStep + 1);
lcd.print(" AYARLARI");
lcd.setCursor(0, 1);
if (settingTemperature) {
lcd.print("> Sicaklik: ");
lcd.print(profile[settingStep].temperature);
lcd.print("C");
} else {
lcd.print(" Sicaklik: ");
lcd.print(profile[settingStep].temperature);
lcd.print("C");
}
lcd.setCursor(0, 2);
if (!settingTemperature) {
lcd.print("> Sure: ");
lcd.print(profile[settingStep].duration);
lcd.print("s");
} else {
lcd.print(" Sure: ");
lcd.print(profile[settingStep].duration);
lcd.print("s");
}
lcd.setCursor(0, 3);
lcd.print("AYAR: Sonraki");
lastState = currentState;
screenNeedsUpdate = false;
}
void displayTooHotWarning() {
if (!screenNeedsUpdate && currentState == lastState) {
return;
}
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(" COK SICAK!");
lcd.setCursor(0, 2);
lcd.print(" Sicaklik: ");
lcd.print(input, 1);
lcd.print("C");
lastState = currentState;
screenNeedsUpdate = false;
}
void displayThermocoupleError() {
if (!screenNeedsUpdate && currentState == lastState) {
return;
}
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("THERMOCOUPLE ARIZASI");
lcd.setCursor(0, 2);
lcd.print("Baglanti Kontrol Et");
lastState = currentState;
screenNeedsUpdate = false;
}
void saveProfileToEEPROM() {
for (int i = 0; i < 10; i++) {
int addr = EEPROM_BASE_ADDR + (i * 4);
EEPROM.put(addr, profile[i].temperature);
EEPROM.put(addr + 2, profile[i].duration);
}
}
void loadProfileFromEEPROM() {
for (int i = 0; i < 10; i++) {
int addr = EEPROM_BASE_ADDR + (i * 4);
EEPROM.get(addr, profile[i].temperature);
EEPROM.get(addr + 2, profile[i].duration);
// Varsayılan değerler (EEPROM boşsa)
if (profile[i].temperature < 0 || profile[i].temperature > 300) {
profile[i].temperature = 25;
}
if (profile[i].duration < 0 || profile[i].duration > 9999) {
profile[i].duration = 60;
}
}
}