#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>  // RTC library to keep track of time

#define SCREEN_WIDTH 128 // OLED width, in pixels
#define SCREEN_HEIGHT 64 // OLED height, in pixels
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // create an OLED display object connected to I2C

RTC_DS1307 rtc;  // create an RTC object

// Pin Declarations
int modePin  = 26;

int gridPin  = 35;
int solarPin = 34;

int selectionBtnPin = 25;
int potPin = 33;

int relay1 = 18;  // MOTOR1
int relay2 = 17;  // Bulb1
int relay3 = 16;  // Bulb2
int relay4 = 4;   // Bulb3

int relay5 = 23;  // MOTOR2
int relay6 = 19;  // Bulb4
int relay7 = 5;   // Bulb5
int relay8 = 15;  // Bulb6

// relay Load Value
int relay1_LoadVal = 70;
int relay2_LoadVal = 12;
int relay3_LoadVal = 23;
int relay4_LoadVal = 87;

int relay5_LoadVal = 23;
int relay6_LoadVal = 19;
int relay7_LoadVal = 5;
int relay8_LoadVal = 35;

int buzzerPin = 32;   // Buzzer

int GridValue  = 0;
int SolarValue = 0;

int selectionBtnVal = 0; // Current button state
int lastBtnVal = 0;      // Last button state to detect state change
int selectedMode = 0;    // Currently selected mode
int potValue = 0;        // Current potentiometer value
// int modes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // Array to store mode states (ON/OFF)
int modes[8] = {1,1,1,0, 1,1,1,0}; // Array to store mode states (ON/OFF)

// Preferences
int Prefrelay1 = 1;  
int Prefrelay2 = 2;  
int Prefrelay3 = 3;  
int Prefrelay4 = 4;  

int Prefrelay5 = 5;  
int Prefrelay6 = 6;  
int Prefrelay7 = 7;  
int Prefrelay8 = 8;  

// Day Slots and Price (Rs/Hr)
int slot1 = 12;  
int slot2 = 11;  
int slot3 = 50;  
int slot4 = 16;

// Auto
int s1_LoadState[8] = {1,1,1,1, 1,1,1,1};
int s2_LoadState[8] = {1,1,1,1, 0,0,0,1};
int s3_LoadState[8] = {1,1,1,1, 0,1,0,1};
int s4_LoadState[8] = {1,1,1,1, 1,1,1,1};


String relayStatusBeepOld = "";
String relayStatusBeepNew = "";

int gridThreshold = 22;   // Threshold for switching to solar (Gride Slot price)
int GridpowerThreshold = 100; // Threshold to check if power source is down (Watts)
int SolarpowerThreshold = 15; // Threshold to check if power source is down (Watts)

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

  // Initialize OLED display with I2C address 0x3C
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("Failed to start SSD1306 OLED"));
    while (1);
  }

  // Initialize RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (!rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // Set the RTC to the current date & time
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  delay(100);
  oled.clearDisplay();
  oled.setTextSize(1);
  oled.setTextColor(WHITE);

  // Set relay pins as output
  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(modePin, INPUT);

  pinMode(buzzerPin, OUTPUT);

  pinMode(selectionBtnPin, INPUT_PULLUP); // Set the button pin as input with pull-up

  // Start with all relays off
  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(buzzerPin, LOW);

  Serial.println("Hello, ESP32!");
  delay(200);
}

