CCCPaste

Dual-Mode Control rev_01

/********* Pleasedontcode.com **********

	Pleasedontcode thanks you for automatic code generation! Enjoy your code!

	- Terms and Conditions:
	You have a non-exclusive, revocable, worldwide, royalty-free license
	for personal and commercial use. Attribution is optional; modifications
	are allowed, but you're responsible for code maintenance. We're not
	liable for any loss or damage. For full terms,
	please visit pleasedontcode.com/termsandconditions.

	- Project: Dual-Mode Control
	- Source Code NOT compiled for: Arduino Mega
	- Source Code created on: 2025-09-27 14:44:33

********* Pleasedontcode.com **********/

/****** SYSTEM REQUIREMENTS *****/
/****** SYSTEM REQUIREMENT 1 *****/
	/* help refine the code. want to run the code */
	/* smoothly both in auto mode and manual mode. */
/****** END SYSTEM REQUIREMENTS *****/


/* START CODE */

/****** DEFINITION OF LIBRARIES *****/

/****** FUNCTION PROTOTYPES *****/
void setup(void);
void loop(void);

 // Merged content from USER CODE, adapted to fit the PRELIMINARY CODE structure for Arduino Mega

//Last Updated 12092025
// Define stepper motor1 of Linear guide @ Spinning Motor driver pins
const int DRIVER1_DIR = 28; // Direction pin for stepper motor1
const int DRIVER1_STEP = 18; // Step pin for stepper motor1
const int DRIVER1_ENA = 29; // Enable pin for stepper motor1

// Define stepper motor2 of Linear guide @ Feeder Motor driver pins
const int DRIVER2_DIR = 30; // Direction pin for stepper motor2
const int DRIVER2_STEP = 6; // Step pin for stepper motor2
const int DRIVER2_ENA = 31; // Enable pin for stepper motor2

// Proximity Sensor for Induction Motor CW and CCW
const int PROXY_POS = 10; // Proxy POS Sensor for Linear Motor Position
const int PROXY_A = 11; // Proxy A Sensor for CW
const int PROXY_B = 12; // Proxy B Sensor for CCW
// const int PROXY_RUNFEED = 13; // Proxy Feed Run Sensor

// Define BTS7960B motor driver pins for Feeder motor
const int EN_FEEDER = 20; // Enable pin for feeder motor
const int LPWM_FEEDER = 3; // Left PWM pin for feeder motor
const int RPWM_FEEDER = 4; // Right PWM pin for feeder motor

// Define Limit switchs of Linear drive @ Spinning motor
const int LIMIT_SWITCH1 = 15; // 32; // Limit switch 1 pin (Bottom)
const int LIMIT_SWITCH2 = 33; // Limit switch 2 pin (Up)
// Define Limit switchs of Linear drive @ Feeder motor
const int LIMIT_SWITCH3 = 35; // Limit switch 3 pin (Reverse)
const int LIMIT_SWITCH4 = 16; // Limit switch 4 pin (Forward)

// Define relay pins
const int RELAY1 = A0; // Relay for 
const int RELAY2 = A1; // Relay for Air-Nipper Cutter
const int RELAY3 = A12; // Relay for Gripper 1
const int RELAY4 = A3; // Relay for Gripper 2
const int RELAY5 = A4; // Relay for CW direction
const int RELAY6 = A5; // Relay for CCW direction
const int RELAY7 = A6; // Relay for Feeder Linear Drive
const int RELAY8 = A7; // Relay for Label Vaccum pad Forward/Backward
const int RELAY9 = 43; // Relay for Spinning Linear Drive
const int RELAY10 = 45; // Relay for Cutter-up & Cutter-down Cylinder
const int RELAY11 = 46; // Relay for
const int RELAY12 = 47; // Relay for
const int RELAY13 = 48; // Relay for
const int RELAY14 = 49; // Relay for
const int RELAY15 = 50; // Relay for
const int RELAY16 = 51; // Relay for

// Flags and variables
bool autoMode = false; // Flag for Auto Mode
bool manualMode = false; // Flag for Manual Mode
int numCycles = 0; // Variable to store number of cycles
bool feederRunning = false; // Flag to indicate if the feeder is running
bool isForward = false;
int targetCounts = 0; // Stores the required counts for current operation
volatile int countA = 0; // Count for Proxy A
volatile int countB = 0; // Count for Proxy B
bool motorRunning = false; // Flag to indicate if the motor is currently running
bool countingEnabled = false; // Flag to enable counting based on relay state
bool isClockwise = false; // Global variable to track motor direction
bool countProxyAEnabled = true; // Flag to enable/disable counting for Proxy A
bool countProxyBEnabled = true; // Flag to enable/disable counting for Proxy B
bool countProxyPOSEnabled = true; // Flag to enable/disable counting for Proxy POS
volatile bool isMoving1 = false; // Flag to indicate if the linear guide is currently moving
volatile bool isMoving2 = false; // Flag to indicate if the linear guide is currently moving
unsigned long feederStartTime = 0;  // When feeder started
unsigned long feederDuration = 0;   // How long to run (ms)
bool linearGuide1Complete = false;
bool linearGuide2Complete = false;
bool feederComplete = false;
bool motorComplete = false;

// Command enumeration
enum Command { START, STOP, G1, G2, G3, G4, VC, VO, VF, VR, PU, PD, OG, CW, CCW, LU, LD, LF, LR, CU, CD, CX, X, FD, RD, AUTO, MANUAL, NONE, UNKNOWN };

// Debounce variables
const unsigned long DEBOUNCE_TIME = 1000; // 1 second debounce time
unsigned long lastCommandTime = 0; // Variable to store the last command execution time

