#include <Arduino.h>
#include <EEPROM.h> // کتابخانه EEPROM
// ——————————————— تعریف پینها ———————————————
constexpr int BUTTON_PINS[] = {2, 3}; // 0: START_BTN, 1: STOP_BTN
constexpr int VALVE_PIN = 4;
constexpr int DRIVE_PIN = 5;
constexpr int SENSOR_PIN = 6;
constexpr int CUTTER_PIN = 7;
constexpr int STATUS_LED = 8;
constexpr int ALARM_PIN = 9;
constexpr int PULSE_SENSOR_PIN = 10;
// ——————————————— تنظیمات زمانی ———————————————
constexpr unsigned long SENSOR_DELAY = 10000;
constexpr unsigned long CUTTER_TIME = 5000;
constexpr unsigned long DEBOUNCE_DELAY = 50;
constexpr unsigned long RESET_HOLD_TIME = 5000;
constexpr unsigned long RESET_BLINK_TIME = 4000;
constexpr unsigned long DAF_CUTTER_TIME = 4000; // زمان فعال بودن کاتر توی DAF (4 ثانیه)
constexpr unsigned long PULSE_INACTIVITY_TIMEOUT = 8000; // زمان غیرفعال بودن پالس (8 ثانیه)
// ——————————————— حالتهای سیستم ———————————————
enum class SystemState {
IDLE,
RUNNING,
EMERGENCY,
DAF
};
SystemState currentState = SystemState::IDLE;
unsigned long systemStartTime = 0;
unsigned long emergencyStartTime = 0;
bool sensorActive = false;
unsigned long resetPressStart = 0;
bool resetTriggered = false;
bool resetConfirmed = false;
unsigned long resetBlinkStart = 0;
bool cutterActive = false;
bool emergencyDueToDriveAlarm = false;
// متغیرها برای سنسور نوری و متراژ
unsigned long pulseCount = 0;
float meterage = 0.0;
bool lastPulseState = HIGH;
unsigned long lastPulseTime = 0; // زمان آخرین پالس سنسور نوری، مقدار اولیه صفر
// متغیرها برای Raising Edge شستیها
bool lastStartState = HIGH;
bool lastStopState = HIGH;
unsigned long startPressTime = 0;
unsigned long stopPressTime = 0;
// متغیر برای زمانبندی کاتر توی DAF
unsigned long dafCutterStartTime = 0;
// آدرس ذخیرهسازی توی EEPROM
constexpr int PULSE_COUNT_EEPROM_ADDR = 0; // آدرس برای ذخیره pulseCount
// ——————————————— راهاندازی اولیه ———————————————
void setup() {
Serial.begin(9600);
for (int pin : BUTTON_PINS) pinMode(pin, INPUT_PULLUP);
pinMode(VALVE_PIN, OUTPUT);
pinMode(DRIVE_PIN, OUTPUT);
pinMode(SENSOR_PIN, INPUT_PULLUP);
pinMode(CUTTER_PIN, OUTPUT);
pinMode(STATUS_LED, OUTPUT);
pinMode(ALARM_PIN, INPUT_PULLUP);
pinMode(PULSE_SENSOR_PIN, INPUT_PULLUP);
// خواندن pulseCount از EEPROM توی setup
unsigned long tempPulseCount;
EEPROM.get(PULSE_COUNT_EEPROM_ADDR, tempPulseCount);
if (tempPulseCount == 0xFFFFFFFF) {
pulseCount = 0; // اگر مقدار EEPROM خراب باشه، صفر کن
} else {
pulseCount = tempPulseCount;
}
setSystemState(SystemState::IDLE);
lastPulseTime = 0; // تنظیم lastPulseTime به صفر توی setup
}
// ——————————————— حلقه اصلی ———————————————
void loop() {
unsigned long currentMillis = millis();
// خواندن وضعیت فعلی دکمهها
bool currentStartState = digitalRead(BUTTON_PINS[0]);
bool currentStopState = digitalRead(BUTTON_PINS[1]);
// دکمه استوپ با Raising Edge و شرط 50ms (اولویت استوپ)
static unsigned long lastStopDebounceTime = 0;
if (currentStopState != lastStopState) {
lastStopDebounceTime = millis();
if (currentStopState == LOW) {
// Serial.println("STOP button pressed"); // حذف پرینت غیرضروری
stopPressTime = millis();
} else {
// Serial.println("STOP button released"); // حذف پرینت غیرضروری
if (millis() - stopPressTime >= DEBOUNCE_DELAY) {
if (currentState == SystemState::RUNNING) {
// Serial.println("STOP triggered: Moving to IDLE"); // حذف پرینت غیرضروری
stopSystem();
} else if (currentState == SystemState::EMERGENCY && (currentMillis - emergencyStartTime >= CUTTER_TIME)) {
if (digitalRead(ALARM_PIN) == HIGH) {
// Serial.println("STOP triggered: Moving to IDLE from EMERGENCY"); // حذف پرینت غیرضروری
stopSystem();
} else if (emergencyDueToDriveAlarm) {
// Serial.println("STOP ignored: Drive alarm still active"); // حذف پرینت غیرضروری
} else {
// Serial.println("STOP triggered: Moving to IDLE from EMERGENCY (Sensor cleared)"); // حذف پرینت غیرضروری
stopSystem();
}
} else if (currentState == SystemState::EMERGENCY) {
// Serial.println("STOP ignored: Cutter still active"); // حذف پرینت غیرضروری
} else if (currentState == SystemState::DAF) {
// Serial.println("STOP ignored: In DAF state, use reset combination"); // حذف پرینت غیرضروری
} else {
// Serial.println("STOP ignored: Already in IDLE"); // حذف پرینت غیرضروری
}
} else {
// Serial.println("STOP ignored: Not held for 50ms"); // حذف پرینت غیرضروری
}
}
}
// دکمه استارت با Raising Edge و شرط 50ms
static unsigned long lastStartDebounceTime = 0;
if (currentStopState == HIGH && currentStartState != lastStartState && !resetConfirmed) {
lastStartDebounceTime = millis();
if (currentStartState == LOW) {
// Serial.println("START button pressed"); // حذف پرینت غیرضروری
startPressTime = millis();
} else {
// Serial.println("START button released"); // حذف پرینت غیرضروری
if (millis() - startPressTime >= DEBOUNCE_DELAY && currentState == SystemState::IDLE) {
// Serial.println("START triggered (released after 50ms)"); // حذف پرینت غیرضروری
startSystem();
} else if (currentState != SystemState::IDLE) {
// Serial.println("START ignored: Not in IDLE state"); // حذف پرینت غیرضروری
} else {
// Serial.println("START ignored: Not held for 50ms"); // حذف پرینت غیرضروری
}
}
}
// بهروزرسانی وضعیت قبلی
lastStartState = currentStartState;
lastStopState = currentStopState;
handleResetLogic(currentMillis);
handleSystemState(currentMillis);
handleResetBlink(currentMillis);
}
// ——————————————— مدیریت حالتها ———————————————
void handleSystemState(unsigned long currentMillis) {
switch (currentState) {
case SystemState::RUNNING:
handleRunningState(currentMillis);
break;
case SystemState::EMERGENCY:
handleEmergencyState(currentMillis);
break;
case SystemState::IDLE:
digitalWrite(STATUS_LED, HIGH);
break;
case SystemState::DAF:
digitalWrite(STATUS_LED, (currentMillis % 1000 < 500) ? HIGH : LOW); // چشمک زدن STATUS_LED
handleDafState(currentMillis); // مدیریت کاتر توی DAF
break;
}
}
// ——————————————— تنظیم وضعیت سیستم ———————————————
void setSystemState(SystemState newState) {
currentState = newState;
digitalWrite(VALVE_PIN, (newState == SystemState::RUNNING));
digitalWrite(DRIVE_PIN, (newState == SystemState::RUNNING));
if (newState == SystemState::EMERGENCY || newState == SystemState::DAF) {
cutterActive = true;
dafCutterStartTime = millis(); // زمان شروع کاتر توی DAF
// Serial.println("Cutter activated"); // حذف پرینت غیرضروری
} else {
cutterActive = false;
// Serial.println("Cutter deactivated"); // حذف پرینت غیرضروری
emergencyDueToDriveAlarm = false;
}
digitalWrite(CUTTER_PIN, cutterActive);
digitalWrite(STATUS_LED, (newState == SystemState::IDLE));
// Serial.print("State changed to: "); // حذف پرینت غیرضروری
// switch (newState) {
// case SystemState::IDLE: Serial.println("IDLE"); break;
// case SystemState::RUNNING: Serial.println("RUNNING"); break;
// case SystemState::EMERGENCY: Serial.println("EMERGENCY"); break;
// case SystemState::DAF: Serial.println("DAF"); break;
// }
}
// ——————————————— مدیریت استارت و استوپ ———————————————
void startSystem() {
systemStartTime = millis();
sensorActive = false;
lastPulseTime = millis(); // تنظیم lastPulseTime به زمان فعلی وقتی استارت میزنیم
setSystemState(SystemState::RUNNING);
// Serial.println("System started, sensors reset"); // حذف پرینت غیرضروری
}
void stopSystem() {
setSystemState(SystemState::IDLE);
sensorActive = false;
lastPulseTime = 0; // ریست lastPulseTime به صفر وقتی استوپ میزنیم
}
void emergencyStop(bool dueToDriveAlarm) {
emergencyStartTime = millis();
emergencyDueToDriveAlarm = dueToDriveAlarm;
setSystemState(SystemState::EMERGENCY);
}
// ——————————————— مدیریت سنسور و قطعکن اضطراری ———————————————
void handleRunningState(unsigned long currentMillis) {
if (!sensorActive) {
unsigned long timeSinceStart = currentMillis - systemStartTime;
// if (timeSinceStart > 0) { // حذف پرینت غیرضروری
// Serial.print("Time since start: ");
// Serial.println(timeSinceStart);
// }
}
if (!sensorActive && (currentMillis - systemStartTime >= SENSOR_DELAY)) {
sensorActive = true;
// Serial.println("Sensor activated after 10 seconds"); // حذف پرینت غیرضروری
}
if (sensorActive) {
bool sensorState = digitalRead(SENSOR_PIN);
// Serial.print("Sensor state: "); // حذف پرینت غیرضروری
// Serial.println(sensorState == HIGH ? "HIGH (Detected)" : "LOW (Not detected)");
if (sensorState == HIGH) {
Serial.println("Alarm: Sensor triggered!");
emergencyStop(false);
}
}
if (digitalRead(ALARM_PIN) == LOW) {
Serial.println("Alarm: Drive alarm activated!");
emergencyStop(true);
}
bool currentPulseState = digitalRead(PULSE_SENSOR_PIN);
if (currentPulseState != lastPulseState && currentPulseState == LOW) {
lastPulseTime = currentMillis; // بهروزرسانی زمان آخرین پالس
pulseCount++;
meterage = pulseCount / 20.0;
// Serial.print("Pulse count: "); // حذف پرینت غیرضروری
// Serial.print(pulseCount);
// Serial.print(", Meterage: "); // حذف پرینت غیرضروری
// Serial.println(meterage);
// ذخیره pulseCount توی EEPROM هر 100,000 پالس
static unsigned long lastSavedPulseCount = 0;
if (pulseCount % 100000 == 0 && pulseCount != lastSavedPulseCount) {
EEPROM.put(PULSE_COUNT_EEPROM_ADDR, pulseCount);
lastSavedPulseCount = pulseCount;
}
if (meterage >= 25000) {
// Serial.println("Meterage reached 25000, moving to DAF"); // حذف پرینت غیرضروری
setSystemState(SystemState::DAF);
}
}
lastPulseState = currentPulseState;
// دیباگ برای بررسی زمانبندی (حذف پرینت غیرضروری)
// Serial.print("Current millis: ");
// Serial.print(currentMillis);
// Serial.print(", Last pulse time: ");
// Serial.println(lastPulseTime);
// چک کردن غیرفعال بودن پالس به مدت 8 ثانیه (برو به EMERGENCY) فقط اگر lastPulseTime صفر نیست
if (currentMillis > lastPulseTime && (currentMillis - lastPulseTime >= PULSE_INACTIVITY_TIMEOUT)) {
Serial.println("Alarm: Pulse sensor inactive for 8 seconds, moving to EMERGENCY");
emergencyStop(true); // استفاده از true برای مشخص کردن دلیل اضطراری
}
}
void handleEmergencyState(unsigned long currentMillis) {
digitalWrite(STATUS_LED, (currentMillis % 500 < 250) ? HIGH : LOW);
if (cutterActive && (currentMillis - emergencyStartTime >= CUTTER_TIME)) {
cutterActive = false;
digitalWrite(CUTTER_PIN, LOW);
// Serial.println("Cutter deactivated after 5 seconds"); // حذف پرینت غیرضروری
}
}
// ——————————————— مدیریت کاتر توی حالت DAF ———————————————
void handleDafState(unsigned long currentMillis) {
if (cutterActive && (currentMillis - dafCutterStartTime >= DAF_CUTTER_TIME)) {
cutterActive = false;
digitalWrite(CUTTER_PIN, LOW);
// Serial.println("Cutter deactivated in DAF after 4 seconds"); // حذف پرینت غیرضروری
}
}
// ——————————————— مدیریت ریست ترکیبی ———————————————
void handleResetLogic(unsigned long currentMillis) {
if ((currentState == SystemState::IDLE || currentState == SystemState::EMERGENCY || currentState == SystemState::DAF) && !resetTriggered) {
if (digitalRead(BUTTON_PINS[0]) == LOW && digitalRead(BUTTON_PINS[1]) == LOW) {
if (resetPressStart == 0) {
resetPressStart = currentMillis;
// Serial.println("Reset combination started"); // حذف پرینت غیرضروری
} else if (currentMillis - resetPressStart >= RESET_HOLD_TIME) {
// Serial.println("Reset triggered"); // حذف پرینت غیرضروری
resetTriggered = true;
resetConfirmed = true;
resetBlinkStart = currentMillis;
}
} else {
resetPressStart = 0;
}
} else {
resetPressStart = 0;
}
if (resetTriggered && (digitalRead(BUTTON_PINS[0]) == HIGH || digitalRead(BUTTON_PINS[1]) == HIGH)) {
resetTriggered = false;
}
}
// ——————————————— مدیریت چشمک زدن پس از ریست ———————————————
void handleResetBlink(unsigned long currentMillis) {
if (resetConfirmed) {
if (currentMillis - resetBlinkStart < RESET_BLINK_TIME) {
digitalWrite(STATUS_LED, LOW);
} else {
resetSystem();
digitalWrite(STATUS_LED, HIGH);
resetConfirmed = false;
// Serial.println("Reset blink completed, system in IDLE"); // حذف پرینت غیرضروری
}
}
}
// ——————————————— تابع ریست ———————————————
void resetSystem() {
setSystemState(SystemState::IDLE);
systemStartTime = 0;
emergencyStartTime = 0;
sensorActive = false;
pulseCount = 0; // صفر کردن pulseCount با ریست ترکیبی
meterage = 0.0;
lastPulseTime = 0; // ریست زمان آخرین پالس
// ذخیره pulseCount صفر توی EEPROM با استفاده از EEPROM.put
EEPROM.put(PULSE_COUNT_EEPROM_ADDR, pulseCount);
}