#include <Servo.h>
#include "pitches.h"
#include <DHT.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- Display Settings ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// --- DHT Settings ---
#define DHTPIN 6
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

// --- Pins ---
#define BUZZER_PIN 7
#define MQ2_PIN A0
#define STEP_UP 4
#define DIR_UP 5
#define STEP_DOWN 2
#define DIR_DOWN 3
#define ENABLE_PIN 8
#define TRIG_PIN 12
#define ECHO_PIN 11
#define SERVO_PIN 9

// --- Constants ---
const int COLLISION_DIST = 5;
const int SAFE_DIST = 45;
const int BASE_SPEED = 8;
const int REVERSE_SPEED = 20;
const int TURN_SPEED = 30;
const int START_BEEP_DIST = 40;
const int SCAN_DELAY = 400;
unsigned long lastSensorPrintTime = 0;
const unsigned long sensorPrintInterval = 3000;  // 3 seconds

Servo scanner;

float temperature = 0;
float humidity = 0;
int gasLevel = 0;

// --- Setup ---
void setup() {
  pinMode(STEP_UP, OUTPUT);
  pinMode(DIR_UP, OUTPUT);
  pinMode(STEP_DOWN, OUTPUT);
  pinMode(DIR_DOWN, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);
  digitalWrite(ENABLE_PIN, LOW);

  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);

  scanner.attach(SERVO_PIN);
  scanner.write(90);  // Forward

  Serial.begin(9600);
  dht.begin();

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED init failed"));
    while (1);
  }

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("Robot Ready");
  display.display();
  delay(2000);
}

// --- Motor Control ---
void stepMotors(int steps, int speedDelay) {
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_UP, HIGH); digitalWrite(STEP_DOWN, HIGH);
    delayMicroseconds(2);
    digitalWrite(STEP_UP, LOW); digitalWrite(STEP_DOWN, LOW);
    delayMicroseconds(speedDelay);
  }
}

void moveForward(int steps, int speedDelay) {
  digitalWrite(DIR_UP, HIGH); digitalWrite(DIR_DOWN, HIGH);
  stepMotors(steps, speedDelay);
}

void moveBackward(int steps, int speedDelay) {
  digitalWrite(DIR_UP, LOW); digitalWrite(DIR_DOWN, LOW);
  stepMotors(steps, speedDelay);
}

void turnLeft(int steps, int speedDelay) {
  digitalWrite(DIR_UP, LOW); digitalWrite(DIR_DOWN, HIGH);
  stepMotors(steps, speedDelay);
}

void turnRight(int steps, int speedDelay) {
  digitalWrite(DIR_UP, HIGH); digitalWrite(DIR_DOWN, LOW);
  stepMotors(steps, speedDelay);
}

void stopMotors() {
  digitalWrite(ENABLE_PIN, HIGH);
  delay(100);
  digitalWrite(ENABLE_PIN, LOW);
}

// --- Sensors ---
long readDistanceCM() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 20000);
  if (duration == 0) return -1;
  return duration * 0.034 / 2;
}

int scanDirection(int angle) {
  scanner.write(angle);
  delay(SCAN_DELAY);
  return readDistanceCM();
}

int readGasLevel() {
  return analogRead(MQ2_PIN);  // Raw value 0-1023
}

void updateDisplay(float temp, float hum, int gasRawVal) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(1);

  display.print("Temp: ");
  display.print(temp);
  display.println(" C");

  display.print("Humidity: ");
  display.print(hum);
  display.println(" %");

  display.print("Gas Raw: ");
  display.print(gasRawVal);
  
  display.setCursor(0, 40);
  display.print("Air Quality: ");
  if (gasRawVal >= 210 && gasRawVal <= 900) {
    display.println("Good");
  } else if (gasRawVal > 905) {
    display.println("Bad");
  } else {
    display.println("Unknown");
  }

  display.display();
}


// --- Buzzer Alert Based on Distance ---
void buzzerAlert(long distance) {
  static bool inCollision = false;

  // Out of range: stop buzzer
  if (distance <= 0 || distance > START_BEEP_DIST) {
    noTone(BUZZER_PIN);
    inCollision = false;
    return;
  }

  // If collision distance reached, turn on continuous beep
  if (distance <= COLLISION_DIST) {
    if (!inCollision) {
      tone(BUZZER_PIN, NOTE_C6);  // Only trigger once
      inCollision = true;
    }
    return;
  }

  // Not in collision anymore, ensure continuous tone is stopped
  if (inCollision) {
    noTone(BUZZER_PIN);
    inCollision = false;
  }

  // Distance between COLLISION_DIST and START_BEEP_DIST → beep with intervals
  int beepInterval = map(distance, COLLISION_DIST, START_BEEP_DIST, 100, 500);
  beepInterval = constrain(beepInterval, 100, 500);

  tone(BUZZER_PIN, NOTE_A4);
  delay(beepInterval / 2);
  noTone(BUZZER_PIN);
  delay(beepInterval / 2);
}


void safeBeep() {
  tone(BUZZER_PIN, NOTE_C5, 150);
  delay(150);
  noTone(BUZZER_PIN);
}

// --- Main Loop ---
void loop() {
  temperature = dht.readTemperature();
  humidity = dht.readHumidity();
  gasLevel = readGasLevel();
  
  if (isnan(temperature) || isnan(humidity)) {
    temperature = 0;
    humidity = 0;
  }

  updateDisplay(temperature, humidity, gasLevel);

  long dist = readDistanceCM();
  Serial.print("Distance: "); Serial.println(dist);
  // Print sensor readings every 3 seconds
  if (millis() - lastSensorPrintTime >= sensorPrintInterval) {
    lastSensorPrintTime = millis();
    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.print(" C, Humidity: ");
    Serial.print(humidity);
    Serial.print(" %, Gas Level: ");
    Serial.println(gasLevel);
  }

  buzzerAlert(dist);

  if (dist > 0 && dist <= COLLISION_DIST) {
    Serial.println("Obstacle! Reversing...");
    stopMotors();
    delay(200);

    while (true) {
    dist = readDistanceCM();
    buzzerAlert(dist); 

    if (dist > SAFE_DIST || dist == -1) {
    noTone(BUZZER_PIN);  // Stop buzzer immediately once safe
    break;
    }

    moveBackward(20, REVERSE_SPEED);
    delay(50);
    }


    safeBeep();
    Serial.println("Safe Distance Reached.!");
    int leftDist = scanDirection(150);
    delay(1000);
    int rightDist = scanDirection(30);
    delay(1000);
    scanner.write(90);
    //delay(200);

    if (leftDist == -1) leftDist = 0;
    if (rightDist == -1) rightDist = 0;

    if (leftDist > rightDist) {
      Serial.println("Turning LEFT");
      turnLeft(200, TURN_SPEED);
    } else {
      Serial.println("Turning RIGHT");
      turnRight(200, TURN_SPEED);
    }

    stopMotors();
    delay(300);
  } else {
    moveForward(30, BASE_SPEED);
  }

  delay(50);
}
A4988
A4988