#include <AccelStepper.h>  // Include the AccelStepper library for stepper motor control
#include <Encoder.h>       // Include the Encoder library for rotary encoder
#include <SD.h>            // Include the SD library

// Define stepper motor connections
#define STEP_PIN      5   // Step pin for stepper motor
#define DIR_PIN       6   // Direction pin for stepper motor
#define ENABLE_PIN    7   // Enable pin for stepper motor

// Define rotary encoder pins
#define ENCODER_PIN1  2    // Encoder pin 1
#define ENCODER_PIN2  3    // Encoder pin 2

// Define SD card chip select pin
#define SD_CHIP_SELECT 10

// Define button pins
#define STOP_BUTTON_PIN 9  // Pin for stop button
#define START_BUTTON_PIN 8 // Pin for start button

// Initialize stepper motor object
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

// Initialize rotary encoder object
Encoder encoder(ENCODER_PIN1, ENCODER_PIN2);

// Variables for rotary encoder
int previousPosition = 0;
int currentPosition = 0;
bool encoderActive = false;  // Flag to check if the encoder is active

// File to store encoder data
File dataFile;

// Variables to store stepper and encoder data
long storedStepperPosition = 0;
long storedEncoderPosition = 0;

// Variables for button states
bool motorRunning = true;  // Flag to check if the motor is running
bool repeatActionActive = false; // Flag to check if repeat action is active
long lastStoppedPosition = 0; // Last position where the motor was stopped
long resumePosition = 0;      // Position to resume from
long targetPosition = 0;      // Target position for the stepper motor

// Flags for data storage control
bool dataStorageActive = false; // Flag to check if data storage is active

void setup() {
  // Initialize Serial communication
  Serial.begin(9600);

  // Print the header for the columns
  Serial.println("Encoder Position\tStepper Position");

  // Set up stepper motor properties
  stepper.setMaxSpeed(1000);   // Adjust this value as needed
  stepper.setAcceleration(100);  // Adjust this value as needed

  // Set up stepper motor pins
  pinMode(ENABLE_PIN, OUTPUT);
  digitalWrite(ENABLE_PIN, LOW);  // Enable the stepper motor driver
  
  // Set initial position of the stepper motor
  stepper.setCurrentPosition(0);

  // Initialize SD card
  if (!SD.begin(SD_CHIP_SELECT)) {
    Serial.println("SD card initialization failed!");
    return;
  }
  Serial.println("SD card initialized.");
  
  // Open file in append mode
  dataFile = SD.open("data.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println("Encoder Position\tStepper Position");
    dataFile.close();
  } else {
    Serial.println("Error opening file.");
  }

  // Set up button pins
  pinMode(STOP_BUTTON_PIN, INPUT_PULLUP);  // Use internal pull-up resistor
  pinMode(START_BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor
}

void loop() {
  // Check button states
  if (digitalRead(STOP_BUTTON_PIN) == LOW) {
    if (motorRunning) {
      motorRunning = false;  // Stop the motor
      repeatActionActive = false; // Stop repeat actions if active
      lastStoppedPosition = stepper.currentPosition(); // Save the current position
      stepper.stop();  // Immediately stop the stepper motor
      Serial.print("Machine stopped at position: ");
      Serial.println(lastStoppedPosition);

      // Store stop position data to SD card
      if (dataStorageActive) {
        dataFile = SD.open("data.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.print("Stopped at Encoder Position: ");
          dataFile.print(currentPosition);
          dataFile.print("\tStepper Position: ");
          dataFile.println(lastStoppedPosition);
          dataFile.close();  // Close the file
        } else {
          Serial.println("Error opening file.");
        }
      }
    }
    delay(200);  // Debounce delay to avoid multiple reads
  }

  if (digitalRead(START_BUTTON_PIN) == LOW) {
    if (!motorRunning) {
      motorRunning = true;   // Start the motor
      resumePosition = lastStoppedPosition; // Resume from the last stopped position
      Serial.print("Machine started. Resuming from position ");
      Serial.println(resumePosition);
      stepper.setCurrentPosition(resumePosition); // Set the current position to resume from
      stepper.moveTo(targetPosition); // Move to the original target position
    }
    delay(200);  // Debounce delay to avoid multiple reads
  }

  if (motorRunning) {
    // Read rotary encoder position
    currentPosition = encoder.read();

    // Check if encoder position has changed significantly
    if (abs(currentPosition - previousPosition) >= 4) { // Adjust this threshold as needed
      encoderActive = true;  // Encoder is active
      previousPosition = currentPosition; // Update previous position

      // Map encoder position to stepper motor steps
      long steps = map(currentPosition, 0, 80, 0, 200);  // Adjust 0-80 to your encoder's range and 0-200 to steps range

      // Move stepper motor to desired position
      stepper.moveTo(steps);
      targetPosition = steps;  // Update the target position

      // Print encoder and stepper motor position to Serial monitor in columns
      Serial.print(currentPosition);
      Serial.print("\t\t");
      Serial.println(steps);

      // Write encoder position to SD card in columns
      if (dataStorageActive) {
        dataFile = SD.open("data.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.print(currentPosition);
          dataFile.print("\t\t");
          dataFile.println(steps);
          dataFile.flush();  // Ensure data is written to the SD card
          dataFile.close();  // Close the file
        } else {
          Serial.println("Error opening file.");
        }
      }
    } else {
      encoderActive = false;  // Encoder is not active
    }

    // Run the stepper motor
    stepper.run();
  } else {
    // If motor is stopped, ensure it is not moving
    stepper.stop();
  }

  // Check for Serial input commands
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('\n'); // Read the entire line
    // Split the command into individual commands
    int startIndex = 0;
    int spaceIndex;

    while ((spaceIndex = command.indexOf(' ', startIndex)) != -1) {
      processCommand(command.substring(startIndex, spaceIndex));
      startIndex = spaceIndex + 1;
    }
    // Process the last command (or only command if no spaces were found)
    processCommand(command.substring(startIndex));
  }

  delay(10);  // Small delay to avoid excessive CPU usage
}

