#include <I2CKeyPad.h>
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <IRremote.h>

#define PIN_IR 4   // Signal Pin of IR IR

IRrecv IR(PIN_IR);
bool next = false;


#include <TM1637.h>

TM1637 TM1, TM2, TM3;       // Each display needs its own object


LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

RTC_DS3231 rtc;
I2CKeyPad keypad(0x38);
char keypad_layout[19] = "123A456B789C*0#DNF"; // N = NO_KEY, F = FAILED
int sensorInterrupt = 0;  // interrupt 0
int sensorPin       = 2; //Digital Pin 2
int solenoidValve1 = 27;
int solenoidValve2 = 12;
int relay_motor = 14; // Digital pin 5
//unsigned int SetPoint = 400; //400 milileter

#define  FS300A_PULSE     508         // PULSE / LITER
#define  FS300A_FLOW_RATE 60          // LITER / MINUTE
float calibrationFactor = 60.0F / 508.0F;  // FS300A_FLOW_RATE / FS300A_PULSE

/*The hall-effect flow sensor outputs pulses per second per litre/minute of flow.*/
//float calibrationFactor = 90; //You can change according to your datasheet

volatile byte pulse = 0;
uint16_t pulseCount;
bool busy;

float flowRate = 0.0;
float flowMilliLitres = 0;
float totalMilliLitres = 0, dispAmt = 0;
float  volume = 0;
float totvol;
float  vol = 0;
unsigned long oldTime = 0;
//const int relay_motor = 10;
//const int mech_countr = 14;
const int roto = 26;
const int emergencyButtonPin = 13;
long code = 0 ;
float price = 90.0;
float rate;  // ml per rupee
String company = ("Machlab.ltd");

const int pwmPin = 15; // Choose the GPIO pin where you want to generate the PWM signal
float frequency; // Frequency in Hz (adjust as needed)
const int dutyCycle = 50; // Duty cycle in percentage (adjust as needed)

void setup() {
  Serial.begin(115200);
  if (!keypad.begin()) {
    Serial.print("Cannot connect to I2C.\n");
    while (1);
  }


  keypad.loadKeyMap(keypad_layout);
  rtc.begin();



  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, INPUT_PULLUP);
  pinMode(pwmPin, OUTPUT);
  digitalWrite(pwmPin, HIGH);

  pinMode(solenoidValve1, OUTPUT);
  pinMode(solenoidValve2, OUTPUT);

  pinMode(relay_motor, OUTPUT);
  //pin Mode(mech_countr, OUTPUT);
  pinMode(roto, INPUT_PULLUP);

  pinMode(emergencyButtonPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(emergencyButtonPin), emergencyStop, FALLING);
  DateTime now = rtc.now(); // Get the current time from RTC

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print(company);
  delay(4000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Time: ");
  lcd.print(now.hour(), DEC);
  lcd.print(':');
  lcd.print(now.minute(), DEC);
  lcd.print(':');
  lcd.print(now.second(), DEC);
  lcd.setCursor(0, 1);
  lcd.print("Enter Amount:");
  delay(1000);
  // First display
  // -----------------------------------------
  TM1.begin(22, 19, 4);       //  clockpin, datapin, #digits
  TM1.displayClear();
  TM1.setBrightness(7);     // full brightness, default is 3

  // -----------------------------------------
  // Second display
  // -----------------------------------------
  TM2.begin(22, 18, 4);       //  clockpin, datapin, #digits
  TM2.displayClear();
  TM2.setBrightness(7);     // full brightness, default is 3

  // -----------------------------------------
  // Third display
  // -----------------------------------------
  TM3.begin(22, 5, 4);       //  clockpin, datapin, #digits
  TM3.displayClear();
  TM3.setBrightness(7);     // full brightness, default is 3
  TM3.displayPChar("90Rs"); // Initial text on third display
  // put your setup code here, to run once:
  Serial.println("Hello, ESP32!");
  attachInterrupt(digitalPinToInterrupt(sensorInterrupt), pulseCounter, CHANGE); //you can use Rising or Falling

  // Print the time on the LCD
  IR.enableIRIn(); // Start the IR
  randomSeed(analogRead(0)); // Seed the random number generator with an analog reading

}