// Auto mode state machine variables
enum AutoState { AUTO_IDLE, AUTO_CHECK_POS, AUTO_LU, AUTO_LF, AUTO_VF, AUTO_FD, AUTO_G3, AUTO_LR, AUTO_G1, AUTO_CU, AUTO_CX, AUTO_CD, AUTO_LD, AUTO_VR, AUTO_CW, AUTO_CCW, AUTO_OG, AUTO_COMPLETE, AUTO_CW_RUNNING, AUTO_CCW_RUNNING, AUTO_STOPPED };
AutoState autoState = AUTO_IDLE;
int currentCycle = 0;
unsigned long autoStepStartTime = 0;
const unsigned long STEP_DELAY = 100; // 100ms delay between steps

// Completion check functions
bool isLinearGuide1Complete() { return linearGuide1Complete; }
bool isLinearGuide2Complete() { return linearGuide2Complete; }
bool isFeederComplete() { return feederComplete; }
bool isMotorComplete() { return motorComplete; }

// Function declarations
void stopMotor(); // To Stop Linear Motor, Feeder Motor, Spinning Motor
void moveLinearGuide1(String direction); // To move Linear UP and Down
void moveLinearGuide2(String direction); // To move Linear Right and Left
void moveStepper1(bool direction, int stopPin); // To Execute Linear & Spinning in sequence
void moveStepper2(bool direction, int stopPin); // To Execute Linear & Feeder in sequence 
void runFeeder(bool forward, unsigned long durationMillis = 0, int speed = 25); // To activte Feeder motor
void setFeederMotorSpeed(int speed, bool forward);
void runOpenGripper(); // To deactivate Gripper Relay 3 & 4
void runMotor(bool clockwise); // Combined CW and CCW motor function
void handleCommand(Command cmd); // To handle and execute Manual Input Command
bool runAutoMode(int numCycles); // To execute Auto Input Command sequence
void resetSerialBuffer(); // To reset Command line
bool checkStopCommand(); // To check for STOP command during process
void resetMode();
void CutterSensor();
void proxyA();
void proxyB();

// Subsystem wrappers to isolate auto/manual control (new)
void Subsystem_AutoStart(int cycles) { // Initialize and start Auto mode for a fixed cycle count
  numCycles = cycles;
  autoMode = true;
  autoState = AUTO_IDLE;
  currentCycle = 0;
  Serial.print("Subsystem Auto Start: "); Serial.println(cycles);
}
void Subsystem_AutoTick() { updateAutoMode(); }
void Subsystem_AutoStop() { stopAutoMode(); }
void Subsystem_ManualActivate() { manualMode = true; resetMode(); Serial.println("Subsystem Manual Mode Activated"); }
void Subsystem_ManualDeactivate() { manualMode = false; Serial.println("Subsystem Manual Mode Deactivated"); }
bool Subsystem_AutoRunning() { return autoMode; }

