#include #include #include #include // LCD Configuration LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20x4 LCD // Pin Definitions - TESTED AND WORKING #define RAIL_STEP_PIN D6 // for feeder linear position motor #define RAIL_DIR_PIN D7 // for feeder linear position motor #define FEEDER_STEP_PIN D10 // for food extraction motor #define FEEDER_DIR_PIN D8 // for food extraction motor #define ENCODER_CLK D2 #define ENCODER_DT D1 #define ENCODER_SW D3 #define ENDSTOP_PIN D0 // for homing limit switch (pulled up) // Stepper Motor Configuration AccelStepper railStepper(AccelStepper::DRIVER, RAIL_STEP_PIN, RAIL_DIR_PIN); AccelStepper feederStepper(AccelStepper::DRIVER, FEEDER_STEP_PIN, FEEDER_DIR_PIN); // Motor Parameters - SIMPLIFIED 200 SPEED FOR ALL #define MOTOR_SPEED 200 // All motors: 200 steps/sec #define MOTOR_ACCELERATION 100 // Acceleration for rail motor #define STEPS_PER_MM 80 // Steps per millimeter for rail motor #define FEEDER_FEED_SPEED 200 // Feeder: 200 steps/sec (simplified, reliable) #define STEPS_PER_REVOLUTION 800 // Both motors: 800 steps = 1 full revolution // System Constants - UPDATED FOR 800 STEPS/REV #define MAX_POSITIONS 3 #define MIN_FEED_TIME 1 #define MAX_FEED_TIME 60 #define DEBOUNCE_DELAY 50 #define LONG_PRESS_TIME 2000 #define MAX_RAIL_POSITION 8000 // Increased for 800 steps/rev (was 3000) #define HOMING_SPEED 100 // Slower homing speed for safety #define HOMING_TIMEOUT 30000 // Homing timeout in milliseconds // EEPROM Addresses #define EEPROM_POSITIONS 0 #define EEPROM_FEED_TIMES 20 #define EEPROM_SETTINGS_SAVED 50 #define EEPROM_HOME_POSITION 60 // System Variables volatile int encoderPos = 0; volatile bool encoderChanged = false; int lastEncoderPos = 0; bool lastButtonState = HIGH; unsigned long buttonPressTime = 0; unsigned long lastDebounceTime = 0; // Homing Variables bool isHomed = false; long homePosition = 0; bool homingInProgress = false; unsigned long homingStartTime = 0; // System State enum SystemState { STARTUP, HOMING, MAIN_MENU, SET_POSITIONS, SET_FEED_TIMES, FEEDING_MODE, RUNNING_CYCLE, MANUAL_CONTROL }; SystemState currentState = STARTUP; int menuIndex = 0; int editingPosition = 0; bool isEditing = false; // Feeding System Variables - UPDATED DEFAULT POSITIONS long feedingPositions[MAX_POSITIONS] = {1600, 4000, 6400}; // Updated for 800 steps/rev int feedingTimes[MAX_POSITIONS] = {5, 5, 5}; // Default feed times in seconds int currentFeedingPosition = 0; bool feedingActive = false; unsigned long feederStartTime = 0; bool feederRunning = false; bool railMoving = false; // Menu Structure const char* mainMenuItems[] = { "1.Start Feeding", "2.Set Positions", "3.Set Feed Times", "4.Manual Control", "5.Home System" }; // Function declarations void loadSettings(); void saveSettings(); void encoderISR(); void handleEncoder(); void handleButton(); void handleShortPress(); void handleLongPress(); void startHoming(); void handleHoming(); void displayMainMenu(); void selectMenuItem(); void displayHomingRequired(); void displaySetPositions(); void displaySetFeedTimes(); void displayFeedingMode(); void displayManualControl(); void displaySystemInfo(); void handleManualControl(); void handleManualMovement(int delta); void toggleFeeder(); void startFeedingCycle(); void handleRunningCycle(); void displayFeedingCountdown(); void moveToFeedingPosition(long targetPosition); void startFeeder(); void stopFeeder(); void stopAllMotors(); void stopFeeding(); void completeFeedingCycle(); void setup() { // Initialize serial FIRST Serial.begin(9600); delay(1000); // Give serial time to initialize Serial.println("Animal Feed System Starting..."); // Initialize LCD lcd.init(); lcd.backlight(); lcd.clear(); // Initialize pins AFTER serial is stable pinMode(ENCODER_CLK, INPUT_PULLUP); pinMode(ENCODER_DT, INPUT_PULLUP); pinMode(ENCODER_SW, INPUT_PULLUP); pinMode(ENDSTOP_PIN, INPUT_PULLUP); // Initialize motor pins pinMode(RAIL_STEP_PIN, OUTPUT); pinMode(RAIL_DIR_PIN, OUTPUT); pinMode(FEEDER_STEP_PIN, OUTPUT); pinMode(FEEDER_DIR_PIN, OUTPUT); // Set initial states - ALL LOW digitalWrite(RAIL_STEP_PIN, LOW); digitalWrite(RAIL_DIR_PIN, LOW); digitalWrite(FEEDER_STEP_PIN, LOW); digitalWrite(FEEDER_DIR_PIN, LOW); // Attach interrupts for encoder attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoderISR, FALLING); // Initialize stepper motors - ALL 200 SPEED railStepper.setMaxSpeed(MOTOR_SPEED); // 200 max railStepper.setAcceleration(MOTOR_ACCELERATION); feederStepper.setMaxSpeed(FEEDER_FEED_SPEED); // 200 max feederStepper.setAcceleration(MOTOR_ACCELERATION); Serial.println("Motors configured - ALL at 200 steps/sec"); // Load settings from EEPROM loadSettings(); // Display welcome message lcd.setCursor(0, 0); lcd.print("Animal Feed System"); lcd.setCursor(0, 1); lcd.print("SIMPLIFIED - 200sps"); lcd.setCursor(0, 2); lcd.print("Focus: MOTOR SPIN"); lcd.setCursor(0, 3); lcd.print("Initializing..."); delay(3000); // Start homing sequence currentState = HOMING; startHoming(); Serial.println("Setup complete - Starting homing"); } void loop() { handleEncoder(); handleButton(); // Always run stepper motors when needed if (homingInProgress) { railStepper.runSpeed(); // PRIORITY: Homing motor spinning } else { railStepper.run(); // Normal positioning } if (feederRunning) { feederStepper.runSpeed(); // Feeder motor spinning } switch (currentState) { case STARTUP: currentState = HOMING; startHoming(); break; case HOMING: handleHoming(); break; case MAIN_MENU: // Main menu handled by encoder and button functions break; case SET_POSITIONS: // Position setting handled by encoder and button functions break; case SET_FEED_TIMES: // Feed time setting handled by encoder and button functions break; case FEEDING_MODE: // Feeding mode handled by encoder and button functions break; case RUNNING_CYCLE: handleRunningCycle(); break; case MANUAL_CONTROL: handleManualControl(); break; } delay(10); } // Encoder interrupt service routine void encoderISR() { static unsigned long lastInterruptTime = 0; unsigned long interruptTime = millis(); if (interruptTime - lastInterruptTime > 5) { if (digitalRead(ENCODER_DT) == HIGH) { encoderPos++; } else { encoderPos--; } encoderChanged = true; } lastInterruptTime = interruptTime; } void handleEncoder() { if (encoderChanged) { encoderChanged = false; int encoderDelta = encoderPos - lastEncoderPos; switch (currentState) { case MAIN_MENU: menuIndex = constrain(menuIndex + encoderDelta, 0, 4); displayMainMenu(); break; case SET_POSITIONS: if (!isEditing) { editingPosition = constrain(editingPosition + encoderDelta, 0, MAX_POSITIONS - 1); } else { // REAL-TIME POSITION ADJUSTMENT - Move motor as encoder turns long newPosition = feedingPositions[editingPosition] + encoderDelta * 100; // Increased step size newPosition = constrain(newPosition, 0, MAX_RAIL_POSITION); // Update position and move motor immediately feedingPositions[editingPosition] = newPosition; railStepper.moveTo(newPosition); Serial.print("Moving to position "); Serial.print(editingPosition + 1); Serial.print(": "); Serial.println(newPosition); } displaySetPositions(); break; case SET_FEED_TIMES: if (!isEditing) { editingPosition = constrain(editingPosition + encoderDelta, 0, MAX_POSITIONS - 1); } else { feedingTimes[editingPosition] = constrain( feedingTimes[editingPosition] + encoderDelta, MIN_FEED_TIME, MAX_FEED_TIME ); } displaySetFeedTimes(); break; case MANUAL_CONTROL: handleManualMovement(encoderDelta); break; } lastEncoderPos = encoderPos; } } void handleButton() { static bool buttonProcessed = false; static unsigned long pressStartTime = 0; bool currentButtonState = digitalRead(ENCODER_SW); // Detect button press start if (currentButtonState == LOW && lastButtonState == HIGH && !buttonProcessed) { pressStartTime = millis(); buttonProcessed = true; Serial.println("Button press started"); } // Detect button release and determine press type if (currentButtonState == HIGH && lastButtonState == LOW && buttonProcessed) { unsigned long pressDuration = millis() - pressStartTime; buttonProcessed = false; Serial.print("Button released after: "); Serial.print(pressDuration); Serial.println("ms"); if (pressDuration > LONG_PRESS_TIME) { Serial.println("Long press detected"); handleLongPress(); } else if (pressDuration > 50) { Serial.println("Short press detected"); handleShortPress(); } } lastButtonState = currentButtonState; } void handleShortPress() { Serial.print("Short press in state: "); Serial.println(currentState); switch (currentState) { case MAIN_MENU: Serial.print("Selecting menu item: "); Serial.println(menuIndex); selectMenuItem(); break; case SET_POSITIONS: if (!isEditing) { isEditing = true; Serial.print("Entering live position edit mode for position "); Serial.println(editingPosition + 1); // Move to current saved position when entering edit mode railStepper.moveTo(feedingPositions[editingPosition]); } else { isEditing = false; // Wait for motor to stop before saving if (railStepper.isRunning()) { lcd.setCursor(0, 3); lcd.print("Wait for motor..."); return; // Don't save yet, motor still moving } saveSettings(); Serial.print("Position "); Serial.print(editingPosition + 1); Serial.print(" saved at: "); Serial.println(feedingPositions[editingPosition]); } displaySetPositions(); break; case SET_FEED_TIMES: if (!isEditing) { isEditing = true; } else { isEditing = false; saveSettings(); } displaySetFeedTimes(); break; case FEEDING_MODE: if (isHomed) { startFeedingCycle(); } else { displayHomingRequired(); } break; case RUNNING_CYCLE: // Emergency stop Serial.println("Emergency stop - button pressed during feeding"); stopFeeding(); currentState = FEEDING_MODE; displayFeedingMode(); break; case MANUAL_CONTROL: // Toggle feeder on/off toggleFeeder(); break; } } void handleLongPress() { Serial.print("Long press in state: "); Serial.println(currentState); // Long press goes back to previous menu or main menu switch (currentState) { case SET_POSITIONS: case SET_FEED_TIMES: case MANUAL_CONTROL: Serial.println("Going back to main menu"); stopAllMotors(); // Stop any motor movement when exiting currentState = MAIN_MENU; isEditing = false; displayMainMenu(); break; case FEEDING_MODE: Serial.println("Going back to main menu"); currentState = MAIN_MENU; displayMainMenu(); break; case RUNNING_CYCLE: Serial.println("Emergency stop - long press during feeding"); stopFeeding(); currentState = MAIN_MENU; displayMainMenu(); break; case MAIN_MENU: // Long press in main menu shows system info displaySystemInfo(); delay(3000); displayMainMenu(); break; default: Serial.println("Long press not available in this state"); break; } } // Homing Functions void startHoming() { if (homingInProgress) return; homingInProgress = true; homingStartTime = millis(); isHomed = false; lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== HOMING ==="); lcd.setCursor(0, 1); lcd.print("Moving to home..."); lcd.setCursor(0, 2); lcd.print("Direction: REVERSE"); lcd.setCursor(0, 3); lcd.print("Speed: 100 steps/sec"); // SIMPLE: Just set speed and start - SAME AS startFeeder() railStepper.setSpeed(-HOMING_SPEED); // NEGATIVE for reverse direction Serial.println("HOMING STARTED - moving REVERSE at 100 steps/sec"); Serial.print("Verification - Speed set to: "); Serial.print(railStepper.speed()); Serial.print(", MaxSpeed: "); Serial.println(railStepper.maxSpeed()); } void handleHoming() { if (!homingInProgress) return; // PRIORITY: SPIN MOTOR CONTINUOUSLY - SAME AS feederStepper.runSpeed() railStepper.runSpeed(); // Check endstop ONLY (minimal processing) if (digitalRead(ENDSTOP_PIN) == LOW) { // Endstop triggered - STOP MOTOR (same as stopFeeder) railStepper.setSpeed(0); railStepper.setCurrentPosition(0); homePosition = 0; isHomed = true; homingInProgress = false; Serial.println("HOMING COMPLETE - ENDSTOP TRIGGERED!"); Serial.println("RAIL MOTOR STOPPED - Speed set to 0"); // Simple completion display lcd.clear(); lcd.setCursor(0, 0); lcd.print("HOMING COMPLETE!"); lcd.setCursor(0, 1); lcd.print("System Ready"); delay(2000); currentState = MAIN_MENU; displayMainMenu(); return; } // Check timeout (minimal processing) if (millis() - homingStartTime > HOMING_TIMEOUT) { railStepper.setSpeed(0); homingInProgress = false; Serial.println("HOMING TIMEOUT - RAIL MOTOR STOPPED"); currentState = MAIN_MENU; displayMainMenu(); return; } // Update display ONLY every 3 seconds (minimal interruption) static unsigned long lastHomingDisplay = 0; if (millis() - lastHomingDisplay > 3000) { lcd.setCursor(0, 3); lcd.print("Rail: SPINNING..."); lastHomingDisplay = millis(); } } void displayMainMenu() { static int scrollOffset = 0; const int totalMenuItems = 5; const int visibleItems = 3; // Auto-scroll to keep selected item visible if (menuIndex < scrollOffset) { scrollOffset = menuIndex; } else if (menuIndex >= scrollOffset + visibleItems) { scrollOffset = menuIndex - visibleItems + 1; } lcd.clear(); lcd.setCursor(0, 0); lcd.print("MENU"); // Show homing status lcd.setCursor(15, 0); if (isHomed) { lcd.print("H:OK"); } else { lcd.print("H:NO"); } // Display visible menu items for (int i = 0; i < visibleItems; i++) { int itemIndex = scrollOffset + i; if (itemIndex < totalMenuItems) { lcd.setCursor(0, i + 1); // Show selection indicator if (itemIndex == menuIndex) { lcd.print(">"); } else { lcd.print(" "); } // Display menu item (truncate if needed) String menuItem = String(mainMenuItems[itemIndex]); if (menuItem.length() > 18) { menuItem = menuItem.substring(0, 18); } lcd.print(menuItem); } } // Show scroll indicators if needed if (scrollOffset > 0) { lcd.setCursor(19, 1); lcd.print("^"); } if (scrollOffset + visibleItems < totalMenuItems) { lcd.setCursor(19, 3); lcd.print("v"); } } void selectMenuItem() { Serial.print("Selecting menu item: "); Serial.println(menuIndex); switch (menuIndex) { case 0: // Start Feeding Serial.println("Start Feeding selected"); if (isHomed) { currentState = FEEDING_MODE; currentFeedingPosition = 0; displayFeedingMode(); } else { displayHomingRequired(); } break; case 1: // Set Positions Serial.println("Set Positions selected"); if (isHomed) { currentState = SET_POSITIONS; editingPosition = 0; isEditing = false; displaySetPositions(); } else { displayHomingRequired(); } break; case 2: // Set Feed Times Serial.println("Set Feed Times selected"); currentState = SET_FEED_TIMES; editingPosition = 0; isEditing = false; displaySetFeedTimes(); break; case 3: // Manual Control Serial.println("Manual Control selected"); if (isHomed) { currentState = MANUAL_CONTROL; displayManualControl(); } else { displayHomingRequired(); } break; case 4: // Home System Serial.println("Home System selected"); currentState = HOMING; startHoming(); break; default: Serial.println("Invalid menu selection"); break; } } void displayHomingRequired() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("HOMING REQUIRED"); lcd.setCursor(0, 1); lcd.print("Please home system"); lcd.setCursor(0, 2); lcd.print("before operation"); lcd.setCursor(0, 3); lcd.print("Press any key..."); delay(3000); displayMainMenu(); } void displaySetPositions() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("SET POSITIONS"); lcd.setCursor(0, 1); lcd.print("Pos "); lcd.print(editingPosition + 1); lcd.print(": "); if (isEditing) { lcd.print("["); lcd.print(feedingPositions[editingPosition]); lcd.print("]"); lcd.setCursor(15, 1); if (railStepper.isRunning()) { lcd.print("MOVE"); } else { lcd.print("STOP"); } } else { lcd.print(feedingPositions[editingPosition]); } lcd.setCursor(0, 2); lcd.print("Current: "); lcd.print(railStepper.currentPosition()); if (isEditing) { lcd.print(" LIVE"); } else { lcd.print(" steps"); } lcd.setCursor(0, 3); if (isEditing) { lcd.print("Turn:MOVE Press:Save"); } else { lcd.print("Press:Edit Hold:Back"); } } void displaySetFeedTimes() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("SET FEED TIMES"); lcd.setCursor(0, 1); lcd.print("Pos "); lcd.print(editingPosition + 1); lcd.print(": "); if (isEditing) { lcd.print("["); lcd.print(feedingTimes[editingPosition]); lcd.print("s]"); } else { lcd.print(feedingTimes[editingPosition]); lcd.print("s"); } lcd.setCursor(0, 2); lcd.print("Range: "); lcd.print(MIN_FEED_TIME); lcd.print("-"); lcd.print(MAX_FEED_TIME); lcd.print("s"); lcd.setCursor(0, 3); if (isEditing) { lcd.print("Turn:Adj Press:Save"); } else { lcd.print("Press:Edit Hold:Back"); } } void displayFeedingMode() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== FEEDING MODE ==="); lcd.setCursor(0, 1); lcd.print("Ready to feed"); lcd.setCursor(0, 2); lcd.print("Positions: "); lcd.print(MAX_POSITIONS); lcd.print(" Speed:200sps"); lcd.setCursor(0, 3); lcd.print("Press: Start Cycle"); } void displayManualControl() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("MANUAL CONTROL"); lcd.setCursor(0, 1); lcd.print("Position: "); lcd.print(railStepper.currentPosition()); lcd.setCursor(0, 2); lcd.print("Feeder: "); if (feederRunning) { lcd.print("SPINNING 200sps"); } else { lcd.print("OFF"); } lcd.setCursor(0, 3); lcd.print("Turn:Move Press:Feeder"); } void displaySystemInfo() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("SYSTEM INFO"); lcd.setCursor(0, 1); lcd.print("Homed: "); lcd.print(isHomed ? "YES" : "NO"); lcd.print(" Speed:200"); lcd.setCursor(0, 2); lcd.print("Position: "); lcd.print(railStepper.currentPosition()); lcd.setCursor(0, 3); lcd.print("Max: "); lcd.print(MAX_RAIL_POSITION); lcd.print(" 800spr"); } void handleManualControl() { // Update display periodically static unsigned long lastUpdate = 0; if (millis() - lastUpdate > 2000) { displayManualControl(); lastUpdate = millis(); } } void handleManualMovement(int delta) { if (!isHomed) return; long newPosition = railStepper.currentPosition() + (delta * 100); // Increased step size newPosition = constrain(newPosition, 0, MAX_RAIL_POSITION); railStepper.moveTo(newPosition); Serial.print("Manual move to position: "); Serial.println(newPosition); } void toggleFeeder() { if (feederRunning) { stopFeeder(); Serial.println("Manual: FEEDER STOPPED"); } else { startFeeder(); Serial.println("Manual: FEEDER STARTED - should be spinning at 200sps"); } displayManualControl(); } // ===== FEEDING CYCLE FUNCTIONS ===== void startFeedingCycle() { currentState = RUNNING_CYCLE; currentFeedingPosition = 0; feedingActive = true; Serial.println("Starting feeding cycle at 200 steps/sec"); Serial.print("Moving to position 1: "); Serial.println(feedingPositions[currentFeedingPosition]); // Show initial display lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== FEEDING ==="); lcd.setCursor(0, 1); lcd.print("Starting cycle..."); lcd.setCursor(0, 2); lcd.print("Moving to pos 1"); moveToFeedingPosition(feedingPositions[currentFeedingPosition]); } void handleRunningCycle() { if (!feedingActive) return; // STEP 1: Move rail motor to position (rail motor working perfectly) if (railMoving) { // Rail motor keeps moving to position automatically in main loop // Check if rail reached the position if (!railStepper.isRunning()) { railMoving = false; Serial.print("Position "); Serial.print(currentFeedingPosition + 1); Serial.println(" reached - rail motor STOPPED"); // Display feeding status displayFeedingCountdown(); // STEP 2: Start feeder spinning at constant speed startFeeder(); feederStartTime = millis(); Serial.println("FEEDER: Spinning at constant 200 steps/sec"); } return; // Don't do anything else while rail is moving } // STEP 3: Feeder spinning phase - feeder runs automatically in main loop if (feederRunning) { // Check if feeding time is complete unsigned long feederElapsed = millis() - feederStartTime; if (feederElapsed >= (unsigned long)feedingTimes[currentFeedingPosition] * 1000) { // STEP 4: Stop feeder, move to next position stopFeeder(); Serial.println("FEEDER: Stopped - feeding time complete"); currentFeedingPosition++; if (currentFeedingPosition >= MAX_POSITIONS) { // All positions done - cycle complete completeFeedingCycle(); } else { // Move to next position Serial.print("RAIL: Moving to next position "); Serial.println(currentFeedingPosition + 1); // Show moving display lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== FEEDING ==="); lcd.setCursor(0, 1); lcd.print("Moving to pos "); lcd.print(currentFeedingPosition + 1); lcd.setCursor(0, 2); lcd.print("Rail motor active..."); // Start rail movement to next position moveToFeedingPosition(feedingPositions[currentFeedingPosition]); } } // During feeder spinning: NO rail motor, NO other processing } } void displayFeedingCountdown() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== FEEDING ==="); lcd.setCursor(0, 1); lcd.print("Position: "); lcd.print(currentFeedingPosition + 1); lcd.print("/"); lcd.print(MAX_POSITIONS); lcd.setCursor(0, 2); lcd.print("Feeder: "); lcd.print(feedingTimes[currentFeedingPosition]); lcd.print("s @ 200sps"); lcd.setCursor(0, 3); lcd.print("Rail: STOPPED"); } void moveToFeedingPosition(long targetPosition) { railStepper.moveTo(targetPosition); railMoving = true; Serial.print("Rail moving to position: "); Serial.println(targetPosition); } // PROVEN startFeeder() function from minimal test void startFeeder() { feederStepper.setSpeed(FEEDER_FEED_SPEED); // 200 steps/sec feederRunning = true; Serial.println("FEEDER STARTED - 200 steps/sec - SPINNING NOW"); // Immediate verification Serial.print("Verification - Speed set to: "); Serial.print(feederStepper.speed()); Serial.print(", MaxSpeed: "); Serial.println(feederStepper.maxSpeed()); } void stopFeeder() { feederStepper.setSpeed(0); feederRunning = false; Serial.println("FEEDER STOPPED - Speed set to 0"); } void stopAllMotors() { railStepper.stop(); stopFeeder(); railMoving = false; Serial.println("All motors stopped"); } void stopFeeding() { stopAllMotors(); feedingActive = false; Serial.println("Feeding stopped - emergency stop"); } void completeFeedingCycle() { stopFeeding(); lcd.clear(); lcd.setCursor(0, 0); lcd.print("=== CYCLE COMPLETE ==="); lcd.setCursor(0, 1); lcd.print("All positions fed!"); lcd.setCursor(0, 2); lcd.print("Press: Start Again"); lcd.setCursor(0, 3); lcd.print("Hold: Main Menu"); currentState = FEEDING_MODE; Serial.println("Feeding cycle complete - all positions fed"); } void saveSettings() { // Save positions and feed times for (int i = 0; i < MAX_POSITIONS; i++) { EEPROM.put(EEPROM_POSITIONS + i * sizeof(long), feedingPositions[i]); EEPROM.put(EEPROM_FEED_TIMES + i * sizeof(int), feedingTimes[i]); } // Save home position EEPROM.put(EEPROM_HOME_POSITION, homePosition); // Mark settings as saved EEPROM.write(EEPROM_SETTINGS_SAVED, 0xAA); // Show confirmation lcd.setCursor(0, 3); lcd.print("Settings Saved! "); delay(1000); Serial.println("Settings saved to EEPROM"); } void loadSettings() { // Check if settings exist if (EEPROM.read(EEPROM_SETTINGS_SAVED) == 0xAA) { // Load positions and feed times for (int i = 0; i < MAX_POSITIONS; i++) { EEPROM.get(EEPROM_POSITIONS + i * sizeof(long), feedingPositions[i]); EEPROM.get(EEPROM_FEED_TIMES + i * sizeof(int), feedingTimes[i]); // Validate loaded values feedingPositions[i] = constrain(feedingPositions[i], 0, MAX_RAIL_POSITION); feedingTimes[i] = constrain(feedingTimes[i], MIN_FEED_TIME, MAX_FEED_TIME); } // Load home position EEPROM.get(EEPROM_HOME_POSITION, homePosition); Serial.println("Settings loaded from EEPROM"); } else { Serial.println("Using default settings"); } }