void loop() {
  // Checks received an IR signal
  if (IR.decode()) {
    RemoteController();
    IR.resume();  // Receive the next value
  }
  rs232();

  char key = keypad.getChar();
  DateTime now = rtc.now(); // Get the current time from RTC
  if (key >= '0' && key <= '9') {
    //code += key;
    code = code * 10 + (key - '0');
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print(code);
    delay(100);
  }
  if (key == 'C')
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Time: ");
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Enter Amount:");
    code = 0;
    // inputIndex = 0; // Reset the input index
    // memset(inputBuffer, 0, sizeof(inputBuffer));
    delay(200);
    //code = ;
  }
  if (key == 'D') {

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Amount: Rs.");
    lcd.print(code);

    rate = 1000.0 / price;
    vol = (code * rate);
    volume = round(vol * 100.0) / 100000.0;
    totvol += volume;
    lcd.setCursor(0, 1);
    lcd.print("Volume = ");

    lcd.print(volume, 2);


    lcd.print("ltr");


    delay(1500);
  }


  if (totalMilliLitres < volume) {

    digitalWrite(relay_motor, HIGH);


    // Check the state of the fuel dispensing switch
    if (digitalRead(roto) == LOW) {

      solenoidLoop();


      if ((millis() - oldTime) > 1000) {
        detachInterrupt(sensorInterrupt);

        //  static unsigned long startTime;
        //  if (micros() - startTime < 1000000UL) return;   // 1000 milliseconds Interval
        //   startTime = micros();

        //  while (busy) {};

        //  pulseCount = pulse;
        //   pulse = 0;

        //  frequency = pulseCount / 2.0f;
        //flowRate = frequency * calibrationFactor;


        //flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
        oldTime = millis();
        for (int i = 0; i < 50; i++) {
          float flowRate = random(0, 100) / 10.0; // Simulating values between 0.0 and 10.0 L/min
          flowMilliLitres = (flowRate / 60) * 1000;
          totalMilliLitres += flowMilliLitres;
        }

        Serial.print("Flow rate :-");
        Serial.print(flowMilliLitres, DEC);
        Serial.print("mL/Second");
        Serial.print("\t");
        lcd.clear();

        lcd.setCursor(0, 0);
        lcd.print("Speed :");
        lcd.print(flowMilliLitres);
        lcd.print(" ml/s");
        Serial.print("Output Liquid Quantity: ");
        Serial.print(totalMilliLitres, DEC);

        Serial.println("mL");
        Serial.print("\t");
        lcd.setCursor(0, 1);
        lcd.print("Filled:");
        lcd.print(totalMilliLitres);
        lcd.print(" ml");
        dispAmt = totalMilliLitres / rate ;
        TM1.displayInt(dispAmt);
        TM2.displayInt(totalMilliLitres);

        // if (totalMilliLitres > 40) {
        //   SetSolinoidValve();
        // }
        solenoidLoop();
        pulseCount = 0;
        attachInterrupt(sensorInterrupt, pulseCounter, FALLING);

      }
    }
    else {

        digitalWrite(relay_motor, LOW);
       digitalWrite(solenoidValve1, LOW);
      digitalWrite(solenoidValve2, LOW);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("please turn on ");
      lcd.setCursor(0, 1);
      lcd.print("roto switch");
      delay(2000);
      return;
    }
  }

  else if (totalMilliLitres == vol)  {

    digitalWrite(relay_motor, LOW);
    //saveToEEPROM(inputBuffer);
    // if(key == 'A')
    // printBill();
    // inputIndex = 0; // Reset the input index
    //    memset(inputBuffer, 0, sizeof(inputBuffer));
    volume = 0;
    //delay(4000);


    // else{
    //    inputIndex = 0; // Reset the input index
    //    memset(inputBuffer, 0, sizeof(inputBuffer));
    //    volume = 0;
    // }
  }
  else {
    digitalWrite(relay_motor, LOW);
    volume = 0;
  }
}

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void emergencyStop() {
  // Stop the liquid dispensing process and perform any necessary cleanup.
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Emergency btn");
  lcd.setCursor(0, 1);
  lcd.print("pressed");
  delay(1000);
  code = 0;
  //  inputIndex = 0; // Reset the input index
  //  memset(inputBuffer, 0, sizeof(inputBuffer));
  digitalWrite(relay_motor, LOW);
  digitalWrite(solenoidValve1, LOW);
  digitalWrite(solenoidValve2, LOW);
  exit(0);
}

// void dispensedamount(){
//   dispAmt = totalMilliLitres/rate ;
//   dispAmt++;
// }
void solenoidLoop() {
  // bool fuelDispensed = false;

  // Check if total milliliters dispensed exceed 80% of the volume

  if (totalMilliLitres < (0.8 * vol)) {
    digitalWrite(solenoidValve1, HIGH);
    digitalWrite(solenoidValve2, HIGH);
  }
  else if (totalMilliLitres == (0.8 * vol)) {
    // Turn off solenoidValve1
    digitalWrite(solenoidValve1, LOW);
    // Turn on solenoidValve2
    digitalWrite(solenoidValve2, HIGH);
  }
  else {
    digitalWrite(solenoidValve1, LOW);
    // Turn off solenoidValve2
    digitalWrite(solenoidValve2, LOW);

  }
}