Command parseCommand(String command);
Command currentMode = NONE;
// Helper functions to set completion flags
void setLinearGuide1Complete() {
 linearGuide1Complete = true;
 Serial.println("Linear Guide 1 operation complete");
}
void setLinearGuide2Complete() {
 linearGuide2Complete = true;
 Serial.println("Linear Guide 2 operation complete");
}
void setFeederComplete() {
 feederComplete = true;
 Serial.println("Feeder operation complete");
}
void setMotorComplete() {
 motorComplete = true;
 Serial.println("Motor operation complete");
}
void setup() {
 // Set pin modes for motors and relays
 pinMode(RPWM_FEEDER, OUTPUT);
 pinMode(LPWM_FEEDER, OUTPUT);
 pinMode(EN_FEEDER, OUTPUT);
 pinMode(PROXY_POS, INPUT_PULLUP); // Set proxy POS as input
 pinMode(PROXY_A, INPUT_PULLUP); // Set proxy A as input
 pinMode(PROXY_B, INPUT_PULLUP); // Set proxy B as input
 // pinMode(PROXY_RUNFEED, INPUT); // Set proxy Feed Run as input
 pinMode(RELAY1, OUTPUT);
 pinMode(RELAY2, OUTPUT);
 pinMode(RELAY3, OUTPUT);
 pinMode(RELAY4, OUTPUT);
 pinMode(RELAY5, OUTPUT);
 pinMode(RELAY6, OUTPUT);
 pinMode(RELAY7, OUTPUT);
 pinMode(RELAY8, OUTPUT);
 pinMode(RELAY9, OUTPUT);
 pinMode(RELAY10, OUTPUT);
 pinMode(RELAY11, OUTPUT);
 pinMode(RELAY12, OUTPUT);
 pinMode(RELAY13, OUTPUT);
 pinMode(RELAY14, OUTPUT);
 pinMode(RELAY15, OUTPUT);
 pinMode(RELAY16, OUTPUT);
 pinMode(LIMIT_SWITCH1, INPUT_PULLUP);
 pinMode(LIMIT_SWITCH2, INPUT_PULLUP);
 pinMode(LIMIT_SWITCH3, INPUT_PULLUP);
 pinMode(LIMIT_SWITCH4, INPUT_PULLUP);
 pinMode(DRIVER1_STEP, OUTPUT);
 pinMode(DRIVER1_DIR, OUTPUT);
 pinMode(DRIVER1_ENA, OUTPUT);
 pinMode(DRIVER2_STEP, OUTPUT);
 pinMode(DRIVER2_DIR, OUTPUT);
 pinMode(DRIVER2_ENA, OUTPUT);

 // Initialize relays to LOW (off state)
 digitalWrite(RELAY1, LOW);
 digitalWrite(RELAY2, LOW);
 digitalWrite(RELAY3, LOW);
 digitalWrite(RELAY4, LOW);
 digitalWrite(RELAY5, LOW);
 digitalWrite(RELAY6, LOW);
 digitalWrite(RELAY7, LOW);
 digitalWrite(RELAY8, LOW);
 digitalWrite(RELAY9, LOW);
 digitalWrite(RELAY10, LOW);
 digitalWrite(RELAY11, LOW);
 digitalWrite(RELAY12, LOW);
 digitalWrite(RELAY13, LOW);
 digitalWrite(RELAY14, LOW);
 digitalWrite(RELAY15, LOW);
 digitalWrite(RELAY16, LOW);
 
 digitalWrite(DRIVER1_ENA, LOW);  // Enable stepper motor1
 digitalWrite(DRIVER2_ENA, LOW);  // Enable stepper motor2
 
 Serial.begin(9600);  // Start serial communication
 resetSerialBuffer();  // Clear serial buffer on startup
}
Command parseCommand(String command) {
 command.trim();
 // Parse incoming command strings and return corresponding Command enum
 if (command == "START") return START;
 if (command == "STOP") return STOP;
 if (command == "G1") return G1;
 if (command == "G2") return G2;
 if (command == "G3") return G3;
 if (command == "G4") return G4;
 if (command == "VC") return VC;
 if (command == "VO") return VO;
 if (command == "VF") return VF;
 if (command == "VR") return VR;
 if (command == "PU") return PU;
 if (command == "PD") return PD;
 if (command == "OG") return OG;
 if (command == "CW") return CW;
 if (command == "CCW") return CCW;
 if (command == "LU") return LU;
 if (command == "LD") return LD;
 if (command == "LF") return LF;
 if (command == "LR") return LR;
 if (command == "CU") return CU;
 if (command == "CD") return CD;
 if (command == "CX") return CX; 
 if (command == "X") return X;
 if (command == "FD") return FD; // Forward command
 if (command == "RD") return RD; // Reverse command
 if (command == "AUTO") return AUTO; // Auto mode
 if (command == "MANUAL") return MANUAL; // Manual mode
 return UNKNOWN; // Return UNKNOWN if command is not recognized
}
void handleCommand(Command cmd) {
 // Handle commands based on the parsed Command enum
 switch (cmd) {
   case START:
     Serial.println("Process started.");
   break;
   case STOP:
     stopMotor();
     Serial.println("MOTORS STOPPED");
   break;
   case G1:
     digitalWrite(RELAY3, HIGH); // Activate RELAY3 for G1 Close
     Serial.println("RELAY3 ON");
   break;
   case G2:
     digitalWrite(RELAY3, LOW); // Deactivate RELAY3 for G2 Open
     Serial.println("RELAY3 OFF");
   break;
   case G3:
     digitalWrite(RELAY4, HIGH); // Activate RELAY4 for G2 Close
     Serial.println("RELAY4 ON");
   break;
   case G4:
     digitalWrite(RELAY4, LOW); // Deactivate RELAY4 for G2 Open
     Serial.println("RELAY4 OFF");
   break;
   case VC: // Removed as on 28082025
     digitalWrite(RELAY11, HIGH); // Activate RELAY10 for Vaccum pad Pick
     Serial.println("RELAY11 ON");
   break;
   case VO: // Removed as on 28082025
     digitalWrite(RELAY11, LOW); // Deactivate RELAY10 for Vaccum pad Eject
     Serial.println("RELAY11 OFF");
   break;
   case VF:
     digitalWrite(RELAY8, HIGH); // Activate RELAY8 for Vaccum pad Forward
     Serial.println("RELAY8 ON");
   break;
   case VR:
     digitalWrite(RELAY8, LOW); // Deactivate RELAY8 for Vaccum pad Reverse
     Serial.println("RELAY8 OFF");
   break;
   case PU:
     digitalWrite(RELAY11, HIGH); // Activate RELAY11 for Push Up Load
     Serial.println("RELAY11 ON");
   break;
   case PD:
     digitalWrite(RELAY11, LOW); // Deactivate RELAY11 for Push Down Load
     Serial.println("RELAY11 OFF");
   break;
   case OG:
     runOpenGripper();
     Serial.println("Gripper 1 & 2 OPEN...");
   break;
   case CW:
     runMotor(true);
     Serial.println("Starting CW motor...");
   break;
   case CCW:
     runMotor(false);
     Serial.println("Starting CCW motor...");
   break;
   case LU:
     moveLinearGuide1("LU");
     Serial.println("Linear Guide1 Moving Up");
   break;
   case LD:
     moveLinearGuide1("LD");
     Serial.println("Linear Guide1 Moving Down");
   break;
   case LF:
     moveLinearGuide2("LF");
     Serial.println("Linear Guide2 Moving Forward");
   break;
   case LR:
     moveLinearGuide2("LR");
     Serial.println("Linear Guide2 Moving Reverse");
   break;
   case CX:
     digitalWrite(RELAY2, HIGH);
     delay(1000);
     digitalWrite(RELAY2, LOW);
     Serial.println("Air-Nipper Cutter Operated.");
   break;
   case CU:
     digitalWrite(RELAY10, HIGH); // Activate RELAY10 for Cutter Up Load
     Serial.println("RELAY10 ON");
   break;
   case CD:
     digitalWrite(RELAY10, LOW); // Deactivate RELAY10 for Cutter Down Load
     Serial.println("RELAY10 OFF");
   break;
   case X:
     stopMotor();
     Serial.println(" All motors stopped.");
   break;
   case FD:
     runFeeder(true, 2150); // Move feeder forward
     Serial.println("Feeder moving forward.");
   break;
   case RD:
     runFeeder(false, 2150); // Move feeder reverse
     Serial.println("Feeder moving reverse.");
   break;
   case AUTO:
   if (!autoMode) {  // Only prompt if not already in Auto mode
     resetMode();  // Reset mode before starting Auto Mode
     autoMode = true;  // Set Auto mode flag
     autoState = AUTO_IDLE;
     currentCycle = 0;
     Serial.println("Auto mode activated.");
     // delay(2000);
     Serial.println("Please enter the number of cycles:");

     unsigned long startWaitTime = millis();
     while (millis() - startWaitTime < 10000) {
       if (Serial.available() > 0) {
         numCycles = Serial.parseInt();
         if (numCycles > 0) { 
           Serial.print("Starting Auto Mode for " );
           Serial.print(numCycles);
           Serial.println(" cycles.");
           break;
          } else {
           Serial.println("Invalid input. Please entre a positive number:");
          }
        }
      }
      if (numCycles == 0) {
       Serial.println("Timeout! Auto mode cancelled.");
       autoMode = false;        
      }
    }
   break;
   case MANUAL:
   if (!manualMode) {  // Only prompt if not already in Manual mode
     resetMode();  // Reset mode before starting Manual Mode
     manualMode = true;  // Set Manual mode flag
     Serial.println("Manual mode activated.");
    }
   break;
   default:
     Serial.println("UNKNOWN COMMAND");
   break;
  }
}
void loop() { 
 // Process serial commands non-blockingly
 if (Serial.available() > 0) {
   String command = Serial.readStringUntil('\n');
   command.trim(); // Remove any trailing spaces or newlines
   Serial.flush();   // Clear buffer after reading
   Serial.println("Received: " + command);  // Debugging
      
   // Check if enough time has passed since the last command
   if (millis() - lastCommandTime >= DEBOUNCE_TIME) {
     Command cmd = parseCommand(command);
     lastCommandTime = millis();

     if (cmd == STOP || cmd == X) {
       // Handle stop command immediately
       if (autoMode) {
         stopAutoMode();
        }
       handleCommand(cmd);
      } else if (cmd == AUTO) {
        handleCommand(cmd);
      } else if (cmd == MANUAL) {
        handleCommand(cmd);
      } else {
       // Handle other commands (START, STOP, Q, etc.)
        handleCommand(cmd);
      }
      // Clear serial buffer after mode changes or commands
      resetSerialBuffer();
    }
  }
 // If in Auto mode, run the auto sequence incrementally
 if (autoMode) {
   updateAutoMode();
  }
  // State monitoring (no more proxy function calls)
 updateFeeder();

 if (motorRunning) {
   if (isClockwise) { // Check if the motor is running CW
     proxyA(); // Check Proxy A
    } 
   else { // Otherwise, it must be CCW
     proxyB(); // Check Proxy B
    }
  }
}
void updateAutoMode() {
 static unsigned long lastUpdateTime = 0;
 static bool firstTimeInState = true;
 unsigned long currentTime = millis();
  
 // Update at 100ms intervals for responsiveness
 if (currentTime - lastUpdateTime < 100) {
   return;
  }
 lastUpdateTime = currentTime;

 // Check for stop commands continuously
 if (checkStopCommand()) {
   stopAutoMode();
   return;
 }
 switch (autoState) {
   case AUTO_IDLE:
     if (firstTimeInState) {
       Serial.print("AUTO_IDLE - Starting cycle ");
       Serial.println(currentCycle + 1);
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     if (currentTime - autoStepStartTime > 500) {
       if (currentCycle < numCycles) {
         currentCycle++;
         Serial.print("Starting cycle ");
         Serial.println(currentCycle);
         autoState = AUTO_CHECK_POS;
         firstTimeInState = true;
         autoStepStartTime = currentTime;       
        } else {
         autoState = AUTO_COMPLETE;
        }
      }
   break;
   case AUTO_CHECK_POS:
     if (firstTimeInState) {
       int sensorState = digitalRead(PROXY_POS);
       Serial.print("Proxy POS Sensor State: ");
       Serial.println(sensorState);
        
       if (sensorState == LOW) {
         Serial.println("Proxy POS not active! Cannot proceed.");
         autoState = AUTO_STOPPED;
        } else {
         Serial.println("Proxy POS active. Proceeding to LU...");
         autoState = AUTO_LU;
         firstTimeInState = true;
        }
       autoStepStartTime = currentTime;
      }
   break;
   case AUTO_LU:
     if (firstTimeInState) {
       linearGuide1Complete = false;
       moveLinearGuide1("LU");
       Serial.println("Executing LU command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     if (isLinearGuide1Complete()) {
       autoState = AUTO_LF;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
       break;
      }
     // Timeout safety
     if (currentTime - autoStepStartTime > 15000) {
       Serial.println("LU operation timeout!");
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_LF:
     if (firstTimeInState) {
       linearGuide2Complete = false;
       moveLinearGuide2("LF");
       Serial.println("Executing LF command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
       delay(1000);
      }
     if (isLinearGuide2Complete()) {
       autoState = AUTO_VF;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
       break;
      }
     if (currentTime - autoStepStartTime > 15000) {
       Serial.println("LF operation timeout!");
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_VF:
     digitalWrite(RELAY8, HIGH);
     Serial.println("Executing VF command...");
     autoState = AUTO_FD;
     autoStepStartTime = currentTime;
     delay(1000);
   break;
   case AUTO_FD:
     if (firstTimeInState) {
       feederComplete = false;
       runFeeder(true, 2150);
       Serial.println("Executing FD command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     if (isFeederComplete()) {
       autoState = AUTO_G3;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
       break;
      }
     if (currentTime - autoStepStartTime > 5000) {
       Serial.println("FD operation timeout!");
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_G3:
     digitalWrite(RELAY4, HIGH);
     Serial.println("Executing G3 command...");
     autoState = AUTO_LR;
     autoStepStartTime = currentTime;
   break;
   case AUTO_LR:
     if (firstTimeInState) {
       linearGuide2Complete = false;
       moveLinearGuide2("LR");
       Serial.println("Executing LR command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     if (isLinearGuide2Complete()) {
       autoState = AUTO_G1;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
       break;
      }
     if (currentTime - autoStepStartTime > 15000) {
       Serial.println("LR operation timeout!");
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_G1:
     digitalWrite(RELAY3, HIGH);
     Serial.println("Executing G1 command...");
     autoState = AUTO_CU;
     autoStepStartTime = currentTime;
     delay(1000);
   break;
   case AUTO_CU:
     digitalWrite(RELAY10, HIGH);
     Serial.println("Executing CU command...");
     autoState = AUTO_CX;
     autoStepStartTime = currentTime;
     delay(1000);
   break;
   case AUTO_CX:
     digitalWrite(RELAY2, HIGH);
     Serial.println("Executing CX command...");
     autoState = AUTO_CD;
     autoStepStartTime = currentTime;
     delay(1000);
   break;
   case AUTO_CD:
     digitalWrite(RELAY2, LOW);
     digitalWrite(RELAY10, LOW);
     Serial.println("Executing CD command...");
     autoState = AUTO_LD;
     autoStepStartTime = currentTime;
     delay(1000);
   break;
   case AUTO_LD:
     if (firstTimeInState) {
       linearGuide1Complete = false;
       moveLinearGuide1("LD");
       Serial.println("Executing LD command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     if (isLinearGuide1Complete()) {
       autoState = AUTO_CW;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
       break;
      }
     if (currentTime - autoStepStartTime > 15000) {
       Serial.println("LD operation timeout!");
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_CW:
     if (firstTimeInState) {
       motorComplete = false;
       runMotor(true);
       Serial.println("Executing CW command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
       autoState = AUTO_CW_RUNNING;  // Move to intermediate state
      }
   break;
   case AUTO_CW_RUNNING:
     // Check if motor has completed its operation
     if (isMotorComplete()) {
       Serial.println("CW operation completed.");
       autoState = AUTO_VR;  // Move to next main state
       firstTimeInState = true;
      }
     // Timeout safety
     if (currentTime - autoStepStartTime > 30000) {
       Serial.println("CW operation timeout!");
       stopMotor();
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_VR:
     digitalWrite(RELAY8, LOW);
     Serial.println("Executing VR command...");
     autoState = AUTO_OG;
     autoStepStartTime = currentTime;
   break;
   case AUTO_OG:
     if (firstTimeInState) {
       runOpenGripper();
       Serial.println("Executing OG command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
      }
     // Wait 3000ms for OG delay
     if (currentTime - autoStepStartTime >= 3000) {
       autoState = AUTO_CCW;
       firstTimeInState = true;
       autoStepStartTime = currentTime;
      }
   break;
   case AUTO_CCW:
     if (firstTimeInState) {
       motorComplete = false;
       runMotor(false); // Start CCW motor
       Serial.println("Executing CCW command...");
       firstTimeInState = false;
       autoStepStartTime = currentTime;
       autoState = AUTO_CCW_RUNNING;  // Move to intermediate state
      }
   break;
   case AUTO_CCW_RUNNING:
     // Check if motor has completed its operation
     if (isMotorComplete()) {
       Serial.println("CCW operation completed.");
       // Move to whatever state comes after CCW, or back to IDLE
       autoState = AUTO_IDLE; 
       firstTimeInState = true;
      }
     // Timeout safety
     if (currentTime - autoStepStartTime > 30000) {
       Serial.println("CCW operation timeout!");
       stopMotor();
       autoState = AUTO_STOPPED;
      }
   break;
   case AUTO_COMPLETE:
     Serial.println("Auto mode completed all cycles.");
     resetMode();
     autoMode = false;
   break;
   case AUTO_STOPPED:
     // Do nothing until reset
     default:
   break;
  }
}
void stopAutoMode() {
 // Stop all ongoing operations
 stopMotor();
 digitalWrite(RELAY2, LOW);
 digitalWrite(RELAY3, LOW);
 digitalWrite(RELAY4, LOW);
 digitalWrite(RELAY8, LOW);
 digitalWrite(RELAY9, LOW);
 digitalWrite(RELAY10, LOW);
 digitalWrite(DRIVER1_ENA, HIGH);
 digitalWrite(DRIVER2_ENA, HIGH);
 digitalWrite(EN_FEEDER, LOW);
  
 autoState = AUTO_STOPPED;
 Serial.println("Auto mode stopped by command.");
}
bool checkStopCommand() {
 while (Serial.available() > 0) {
   String command = Serial.readStringUntil('\n');
   command.trim();
    
   if (command == "STOP" || command == "X") {
     Serial.println("Stop command received: " + command);
     return true;
    }
   // Put the command back into the buffer for normal processing
   // for (int i = 0; i < command.length(); i++) {
   //   Serial.print(command[i]);
   // }
  }
 return false;
}
void setFeederMotorSpeed(int speed, bool forward) {
 analogWrite(RPWM_FEEDER, forward ? map(speed, 0, 100, 0, 255) : 0);
 analogWrite(LPWM_FEEDER, forward ? 0 : map(speed, 0, 100, 0, 255));
}
void runFeeder(bool forward, unsigned long durationMillis, int speed) {
 feederComplete = false;
 if (feederRunning) {
   Serial.println("Feeder is already running.");
   return; // Return the current direction if already running
  }
 // Start the feeder motor
 setFeederMotorSpeed(speed, forward);
 digitalWrite(EN_FEEDER, HIGH);  // Enable feeder motor 
 delay (100);
 feederRunning = true; // set feeder running flag to true
 isForward = forward;
 // Set duration if provided (0 means run indefinitely)
 if (durationMillis > 0) {
   feederStartTime = millis();
   feederDuration = durationMillis;
  } else {
   feederDuration = 0; // Run until manually stopped
  }
 Serial.print(forward ? "FEEDER MOTOR FD STARTED" : "FEEDER MOTOR RD STARTED");
 if (durationMillis > 0) {
   Serial.print(" for ");
   Serial.print(durationMillis / 1000.0);
   Serial.println(" seconds");
  } else {
   Serial.println(" (indefinite run)");
  }
}
void updateFeeder() {
 if (feederRunning && feederDuration > 0 && (millis() - feederStartTime >= feederDuration)) {
   // Time's up - stop the motor
   digitalWrite(EN_FEEDER, LOW);
   stopMotor();
   feederRunning = false;
   Serial.println("Feeder motor stopped (time elapsed)");
   // SET COMPLETION FLAG HERE
   setFeederComplete();
  }
}

void moveLinearGuide1(String direction1) {
 linearGuide1Complete = false;
 if (direction1 == "LU") {  // Forward Direction (Linear motor up)
   int sensorState = digitalRead(PROXY_POS);
   Serial.print("Proxy POS Sensor State: ");
   Serial.println(sensorState); // Print the actual state of the sensor

   // Check if proxyPOS is active
   if (sensorState == LOW) { // Assuming HIGH means the sensor is inactive
     Serial.println("Cannot move up. Proxy POS is not active.");
     return; // Exit the function if proxyPOS is not active
    }
    // Check if already at upper limit (like LF/LR does)
   if (digitalRead(LIMIT_SWITCH2) == LOW) {
     Serial.println("Already at Limit Switch 2");
     setLinearGuide1Complete(); // ADD THIS - was missing!
     return;
    }
   Serial.println("LU");
   digitalWrite(DRIVER1_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY9, HIGH); // Turn ON Relay 1
   Serial.println("RELAY9 ON"); // Notify Python
   delay(100);
   digitalWrite(DRIVER1_DIR, HIGH); // Set direction forward
   delay(100);
   digitalWrite(DRIVER1_ENA, LOW); // Enable motor
   delay(200);

   Serial.print("Limit States - 1: ");
   Serial.print(digitalRead(LIMIT_SWITCH1));
   Serial.print("  & Limit States - 2: ");
   Serial.println(digitalRead(LIMIT_SWITCH2));

   // Direct stepper movement (similar to LF/LR)
   unsigned long startTime = millis();
   const unsigned long timeout = 15000; // 15 second timeout
    
   while (digitalRead(LIMIT_SWITCH2) == HIGH) {
     digitalWrite(DRIVER1_STEP, HIGH);
     delayMicroseconds(40);
     digitalWrite(DRIVER1_STEP, LOW);
     delayMicroseconds(45);
      
     // Check for timeout
     if (millis() - startTime > timeout) {
       Serial.println("LU movement timeout! Stopping.");
       break;
      }
     if (checkStopCommand()) {
       Serial.println("Emergency Stop Triggered");
       stopMotor();
       digitalWrite(RELAY9, LOW);
       return;
      }
    }
   digitalWrite(RELAY9, LOW);
   Serial.println("RELAY9 OFF");
   Serial.println("Motors stopped at Limit Switch 2 or Emergency Stop.");
   setLinearGuide1Complete();
  }
 else if (direction1 == "LD") {  // Linear motor down
   // Check if already at lower limit
   if (digitalRead(LIMIT_SWITCH1) == LOW) {
     Serial.println("Already at Limit Switch 1");
     setLinearGuide1Complete();
     return;
    }
   Serial.println("LD");
   digitalWrite(DRIVER1_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY9, HIGH);
   Serial.println("RELAY9 ON");
   delay(100);
   digitalWrite(DRIVER1_DIR, LOW); // Set direction for DOWN movement
   delay(100);
   digitalWrite(DRIVER1_ENA, LOW);
   delay(200);
  
   Serial.print("Limit States - 1: ");
   Serial.print(digitalRead(LIMIT_SWITCH1));
   Serial.print("  & Limit States - 2: ");
   Serial.println(digitalRead(LIMIT_SWITCH2));
   
   // Direct stepper movement (similar to LF/LR)
   unsigned long startTime = millis();
   const unsigned long timeout = 15000; // 15 second timeout
    
   while (digitalRead(LIMIT_SWITCH1) == HIGH) {
     digitalWrite(DRIVER1_STEP, HIGH);
     delayMicroseconds(40);
     digitalWrite(DRIVER1_STEP, LOW);
     delayMicroseconds(45);
      
     // Check for timeout
     if (millis() - startTime > timeout) {
       Serial.println("LD movement timeout! Stopping.");
       break;
      }
     if (checkStopCommand()) {
       Serial.println("Emergency Stop Triggered");
       stopMotor();
       digitalWrite(RELAY9, LOW);
       return;
      }
    }
   digitalWrite(RELAY9, LOW);
   Serial.println("RELAY9 OFF");
   Serial.println("Motors stopped at Limit Switch 1 or Emergency Stop.");
   delay(100);
   setLinearGuide1Complete();
  }
 else {
   Serial.println("Invalid direction for moveLinearGuide.");
  }
}
void moveLinearGuide2(String direction2) {
 linearGuide2Complete = false;
 if (direction2 == "LF") {  // Linear motor forward
   if (digitalRead(LIMIT_SWITCH4) == LOW) {
     Serial.println("Already at Limit Switch 4");
     setLinearGuide2Complete();
     return;
    }
   Serial.println("LF");
   digitalWrite(DRIVER2_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY7, HIGH); // Turn ON Relay 7
   Serial.println("RELAY7 ON"); // Notify Python
   delay(100);
   digitalWrite(DRIVER2_DIR, HIGH); // Set direction forward
   delay(100);
   digitalWrite(DRIVER2_ENA, LOW); // Enable motor
   delay(100);
   
   Serial.print("Limit States - 3: ");
   Serial.print(digitalRead(LIMIT_SWITCH3));
   Serial.print("  & Limit States - 4: ");
   Serial.println(digitalRead(LIMIT_SWITCH4));

   // Direct stepper movement (similar to LF/LR)
   unsigned long startTime = millis();
   const unsigned long timeout = 15000; // 15 second timeout

   while (digitalRead(LIMIT_SWITCH4) == HIGH) {
     digitalWrite(DRIVER2_STEP, HIGH);
     delayMicroseconds(20);
     digitalWrite(DRIVER2_STEP, LOW);
     delayMicroseconds(25);
     
     // Check for timeout
     if (millis() - startTime > timeout) {
       Serial.println("LF movement timeout! Stopping.");
       break;
      }
     if (checkStopCommand()) {
       Serial.println("Emergency Stop Triggered");
       stopMotor();
       digitalWrite(RELAY7, LOW);
       digitalWrite(DRIVER2_ENA, HIGH);
       return;
      }
    }
   digitalWrite(DRIVER2_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY7, LOW); // Turn OFF Relay 7
   Serial.println("RELAY7 OFF"); // Notify Python
   Serial.println("Motors stopped at Limit Switch 4 or Emergency Stop.");
   delay (100);
   // SET COMPLETION FLAG HERE
   setLinearGuide2Complete();
  } 
 else if (direction2 == "LR") {  // Move Reverse Direction (Linear motor reverse)
  // Check if already at lower limit
   if (digitalRead(LIMIT_SWITCH3) == LOW) { // Already at limit
     Serial.println("Already at Limit Switch 3");
     setLinearGuide2Complete();
     return;
    }
   Serial.println("LR");
   digitalWrite(DRIVER2_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY7, HIGH); // Turn ON Relay 7
   Serial.println("RELAY7 ON"); // Notify Python
   delay(100);
   digitalWrite(DRIVER2_DIR, LOW); // Set direction reverse
   delay(100);
   digitalWrite(DRIVER2_ENA, LOW); // Enable motor
   delay(100);
   
   Serial.print("Limit States - 3: ");
   Serial.print(digitalRead(LIMIT_SWITCH3));
   Serial.print("  & Limit States - 4: ");
   Serial.println(digitalRead(LIMIT_SWITCH4));
      
   // Run BTS7960B motor forward at 5 speed
   digitalWrite(EN_FEEDER, HIGH); // Enable feeder motor
   setFeederMotorSpeed(35, true); // Use the helper function to set speed and direction

   // Direct stepper movement (similar to LF/LR)
   unsigned long startTime = millis();
   const unsigned long timeout = 15000; // 15 second timeout
   
   while (digitalRead(LIMIT_SWITCH3) == HIGH) {
     digitalWrite(DRIVER2_STEP, HIGH);
     delayMicroseconds(20);
     digitalWrite(DRIVER2_STEP, LOW);
     delayMicroseconds(25);

     // Check for timeout
     if (millis() - startTime > timeout) {
       Serial.println("LD movement timeout! Stopping.");
       break;
      }
      
     if (checkStopCommand()) {
       Serial.println("Emergency Stop Triggered");
       stopMotor();
       digitalWrite(RELAY7, LOW);
       digitalWrite(DRIVER2_ENA, HIGH);
       return;
      }
    }
   // Stop both motors when Limit Switch 3 is triggered or X command is received
   digitalWrite(DRIVER2_ENA, HIGH);
   delay(100);
   digitalWrite(RELAY7, LOW); // Turn OFF Relay 7
   digitalWrite(EN_FEEDER, LOW);
   Serial.println("RELAY7 OFF"); // Notify Python
   Serial.println("Motors stopped at Limit Switch 3 or Emergency Stop.");
   delay (100);
   // SET COMPLETION FLAG HERE
   setLinearGuide2Complete();
  }
  else {
   Serial.println("Invalid direction for moveLinearGuide.");
  }
}
void moveStepper1(bool direction, int stopPin) {
 digitalWrite(DRIVER1_DIR, direction);
 digitalWrite(RELAY9, HIGH);
 digitalWrite(DRIVER1_ENA, LOW); // Enable stepper motor
 
 // Move stepper motor until limit switch is triggered or "X" command is received
 while (digitalRead(stopPin) == HIGH) {
   digitalWrite(DRIVER1_STEP, HIGH);
   delayMicroseconds(25);
   digitalWrite(DRIVER1_STEP, LOW);
   delayMicroseconds(30);
   
   if (checkStopCommand()) {
     Serial.println("Emergency Stop Triggered");
     stopMotor(); // Stop when "X" command is received
     return;
    }
  }
 digitalWrite(RELAY9, LOW); // Turn off Relay 1 to stop stepper motor control
}
void runOpenGripper() {
 // Define the behavior for Opening Grippers 1 & 2 after tying
 digitalWrite(RELAY3, LOW); // Deactivate Gripper 1 (RELAY3)
 digitalWrite(RELAY4, LOW); // Deactivate Gripper 2 (RELAY4)
 delay(1000);
}
void stopMotor() {
 // Stop Feeder motor
 analogWrite(RPWM_FEEDER, 0);
 analogWrite(LPWM_FEEDER, 0);
 digitalWrite(EN_FEEDER, LOW);
 digitalWrite(DRIVER1_ENA, HIGH);
 digitalWrite(DRIVER2_ENA, HIGH);
 feederRunning = false; // Reset feeder running flag
 // Turn off relays
 digitalWrite(RELAY9, LOW);
 digitalWrite(RELAY5, LOW);      // Disable both relays
 digitalWrite(RELAY6, LOW);
 motorRunning = false;  
 Serial.println("Motor stopped");
}
void resetSerialBuffer() {
 while (Serial.available()) {
   Serial.read();  // Clear serial input buffer
  }
}
void resetMode() {
 autoMode = false; // Reset Auto mode flag
 manualMode = false; // Reset Manual mode flag
 currentMode = NONE; // Reset the current mode to a default state
 resetSerialBuffer(); // Clear the serial buffer
 Serial.println("Enter next command (START, STOP, Q):");  // Prompt for the next command
}
void runMotor(bool clockwise) {
 motorComplete = false;
 if (motorRunning) {
   Serial.println("Motor is already running.");
   return isClockwise; // Return the current direction if already running
  }
 int relayPin = clockwise ? RELAY5 : RELAY6; // Choose correct relay
 digitalWrite(relayPin, HIGH); // Start motor
 motorRunning = true; // Set motor running flag to true
 countA = 0; // Reset count for Proxy A
 countB = 0; // Reset count for Proxy B
 countingEnabled = true; // Enable counting
 isClockwise = clockwise; // Set the direction

 // Disable counting for the opposite proxy and Proxy POS
 if (clockwise) {
   countProxyBEnabled = false; // Disable counting for Proxy B
   countProxyPOSEnabled = false; // Disable counting for Proxy POS
   countProxyAEnabled = true; // Enable counting for current direction
  } 
  else {
   countProxyAEnabled = false; // Disable counting for Proxy A
   countProxyPOSEnabled = false; // Disable counting for Proxy POS
   countProxyBEnabled = true; // Enable counting for current direction
  }
 Serial.println(clockwise ? "INDUCTION MOTOR CW STARTED" : "INDUCTION MOTOR CCW STARTED");
 return clockwise; // Return the direction
}
void proxyA() {
 static int lastStateA = LOW; // Remember previous state for Proxy A
 static unsigned long lastTriggerTimeA = 0;
 const unsigned long debounceDelay = 300;
 const unsigned long minCountInterval = 2600; // Minimum time between counts (1 second)
 
 int sensorStateA = digitalRead(PROXY_A); // Read the current state of the sensor
 unsigned long currentTime = millis();

 // Status feedback
 Serial.print("Proxy A Status: ");
 Serial.println(sensorStateA == HIGH ? "Triggered (High)" : "Not Triggered (Low)");

 // Check for Proxy A (CW)
 if (isClockwise && countProxyAEnabled) {
   // Only detect Rising edge (LOW to HIGH)
   if (lastStateA == LOW && sensorStateA == HIGH) {
     // Debounce check
     if ((currentTime - lastTriggerTimeA) > debounceDelay && (currentTime - lastTriggerTimeA) > minCountInterval) {
       countA++;  // Increment count for Proxy A
       lastTriggerTimeA = currentTime;  // Update last trigger time
       Serial.print("Proxy A Count: ");
       Serial.println(countA);

       // Stop relay 4 if count reaches 4
       if (countA >= 4) {
         digitalWrite(RELAY5, LOW); // Stop relay 4
         stopMotor();
         motorRunning = false; // Set flag to stop motor
         countA = 0; // Reset count after stopping
         countProxyAEnabled = false; // Disable counting for Proxy A
         countProxyBEnabled = true; // Re-enable counting for Proxy B
         // SET COMPLETION FLAG HERE
         setMotorComplete();
        }
      }
    }
   lastStateA = sensorStateA; // Update last state for Proxy A
  }
}
void proxyB() {
 static int lastStateB = LOW; // Remember previous state for Proxy B
 static unsigned long lastTriggerTimeB = 0;
 const unsigned long debounceDelay = 300;
 const unsigned long minCountInterval = 2600; // Minimum time between counts (1 second)

 int sensorStateB = digitalRead(PROXY_B);  // Read the current state of the sensor
 unsigned long currentTime = millis();

 // Status feedback
 Serial.print("Proxy B Status: ");
 Serial.println(sensorStateB == HIGH ? "Triggered (High)" : "Not Triggered (Low)");

 // Check for Proxy B (CCW)
 if (!isClockwise && countProxyBEnabled) {
   // Only detect Rising edge (LOW to HIGH)
   if (lastStateB == LOW && sensorStateB == HIGH) {
     // Debounce check
      if ((currentTime - lastTriggerTimeB) > debounceDelay && (currentTime - lastTriggerTimeB) > minCountInterval) {
       countB++;  // Increment count for Proxy B
       lastTriggerTimeB = currentTime;
       Serial.print("Proxy B Count: ");
       Serial.println(countB);

        // Stop relay 5 if count reaches 5
        if (countB >= 4) {
         digitalWrite(RELAY6, LOW); // Stop relay 5
         stopMotor();
         motorRunning = false; // Set flag to stop motor
         countB = 0; // Reset count after stopping
         countProxyBEnabled = false; // Disable counting for Proxy B
         countProxyAEnabled = true; // Re-enable counting for Proxy A
         // SET COMPLETION FLAG HERE
         setMotorComplete();
        }
      }
    }
   lastStateB = sensorStateB; // Update last state for Proxy B
  }
}

/* END CODE */