void loop() {
  // ========== Load State Selection Code ==========

  // Manual
  if (digitalRead(modePin) == HIGH){
    Serial.println("Mode : Manual"); delay(100);

    // Read the button state
    selectionBtnVal = digitalRead(selectionBtnPin);

    // If button state changes from HIGH to LOW (button press)
    if (selectionBtnVal == LOW && lastBtnVal == HIGH) {
      modes[selectedMode] = !modes[selectedMode]; // Toggle the selected mode
      Serial.print("Mode ");
      Serial.print(selectedMode + 1); // Mode number (1 to 8)
      Serial.print(" is now ");
      Serial.println(modes[selectedMode] ? "ON" : "OFF");
      delay(2000); // Debounce delay
    }

    // Update the last button state
    lastBtnVal = selectionBtnVal;

    // Read the potentiometer value
    potValue = analogRead(potPin);
    
    // Map the potentiometer value to the mode number (0 to 7)
    selectedMode = map(potValue, 0, 4095, 0, 7);

    // Print the selected mode number
    Serial.print("Selected Mode: ");
    Serial.print(selectedMode + 1); // Mode number (1 to 8)
    Serial.print(modes[selectedMode] ? " : ON" : " : OFF");
    Serial.println("");

    ManualactivatePowerSource();

    delay(100); // Delay to reduce serial output rate

  }
  // Automatic Mode
  else{
    Serial.println("Mode : Automatic"); delay(100);

    // Auto
    // Read power values
    GridValue = analogRead(gridPin);
    GridValue = map(GridValue, 0, 4095, 0, 1100);  // 0 to 4500 W

    SolarValue = analogRead(solarPin);
    SolarValue = map(SolarValue, 0, 4095, 0, 36);  // 0 to 2500 W

    // Get current time
    DateTime now = rtc.now();
    int currentHour = now.hour();
    Serial.println("Hr: " + String(currentHour) + " GridV: " + String(GridValue) + "W SolarV: " + String(SolarValue) + "W");

    // Determine slot price
    // int currentSlotPrice = getSlotPrice(currentHour);
    
    // Select the slot for simulation 
    // int currentSlotPrice = slot1;
    int currentSlotPrice = slot2;
    // int currentSlotPrice = slot3;
    // int currentSlotPrice = slot4;

    // Clear display before updating
    oled.clearDisplay();
    displayOnOLED(0,   0, "IIoT Project");
    displayOnOLED(10, 12, "Slot : " + String(currentSlotPrice) + " Rs/Hr");

    // Check grid and solar status
    if (GridValue < GridpowerThreshold && SolarValue < SolarpowerThreshold) {
      Serial.println("Grid & Solar is DOWN");
      activatePowerSource(false,false,false,false, false,false,false,false); 
      displayOnOLED(20, 24, "G -   S -");

    } else if (GridValue < GridpowerThreshold) {
      Serial.println("Grid DOWN, Solar UP");
      activatePowerSource(true,true,true,true, false,false,false,false); 
      displayOnOLED(20, 24, "G -   S *");

    } else if (SolarValue < SolarpowerThreshold) {
      Serial.println("Grid UP, Solar DOWN");
      activatePowerSource(true,true,true,true, true,true,true,true); 
      displayOnOLED(20, 24, "G *   S -");

      /*
        int s1_LoadState[8] = {1,1,1,0, 1,1,1,0};   only grid
        int s2_LoadState[8] = {1,1,1,0, 0,0,1,0};   only grid solar-1 (grpB: 2 ON grid)
        int s3_LoadState[8] = {1,1,1,0, 1,1,1,0};   grpA : grid + grpB: solar 
        int s4_LoadState[8] = {1,1,1,0, 0,1,1,0};   grpA : grid + grpB: solar (grpB: 1 ON grid)
        ...

          
        displayOnOLED(20, 24, "G -   S *");
        Serial.println("Relay: Solar ON  (Grid Slot Price HIGH !!!)");

        } else {
        activatePowerSource(true,true,true,true, true,true,true,true);  
        displayOnOLED(20, 24, "G *   S *");
        Serial.println("Relay: Grid UP, Solar UP");
      }
      */

    } else {
        // (slot vise load ON/OFF)
        if (slot1 == currentSlotPrice) {
          activatePowerSource(true,true,true,false, true,true,true,false);
          displayOnOLED(20, 24, "G *   S -");
          Serial.println("Relay: Grid *   Solar -");
        }  //only grid
        else if (slot2 == currentSlotPrice){
          activatePowerSource(true,true,true,false, false,false,true,false);
          displayOnOLED(20, 24, "G *   S *");
          Serial.println("Relay: Grid *   Solar *");
        }//only grid solar-1 (grpB: 2 ON grid)
        else if (slot3 == currentSlotPrice){
          activatePowerSource(true,true,true,false, true,true,true,false);
          displayOnOLED(20, 24, "G *   S *");
          Serial.println("Relay: Grid *   Solar *");
        }  //grpA : grid + grpB: solar 
        else {
          activatePowerSource(true,true,true,false, false,true,true,false);
          displayOnOLED(20, 24, "G *   S *");
          Serial.println("Relay: Grid *   Solar *");
        } //grpA : grid + grpB: solar (grpB: 1 ON grid)
    }


    // ------------------------------------------------------
    // Maximun Power Demand:
    // ------------------------------------------------------

    // Read the button state
    selectionBtnVal = digitalRead(selectionBtnPin);

    // If button state changes from HIGH to LOW (button press)
    if (selectionBtnVal == LOW && lastBtnVal == HIGH) {
      modes[selectedMode] = !modes[selectedMode]; // Toggle the selected mode
      Serial.print("Mode ");
      Serial.print(selectedMode + 1); // Mode number (1 to 8)
      Serial.print(" is now ");
      Serial.println(modes[selectedMode] ? "ON" : "OFF");
      delay(2000); // Debounce delay
    }
    
    lastBtnVal = selectionBtnVal; // Update the last button state
    potValue = analogRead(potPin);// Read the potentiometer value
    
    // Map the potentiometer value to the mode number (0 to 7)
    selectedMode = map(potValue, 0, 4095, 0, 7);

    // Print the selected mode number
    Serial.print("Selected Mode: "); Serial.print(selectedMode + 1); // Mode number (1 to 8)
    Serial.print(modes[selectedMode] ? " : ON" : " : OFF"); Serial.println("");

    digitalWrite(relay4, modes[3] ? HIGH : LOW);
    digitalWrite(relay8, modes[7] ? HIGH : LOW);

    int totalWatts = calculateTotalPower();
    Serial.println("Total Watts: " + String(totalWatts));

    if (totalWatts > 225){
      if (digitalRead(relay4)){Serial.println("Load 4 : ON [Solar]");}
      if (digitalRead(relay8)){Serial.println("Load 8 : ON [Solar]");}
    }
    else{
      if (digitalRead(relay4)){Serial.println("Load 4 : ON");}
      if (digitalRead(relay8)){Serial.println("Load 8 : ON");}
    }
  } 
  
  

  // ========== Existing IIoT Project Code ==========
  Serial.println("-----------------------------------------------------------------------------");
  
  // Display relay status on OLED
  String relayStatus1 = "r1 " + getRelayStatusOled(relay1) + " r2 " + getRelayStatusOled(relay2); 
  String relayStatus2 = "r3 " + getRelayStatusOled(relay3) + " r4 " + getRelayStatusOled(relay4);
  String relayStatus3 = "r5 " + getRelayStatusOled(relay5) + " r6 " + getRelayStatusOled(relay6); 
  String relayStatus4 = "r7 " + getRelayStatusOled(relay7) + " r8 " + getRelayStatusOled(relay8);

  displayOnOLED(40, 0, relayStatus1 + "  " + relayStatus2);
  displayOnOLED(50, 0, relayStatus3 + "  " + relayStatus4);

  Serial.println(relayStatus1 + " " + relayStatus2 + "        " + relayStatus3 + " " + relayStatus4);
  
  delay(2000);
}

