from machine import Pin, PWM, I2C
from time import sleep, ticks_us, ticks_diff
from dht import DHT22
from ssd1306 import SSD1306_I2C  # OLED library for SSD1306

# Initialize OLED display
i2c = I2C(0, scl=Pin(22), sda=Pin(21))  # Adjust pins for Wokwi
oled = SSD1306_I2C(128, 64, i2c)        # OLED resolution 128x64

# Input pins
pir_sensor = Pin(14, Pin.IN)            # PIR motion sensor
dht_sensor = DHT22(Pin(4))              # DHT22 temperature and humidity sensor
trig = Pin(12, Pin.OUT)                 # Ultrasonic trigger pin
echo = Pin(13, Pin.IN)                  # Ultrasonic echo pin

# Output pins
buzzer = Pin(27, Pin.OUT)               # Buzzer
led = Pin(25, Pin.OUT)                  # LED
servo = PWM(Pin(15), freq=50)           # Servo motor (PWM)

# Function to set servo angle (0 to 180 degrees)
def set_servo_angle(angle):
    duty = int(40 + (angle / 180) * 115)  # Map angle to PWM duty cycle
    servo.duty(duty)

# Function to measure distance using ultrasonic sensor
def measure_distance():
    trig.value(0)
    sleep(0.002)  # Stabilize trigger pin
    trig.value(1)
    sleep(0.01)   # Send 10us pulse
    trig.value(0)

    while echo.value() == 0:
        start_time = ticks_us()  # Start timing
    while echo.value() == 1:
        end_time = ticks_us()    # End timing

    duration = ticks_diff(end_time, start_time)  # Pulse duration in microseconds
    distance = (duration / 2) * 0.0343           # Convert to cm
    return distance

# Initial setup
set_servo_angle(0)  # Servo at 0 degrees (door closed)
buzzer.value(0)
led.value(0)
oled.fill(0)
oled.show()

# Main loop
while True:
    # Read sensors
    motion_detected = pir_sensor.value()  # PIR sensor reading
    distance = measure_distance()         # Ultrasonic distance in cm
    try:
        dht_sensor.measure()
        temperature = dht_sensor.temperature()  # Temperature in °C
    except OSError:
        temperature = None

    # CASE 1: Both PIR and Ultrasonic detect
    if motion_detected and distance < 15:
        print("DOOR OPEN")
        oled.fill(0)
        oled.text("DOOR OPEN", 0, 0)
        oled.show()

        led.value(1)         # Turn on LED
        buzzer.value(1)      # Turn on buzzer
        set_servo_angle(60)  # Open the door
        sleep(5)             # Wait 5 seconds
        set_servo_angle(0)   # Close the door
        buzzer.value(0)      # Turn off buzzer
        led.value(0)         # Turn off LED

    # CASE 2: PIR only
    elif motion_detected:
        print("MOTION DETECTED")
        oled.fill(0)
        oled.text("MOTION DETECTED", 0, 0)
        oled.show()

        led.value(1)         # Turn on LED
        sleep(5)             # Wait 5 seconds
        led.value(0)         # Turn off LED

    # CASE 3: Ultrasonic only
    elif distance < 15:
        print("THERE'S SOMEONE AT THE DOOR")
        oled.fill(0)
        oled.text("SOMEONE AT DOOR", 0, 0)
        oled.show()

        led.value(1)         # Turn on LED
        sleep(5)             # Wait 5 seconds
        led.value(0)         # Turn off LED

    # CASE 4: High temperature
    if temperature is not None and temperature > 30:
        print(f"High Temp: {temperature}°C")
        oled.fill(0)
        oled.text(f"Temp: {temperature:.1f}C", 0, 0)
        oled.show()

        led.value(1)         # Turn on LED
        buzzer.value(1)      # Buzzer short beep
        sleep(0.01)
        buzzer.value(0)      # Turn off buzzer
    else:
        led.value(0)         # Turn off LED for normal temperature

    # Clear OLED if no conditions are met
    if not motion_detected and distance >= 15 and (temperature is None or temperature <= 30):
        oled.fill(0)
        oled.show()

    sleep(0.5)  # Delay for loop stability