#include <PID_v1.h> // Include the PID library

// Define pins
const int thermistorPin = A0; // Analog pin connected to the thermistor
const int heaterPin = 9; // PWM pin connected to the heater
const int dirPin1 = 2; // Direction pin for stepper motor 1
const int stepPin1 = 3; // Step pin for stepper motor 1
const int dirPin2 = 4; // Direction pin for stepper motor 2
const int stepPin2 = 5; // Step pin for stepper motor 2
const int dirPin3 = 6; // Direction pin for stepper motor 3
const int stepPin3 = 7; // Step pin for stepper motor 3
const int fanPin = 6; // PWM pin for the fan
const int stepsPerRevolution = 200; // Number of steps per revolution for the stepper motor (200 for a typical NEMA 17)
int stepDelay = 500; // Delay time between steps in microseconds

// Timing variables for stepper motors and temperature updates
unsigned long lastStepTime1 = 0; // Last step time for motor 1
unsigned long lastStepTime2 = 0; // Last step time for motor 2
unsigned long lastStepTime3 = 0; // Last step time for motor 3
unsigned long lastTemperatureUpdateTime = 0; // Last update time for temperature control
unsigned long lastPrintTime = 0; // Last time the status was printed to the serial monitor
bool stepState1 = LOW; // Step state for motor 1
bool stepState2 = LOW; // Step state for motor 2
bool stepState3 = LOW; // Step state for motor 3

// PID parameters
double Setpoint = 230.0; // Desired temperature in Celsius
double Input, Output; // Variables for PID input and output
double Kp = 1, Ki = 4, Kd = .8; // PID constants: Proportional, Integral, Derivative
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); // Initialize PID controller

// Thermistor variables
const double BETA = 3950; // Beta constant for the thermistor
const double ROOM_TEMP = 298.15; // Room temperature in Kelvin (25ºC)
const double NOMINAL_RESISTANCE = 100000; // Nominal resistance of the thermistor (100k ohms)

void setup() {
  Serial.begin(9600); // Start serial communication at 9600 baud
  pinMode(heaterPin, OUTPUT); // Set heater pin as output
  pinMode(fanPin, OUTPUT); // Set fan pin as output

  // Configure the PID controller
  myPID.SetMode(AUTOMATIC); // Set PID to automatic mode

  // Configure stepper motor pins
  pinMode(stepPin1, OUTPUT); // Set step pin for motor 1 as output
  pinMode(dirPin1, OUTPUT); // Set direction pin for motor 1 as output
  pinMode(stepPin2, OUTPUT); // Set step pin for motor 2 as output
  pinMode(dirPin2, OUTPUT); // Set direction pin for motor 2 as output
  pinMode(stepPin3, OUTPUT); // Set step pin for motor 3 as output
  pinMode(dirPin3, OUTPUT); // Set direction pin for motor 3 as output

  // Initialize motor direction
  digitalWrite(dirPin1, HIGH); // Set direction for motor 1
  digitalWrite(dirPin2, HIGH); // Set direction for motor 2
  digitalWrite(dirPin3, HIGH); // Set direction for motor 3
}

void loop() {
  unsigned long currentTime = millis(); // Get the current time

  // Execute tasks
  controlMotor1(); // Control stepper motor 1
  controlMotor2(); // Control stepper motor 2
  controlMotor3(); // Control stepper motor 3
  controlTemperature(); // Control temperature using PID
  controlFan(); // Control fan speed
  printStatus(); // Print status to serial monitor
}

void controlMotor1() {
  unsigned long currentMicros = micros(); // Get the current time in microseconds

  // Stepper motor control
  if (currentMicros - lastStepTime1 >= stepDelay) { // Check if enough time has passed
    lastStepTime1 = currentMicros; // Update last step time

    // Toggle step pin state
    stepState1 = !stepState1; // Alternate the state of the step pin
    digitalWrite(stepPin1, stepState1); // Set the step pin
  }
}

void controlMotor2() {
  unsigned long currentMicros = micros(); // Get the current time in microseconds

  // Stepper motor control
  if (currentMicros - lastStepTime2 >= stepDelay) { // Check if enough time has passed
    lastStepTime2 = currentMicros; // Update last step time

    // Toggle step pin state
    stepState2 = !stepState2; // Alternate the state of the step pin
    digitalWrite(stepPin2, stepState2); // Set the step pin
  }
}

void controlMotor3() {
  unsigned long currentMicros = micros(); // Get the current time in microseconds

  // Stepper motor control
  if (currentMicros - lastStepTime3 >= stepDelay) { // Check if enough time has passed
    lastStepTime3 = currentMicros; // Update last step time

    // Toggle step pin state
    stepState3 = !stepState3; // Alternate the state of the step pin
    digitalWrite(stepPin3, stepState3); // Set the step pin
  }
}

void controlTemperature() {
  // Update temperature and PID control every 500 ms
  if (millis() - lastTemperatureUpdateTime >= 500) { // Check if enough time has passed
    lastTemperatureUpdateTime = millis(); // Update last temperature update time

    // Read thermistor temperature
    int analogValue = analogRead(thermistorPin); // Read analog value from thermistor pin
    double resistance = (1023.0 / analogValue - 1) * NOMINAL_RESISTANCE; // Calculate thermistor resistance
    double temperature = 1 / (log(resistance / NOMINAL_RESISTANCE) / BETA + 1 / ROOM_TEMP) - 273.15; // Calculate temperature in Celsius

    Input = temperature; // Set PID input to the measured temperature

    // Compute PID
    myPID.Compute(); // Perform PID computation

    // Control heater element
    analogWrite(heaterPin, Output); // Set heater power based on PID output
  }
}

void controlFan() {
  // Control fan based on temperature
  analogWrite(fanPin, 225); // Set fan speed (fixed value in this example)
}

void printStatus() {
  // Print values to serial monitor every 500 ms to reduce impact on motor control
  if (millis() - lastPrintTime >= 500) { // Check if enough time has passed
    lastPrintTime = millis(); // Update last print time
    Serial.print("Temperature: "); // Print temperature label
    Serial.println(Input); // Print current temperature
    // Serial.print("C, Output: ");
    // Serial.println(Output);
  }
}