int getSlotPrice(int currentHour) {
  if ((currentHour >=  0) && (currentHour <  6)) return slot1;
  if ((currentHour >=  6) && (currentHour < 12)) return slot2;
  if ((currentHour >= 12) && (currentHour < 18)) return slot3;
  if ((currentHour >= 18) && (currentHour < 24)) return slot4;
  return 0;
}

void ManualactivatePowerSource() {
  if (modes[0] == 1){digitalWrite(relay1, HIGH);}else{digitalWrite(relay1, LOW);}
  if (modes[1] == 1){digitalWrite(relay2, HIGH);}else{digitalWrite(relay2, LOW);} 
  if (modes[2] == 1){digitalWrite(relay3, HIGH);}else{digitalWrite(relay3, LOW);} 
  if (modes[3] == 1){digitalWrite(relay4, HIGH);}else{digitalWrite(relay4, LOW);}
  if (modes[4] == 1){digitalWrite(relay5, HIGH);}else{digitalWrite(relay5, LOW);} 
  if (modes[5] == 1){digitalWrite(relay6, HIGH);}else{digitalWrite(relay6, LOW);} 
  if (modes[6] == 1){digitalWrite(relay7, HIGH);}else{digitalWrite(relay7, LOW);} 
  if (modes[7] == 1){digitalWrite(relay8, HIGH);}else{digitalWrite(relay8, LOW);}


  // if (modes[0] == 1){digitalWrite(relay1, R1);}else{digitalWrite(relay1, LOW);}
  // if (modes[1] == 1){digitalWrite(relay2, R2);}else{digitalWrite(relay2, LOW);}
  // if (modes[2] == 1){digitalWrite(relay3, R3);}else{digitalWrite(relay3, LOW);}
  // if (modes[3] == 1){digitalWrite(relay4, R4);}else{digitalWrite(relay4, LOW);}
  // if (modes[4] == 1){digitalWrite(relay5, R5);}else{digitalWrite(relay5, LOW);}
  // if (modes[5] == 1){digitalWrite(relay6, R6);}else{digitalWrite(relay6, LOW);}
  // if (modes[6] == 1){digitalWrite(relay7, R7);}else{digitalWrite(relay7, LOW);}
  // if (modes[7] == 1){digitalWrite(relay8, R8);}else{digitalWrite(relay8, LOW);}

    // if (SolarValue > SolarpowerThreshold){
      // if (modes[0] == 1){digitalWrite(relay1, HIGH);}
      // if (modes[1] == 1){digitalWrite(relay2, HIGH);} 
      // if (modes[2] == 1){digitalWrite(relay3, HIGH);} 
      // if (modes[3] == 1){digitalWrite(relay4, HIGH);}
      // if (modes[4] == 1){digitalWrite(relay5, HIGH);} 
      // if (modes[5] == 1){digitalWrite(relay6, HIGH);} 
      // if (modes[6] == 1){digitalWrite(relay7, HIGH);} 
      // if (modes[7] == 1){digitalWrite(relay8, HIGH);}
    // }
  
  // Display relay status on OLED
  String relayStatus1 = "r1 " + getRelayStatusOled(relay1) + " r2 " + getRelayStatusOled(relay2); 
  String relayStatus2 = "r3 " + getRelayStatusOled(relay3) + " r4 " + getRelayStatusOled(relay4);
  String relayStatus3 = "r5 " + getRelayStatusOled(relay5) + " r6 " + getRelayStatusOled(relay6); 
  String relayStatus4 = "r7 " + getRelayStatusOled(relay7) + " r8 " + getRelayStatusOled(relay8);

  String  relayStatusNew = relayStatus1 + relayStatus2 + relayStatus3 + relayStatus4;

  if (relayStatusBeepOld != relayStatusNew) {
    tone(buzzerPin, 1000, 500);
    delay(1000);
    relayStatusBeepOld = relayStatusNew;
  }
}

