#include <SPI.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <PID_v1.h>
#include <Bounce2.h>


// Arduino Mega interrupt pins:
// INT.0 on Pin 21
// INT.1 on Pin 20
// INT.2 on Pin 19
// INT.3 on Pin 18
// INT.4 on Pin 2
// INT.5 on Pin 3


const byte buttonPin0 = 2;     // Interrupt pins  
const byte buttonPin1 = 3;
const byte buttonPin2 = 18;
const byte buttonPin3 = 19;

enum States { STOP, STARTING, AUTO, USER };
volatile States state = STOP;

Bounce debouncer0 = Bounce();
Bounce debouncer1 = Bounce();
Bounce debouncer2 = Bounce();
Bounce debouncer3 = Bounce();

double temperature;
double desiredTemperature = 100.0;
double feederSpeed;
double blowerSpeed;
double oxygenLevel;
double targetOxygenLevel = 21.0;
bool isRunning = true;
bool manualMode = false;

const double feeder_Kp = 5.0;
const double feeder_Ki = 0.3;
const double feeder_Kd = 0.1;
const double blower_Kp = 5.0;
const double blower_Ki = 0.3;
const double blower_Kd = 0.1;

PID feederPID(&temperature, &feederSpeed, &desiredTemperature, feeder_Kp, feeder_Ki, feeder_Kd, DIRECT);
PID blowerPID(&oxygenLevel, &blowerSpeed, &targetOxygenLevel, blower_Kp, blower_Ki, blower_Kd, DIRECT);

const int temperaturePin = A0;
const int oxygenPin = A1;

const int Feeder_Relay_pin = 7;
unsigned long Feeder_on_Duration = 2000;
unsigned long Feeder_off_Duration = 20000;
unsigned long rememberTime1 = 0;
int Feeder_Relay_State = LOW;

const int Blower_Relay_pin = 8;
unsigned long Blower_on_Duration = 2000;
unsigned long Blower_off_Duration = 2000;
unsigned long rememberTime2 = 0;
int Blower_Relay_State = LOW;

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

volatile int buttonPressed = -1;

void changeState0() { buttonPressed = 0; }
void changeState1() { buttonPressed = 1; }
void changeState2() { buttonPressed = 2; }
void changeState3() { buttonPressed = 3; }

void handleButtonPress() {
  if (buttonPressed == 0) state = STOP;
  if (buttonPressed == 1) state = STARTING;
  if (buttonPressed == 2) state = AUTO;
  if (buttonPressed == 3) state = USER;
  buttonPressed = -1;
  Serial.print("STATE: ");
  Serial.println(state);
}

void displayInfo(const char* stateName);

void setup() {
  Serial.begin(9600);

  pinMode(buttonPin0, INPUT_PULLUP);
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);

  debouncer0.attach(buttonPin0);
  debouncer0.interval(5);
  debouncer1.attach(buttonPin1);
  debouncer1.interval(5);
  debouncer2.attach(buttonPin2);
  debouncer2.interval(5);
  debouncer3.attach(buttonPin3);
  debouncer3.interval(5);

  attachInterrupt(digitalPinToInterrupt(buttonPin0), changeState0, FALLING);
  attachInterrupt(digitalPinToInterrupt(buttonPin1), changeState1, FALLING);
  attachInterrupt(digitalPinToInterrupt(buttonPin2), changeState2, FALLING);
  attachInterrupt(digitalPinToInterrupt(buttonPin3), changeState3, FALLING);

  //attachInterrupt(digitalPinToInterrupt(buttonPin0), changeState0, FALLING);
  //attachInterrupt(digitalPinToInterrupt(buttonPin1), changeState1, FALLING);
  //attachInterrupt(digitalPinToInterrupt(buttonPin2), changeState2, RISING);  // Changed from FALLING to RISING
  //attachInterrupt(digitalPinToInterrupt(buttonPin3), changeState3, RISING);  // Changed from FALLING to RISING


  pinMode(Feeder_Relay_pin, OUTPUT);
  pinMode(Blower_Relay_pin, OUTPUT);

  feederPID.SetMode(AUTOMATIC);
  blowerPID.SetMode(AUTOMATIC);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE, BLACK);

  Serial.println("Setup complete");
}