void processCommand(String cmd) {
  if (cmd.startsWith("a")) {
    // Extract the number of steps from the command
    int steps = cmd.substring(1).toInt();
    if (steps > 0) {
      Serial.print("Moving stepper motor to ");
      Serial.print(steps);
      Serial.println(" steps.");
      stepper.moveTo(steps);
      targetPosition = steps;  // Update the target position
    } else {
      Serial.println("Invalid step value.");
    }
  } else if (cmd == "S1") {
    // Start storing data
    dataStorageActive = true;
    Serial.println("Data storage started.");
  } else if (cmd == "S2") {
    // Stop storing data
    dataStorageActive = false;
    Serial.println("Data storage stopped.");
  } else if (cmd == "S") {
    // Store current stepper and encoder positions to SD card
    if (dataStorageActive) {
      dataFile = SD.open("data.txt", FILE_WRITE);
      if (dataFile) {
        dataFile.print("Stored Encoder Position: ");
        dataFile.print(currentPosition);
        dataFile.print("\t\tStored Stepper Position: ");
        dataFile.println(stepper.currentPosition());
        dataFile.close();  // Close the file
        Serial.println("Data stored successfully.");
      } else {
        Serial.println("Error opening file.");
      }
    } else {
      Serial.println("Data storage is not active. Use S1 to start storing data.");
    }
  } else if (cmd == "O") {
    // Open file and read its contents
    File readFile = SD.open("data.txt");
    if (readFile) {
      Serial.println("Reading data from file:");
      while (readFile.available()) {
        Serial.write(readFile.read());
      }
      readFile.close();
    } else {
      Serial.println("Error opening file.");
    }
  } else if (cmd == "r") {
    // Begin repeat action
    repeatActionActive = true;
    Serial.println("Repeating actions from stored data:");
    File readFile = SD.open("data.txt");
    if (readFile) {
      while (readFile.available() && repeatActionActive) {
        String line = readFile.readStringUntil('\n');
        // Skip the header line
        if (line.startsWith("Encoder Position") || line.startsWith("Stopped at Encoder Position")) continue;

        // Extract encoder and stepper positions from the line
        int tabPos = line.indexOf('\t');
        if (tabPos > 0) {
          long encoderPos = line.substring(0, tabPos).toInt();
          long stepperPos = line.substring(tabPos + 2).toInt(); // +2 to skip the tab character

          // Move stepper motor to the stored position
          stepper.moveTo(stepperPos);
          while (stepper.distanceToGo() != 0 && repeatActionActive) {
            stepper.run();
            // Check for stop button state during repeat action
            if (digitalRead(STOP_BUTTON_PIN) == LOW) {
              repeatActionActive = false;
              lastStoppedPosition = stepper.currentPosition(); // Save the current position
              stepper.stop();
              Serial.println("Machine stopped during repeat action.");
              // Store stop position data to SD card
              dataFile = SD.open("data.txt", FILE_WRITE);
              if (dataFile) {
                dataFile.print("Stopped at Encoder Position: ");
                dataFile.print(encoderPos);
                dataFile.print("\tStepper Position: ");
                dataFile.println(lastStoppedPosition);
                dataFile.close();  // Close the file
              } else {
                Serial.println("Error opening file.");
              }
              break;
            }
          }

          // Print the positions to the Serial Monitor
          Serial.print(encoderPos);
          Serial.print("\t\t");
          Serial.println(stepperPos);
        }
      }
      readFile.close();
    } else {
      Serial.println("Error opening file.");
    }
  }
}
A4988