//------------REMOTE CONTROLLER FUNCTION------------
void RemoteController() {
  if (IR.decode()) {
    long long key = IR.decodedIRData.decodedRawData;
    //Serial.println(IR.decodedIRData.decodedRawData, HEX);

    switch (key) {
      case 0xFD02FF00:
        Serial.println("+");
        next ? price += 1 : price++;
        Serial.println(price);
        break;

      case 0x6798FF00:
        Serial.println("-");
        next ? price -= 1 : price--;
        Serial.println(price);

        break;
      case 0x57A8FF00:
        Serial.println("total dispensed volume:");
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("dispensed volume:");
        lcd.setCursor(0, 1);
        lcd.println(totvol);
        break;
    }
    delay(100);
    IR.resume();
  }
}


void rs232() {
  if (Serial.available() > 0) {
    char command = Serial.read(); // Read the incoming command

    if (command == 'C') {

      //float newCalibrationFactor = 0.0;

      Serial.print("Enter new calibration factor: ");
      float newCalibrationFactor = Serial.parseFloat();
      calibrationFactor = newCalibrationFactor;
      Serial.print("Calibration factor updated to: ");
      Serial.print(calibrationFactor);

    }
    if (command == 'N') {

      Serial.print("Enter Company name: ");
      String newString = Serial.readString();

      // Update the customString
      company = newString;
      
      Serial.print("Updated name: " + company);
      lcd.clear();
      lcd.print(company);
      delay(1500);
      // exit(0);

    }
    if (command == 'P') {


      Serial.print("Enter new price: ");
      float newprice = Serial.parseFloat();
      price = newprice;
      Serial.print("price is updated to: ");
      Serial.println(price);
    }

  }
}
esp:VIN
esp:GND.2
esp:D13
esp:D12
esp:D14
esp:D27
esp:D26
esp:D25
esp:D33
esp:D32
esp:D35
esp:D34
esp:VN
esp:VP
esp:EN
esp:3V3
esp:GND.1
esp:D15
esp:D2
esp:D4
esp:RX2
esp:TX2
esp:D5
esp:D18
esp:D19
esp:D21
esp:RX0
esp:TX0
esp:D22
esp:D23
8bit I2CBreakout
chip1:R1
chip1:R2
chip1:R3
chip1:R4
chip1:C1
chip1:C2
chip1:C3
chip1:C4
chip1:SDA
chip1:SCL
chip1:GND
chip1:VCC
keypad1:R1
keypad1:R2
keypad1:R3
keypad1:R4
keypad1:C1
keypad1:C2
keypad1:C3
keypad1:C4
lcd1:GND
lcd1:VCC
lcd1:SDA
lcd1:SCL
GND5VSDASCLSQWRTCDS1307+
rtc1:GND
rtc1:5V
rtc1:SDA
rtc1:SCL
rtc1:SQW
Loading
ds18b20
r1:1
r1:2
4-Digit Display
sevseg1:CLK
sevseg1:DIO
sevseg1:VCC
sevseg1:GND
NOCOMNCVCCGNDINLED1PWRRelay Module
relay1:VCC
relay1:GND
relay1:IN
relay1:NC
relay1:COM
relay1:NO
sw2:1
sw2:2
sw2:3
NOCOMNCVCCGNDINLED1PWRRelay Module
relay2:VCC
relay2:GND
relay2:IN
relay2:NC
relay2:COM
relay2:NO
NOCOMNCVCCGNDINLED1PWRRelay Module
relay3:VCC
relay3:GND
relay3:IN
relay3:NC
relay3:COM
relay3:NO
4-Digit Display
sevseg2:CLK
sevseg2:DIO
sevseg2:VCC
sevseg2:GND
4-Digit Display
sevseg3:CLK
sevseg3:DIO
sevseg3:VCC
sevseg3:GND
led1:A
led1:C
gnd1:GND
vcc1:VCC
vcc2:VCC
led2:A
led2:C
led3:A
led3:C
vcc3:VCC
gnd2:GND
gnd3:GND
ir2:GND
ir2:VCC
ir2:DAT
vcc4:VCC
gnd4:GND
Pulse GeneratorBreakout
pulse1:GND
pulse1:VCC
pulse1:PULSE
24LC256Breakout
chip2:A0
chip2:A1
chip2:A2
chip2:GND
chip2:VCC
chip2:WP
chip2:SCL
chip2:SDA
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r