// Function to activate power source based on input
void activatePowerSource(bool relay1State, bool relay2State, bool relay3State, bool relay4State, bool relay5State, bool relay6State, bool relay7State, bool relay8State) {
  digitalWrite(relay1, relay1State ? HIGH : LOW);
  digitalWrite(relay2, relay2State ? HIGH : LOW);
  digitalWrite(relay3, relay3State ? HIGH : LOW);
  digitalWrite(relay4, relay4State ? HIGH : LOW);

  digitalWrite(relay5, relay5State ? HIGH : LOW);
  digitalWrite(relay6, relay6State ? HIGH : LOW);
  digitalWrite(relay7, relay7State ? HIGH : LOW);
  digitalWrite(relay8, relay8State ? HIGH : LOW);

  // Display relay status on OLED
  String relayStatus1 = "r1 " + getRelayStatusOled(relay1) + " r2 " + getRelayStatusOled(relay2); 
  String relayStatus2 = "r3 " + getRelayStatusOled(relay3) + " r4 " + getRelayStatusOled(relay4);
  String relayStatus3 = "r5 " + getRelayStatusOled(relay5) + " r6 " + getRelayStatusOled(relay6); 
  String relayStatus4 = "r7 " + getRelayStatusOled(relay7) + " r8 " + getRelayStatusOled(relay8);

  String relayStatusNew = relayStatus1 + relayStatus2 + relayStatus3 + relayStatus4;

  if (relayStatusBeepOld != relayStatusNew) {
    tone(buzzerPin, 1000, 500);
    delay(1000);
    relayStatusBeepOld = relayStatusNew;
  }
}


String getRelayStatusOled(int relayPin) {
  return digitalRead(relayPin) ? "*" : "-";
}

void displayOnOLED(int row, int col, String text) {
  oled.setCursor(col, row);
  oled.println(text);
  oled.display();
}

// Function to calculate the total power of turned-on relays
int calculateTotalPower() {
  int totalPower = 0;

  // Check each relay state and add corresponding load value if turned on
  totalPower += digitalRead(relay1) == HIGH ? relay1_LoadVal : 0;
  totalPower += digitalRead(relay2) == HIGH ? relay2_LoadVal : 0;
  totalPower += digitalRead(relay3) == HIGH ? relay3_LoadVal : 0;
  totalPower += digitalRead(relay4) == HIGH ? relay4_LoadVal : 0;
  totalPower += digitalRead(relay5) == HIGH ? relay5_LoadVal : 0;
  totalPower += digitalRead(relay6) == HIGH ? relay6_LoadVal : 0;
  totalPower += digitalRead(relay7) == HIGH ? relay7_LoadVal : 0;
  totalPower += digitalRead(relay8) == HIGH ? relay8_LoadVal : 0;

  return totalPower;
}
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
Loading
ssd1306
GND5VSDASCLSQWRTCDS1307+
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module