void loop() {
  debouncer0.update();
  debouncer1.update();
  debouncer2.update();
  debouncer3.update();

  if (buttonPressed != -1) {
    handleButtonPress();
  }


  // Read sensor values
  temperature = analogRead(temperaturePin) * 0.48828125;
  oxygenLevel = analogRead(oxygenPin) * 0.48828125;

  // Calculate PID outputs
  feederPID.Compute();
  blowerPID.Compute();

  //////////////////////////////////////////////////////////////////////////////////
  // FEEDER AND BLOWER RELAY TIMER,  counting millisecond ticks
  //////////////////////////////////////////////////////////////////////////////////
        unsigned long currentMillis = millis();

        // Set the Feeder Relay state, ON and OFF
        if (Feeder_Relay_State == HIGH) {
          if (currentMillis - rememberTime1 >= Feeder_on_Duration) {
            Feeder_Relay_State = LOW;
            rememberTime1 = currentMillis;
            Serial.println("Feeder Relay OFF");
          }
        } else {
          if (currentMillis - rememberTime1 >= Feeder_off_Duration) {
            Feeder_Relay_State = HIGH;
            rememberTime1 = currentMillis;
            Serial.println("Feeder Relay ON");
          }
        }


        // Set the Blower Relay state, ON and OFF
        if (Blower_Relay_State == HIGH) {
          if (currentMillis - rememberTime2 >= Blower_on_Duration) {
            Blower_Relay_State = LOW;
            rememberTime2 = currentMillis;
            Serial.println("Blower Relay OFF");
          }
        } else {
          if (currentMillis - rememberTime2 >= Blower_off_Duration) {
            Blower_Relay_State = HIGH;
            rememberTime2 = currentMillis;
            Serial.println("Blower Relay ON");
          }
        }

  //////////////////////////////////////////////////////////////////////////////////



  switch (state) {
    case STOP:
              displayInfo("STOP");
              digitalWrite(Feeder_Relay_pin, false);
              digitalWrite(Blower_Relay_pin, false);                
    break;


    case STARTING:
              displayInfo("STARTING");
              digitalWrite(Feeder_Relay_pin, Feeder_Relay_State);
              digitalWrite(Blower_Relay_pin, Blower_Relay_State);
              Feeder_on_Duration  = 1000;       // ON   time for Feeder_Relay   1 seconds
              Feeder_off_Duration = 20000;      // OFF  time for Feeder_Relay  20 seconds    
              Blower_on_Duration  = 1000;       // ON   time for Blower_Relay   1 seconds
              Blower_off_Duration = 3000;       // OFF  time for Blower_Relay   3 seconds              
    break;


    case AUTO:
              displayInfo("RUN AUTO");
              digitalWrite(Feeder_Relay_pin, Feeder_Relay_State);
              digitalWrite(Blower_Relay_pin, Blower_Relay_State);
              Feeder_on_Duration  = 500;       // TAHAN PID-SAATIMELTA LASKENNALLINEN ARVO, taytyy kehitella laskukaava..
              Feeder_off_Duration = 5000;      // TAHAN PID-SAATIMELTA LASKENNALLINEN ARVO, taytyy kehitella laskukaava..  
              Blower_on_Duration  = 500;       // TAHAN PID-SAATIMELTA LASKENNALLINEN ARVO, taytyy kehitella laskukaava..
              Blower_off_Duration = 2000;      // TAHAN PID-SAATIMELTA LASKENNALLINEN ARVO, taytyy kehitella laskukaava..
    break;


    case USER:
              displayInfo("RUN MANUAL");
              digitalWrite(Feeder_Relay_pin, Feeder_Relay_State);
              digitalWrite(Blower_Relay_pin, Blower_Relay_State);
              Feeder_on_Duration  = 1000;       // TAHAN KAYTTAJAN OMA ASETUS, SAATO VALIKOSTA TAI ETHERNETIN KAUTTA..
              Feeder_off_Duration = 20000;      // TAHAN KAYTTAJAN OMA ASETUS, SAATO VALIKOSTA TAI ETHERNETIN KAUTTA..
              Blower_on_Duration  = 1000;       // TAHAN KAYTTAJAN OMA ASETUS, SAATO VALIKOSTA TAI ETHERNETIN KAUTTA..
              Blower_off_Duration = 3000;       // TAHAN KAYTTAJAN OMA ASETUS, SAATO VALIKOSTA TAI ETHERNETIN KAUTTA.. 
    break;
  }
}

void displayInfo(const char* stateName) {
  Serial.print(stateName);
  Serial.print(" | Temp: ");
  Serial.print(temperature);
  Serial.print(" | Oxygen: ");
  Serial.print(oxygenLevel);
  Serial.print(" | FRelay: ");
  Serial.print(digitalRead(Feeder_Relay_pin));
  Serial.print(" | BRelay: ");
  Serial.print(digitalRead(Blower_Relay_pin));
  Serial.println();

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("POLTTOMAATTI 1.5\n");
  display.print(stateName);
  display.print("\nLampotila: ");
  display.print(String(temperature, 1));
  display.print("\nHappiarvo: ");
  display.print(String(oxygenLevel, 1));
  display.print("\nRuuvi-Rele: ");
  display.print(digitalRead(Feeder_Relay_pin));
  display.print("\nIlma-Rele: ");
  display.print(digitalRead(Blower_Relay_pin));
  display.print("\n");
  display.display();
}
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module