/*
  Smart IoT Bin
  OpenSource Section | SB.Chetak Innovation

  Hardware reference:
  - Arduino Uno
  - HC-SR04 proximity sensor
  - HC-SR04 fill-level sensor
  - SG90-compatible servo
  - I2C LCD 16x2
  - SIM800L-compatible GSM module

  IMPORTANT:
  Use a properly regulated low-voltage supply for the GSM module and servo.
  This sketch contains a placeholder phone number.
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <SoftwareSerial.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo lidServo;
SoftwareSerial gsm(10, 11); // Arduino RX, TX

const uint8_t TRIG_PROX  = 3;
const uint8_t ECHO_PROX  = 4;
const uint8_t TRIG_LEVEL = 5;
const uint8_t ECHO_LEVEL = 6;
const uint8_t SERVO_PIN  = 9;

const float BIN_HEIGHT_CM = 30.0;
const float OPEN_DISTANCE_CM = 20.0;
const int FULL_THRESHOLD_PERCENT = 80;

const unsigned long LID_OPEN_MS = 2500UL;
const unsigned long SAMPLE_MS = 700UL;

bool lidOpen = false;
bool fullAlertSent = false;
unsigned long lidOpenedAt = 0;
unsigned long lastSampleAt = 0;

float distanceCm(uint8_t trigPin, uint8_t echoPin) {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duration = pulseIn(echoPin, HIGH, 30000UL);
  if (duration == 0) return -1.0;
  return duration * 0.0343 / 2.0;
}

void sendSMS(const String &message) {
  const char PHONE[] = "+000000000000"; // replace with verified recipient
  gsm.println("AT+CMGF=1");
  delay(300);
  gsm.print("AT+CMGS=\"");
  gsm.print(PHONE);
  gsm.println("\"");
  delay(300);
  gsm.print(message);
  gsm.write(26);
  delay(1200);
}

void setLid(bool open) {
  lidServo.write(open ? 90 : 0);
  lidOpen = open;
  if (open) lidOpenedAt = millis();
}

void setup() {
  pinMode(TRIG_PROX, OUTPUT);
  pinMode(ECHO_PROX, INPUT);
  pinMode(TRIG_LEVEL, OUTPUT);
  pinMode(ECHO_LEVEL, INPUT);

  lidServo.attach(SERVO_PIN);
  setLid(false);

  lcd.init();
  lcd.backlight();

  gsm.begin(9600);

  lcd.setCursor(0, 0);
  lcd.print("Smart IoT Bin");
  lcd.setCursor(0, 1);
  lcd.print("Ready");
}

void loop() {
  unsigned long now = millis();

  if (lidOpen && now - lidOpenedAt >= LID_OPEN_MS) {
    setLid(false);
  }

  if (now - lastSampleAt < SAMPLE_MS) return;
  lastSampleAt = now;

  float prox = distanceCm(TRIG_PROX, ECHO_PROX);
  if (!lidOpen && prox > 0 && prox <= OPEN_DISTANCE_CM) {
    setLid(true);
  }

  float emptySpace = distanceCm(TRIG_LEVEL, ECHO_LEVEL);
  int fillPercent = 0;

  if (emptySpace > 0) {
    float ratio = 1.0 - (emptySpace / BIN_HEIGHT_CM);
    fillPercent = constrain((int)(ratio * 100.0), 0, 100);
  }

  lcd.setCursor(0, 0);
  lcd.print("Fill:");
  lcd.print(fillPercent);
  lcd.print("%       ");

  lcd.setCursor(0, 1);
  lcd.print(lidOpen ? "Lid: OPEN      " : "Lid: CLOSED    ");

  if (fillPercent >= FULL_THRESHOLD_PERCENT && !fullAlertSent) {
    sendSMS("Smart IoT Bin alert: bin requires collection.");
    fullAlertSent = true;
  }

  if (fillPercent <= 20) {
    fullAlertSent = false;
  }
}
