import network
import time
import machine
from machine import Pin, ADC, SoftI2C
import dht
import ujson
from umqtt.simple import MQTTClient 
from esp32_i2c_lcd import I2cLcd
import BlynkLib

# Blynk authentication token
BLYNK_AUTH = "ZGac_fAwao5NBGqYIc7lT1P6FGpDrZcc"

# Blynk template ID and name
BLYNK_TEMPLATE_ID = "TMPL6RWFm7ZqG"
BLYNK_TEMPLATE_NAME = "Project IOT"

# MQTT Server Parameters
MQTT_CLIENT_ID = ""
MQTT_BROKER = "mqtt-dashboard.com"
MQTT_USER = ""
MQTT_PASSWORD = ""
MQTT_TOPIC = "wokwiproject"

sensor = dht.DHT22(Pin(15))
lcdI2C = I2cLcd(SoftI2C(scl=Pin(22), sda=Pin(21), freq=100000), 0x27, 2, 16)  # Change the address (0x27) and dimensions if needed

# Ultrasonic sensor pins
TRIG_PIN = Pin(4, Pin.OUT)
ECHO_PIN = Pin(5, Pin.IN)

# LED1 pin for DISTANCE Sensor RED
LED_PIN2 = Pin(2, Pin.OUT)
# LED2 pin for LIGHT Sensor YELLOW
LED_PIN1 = Pin(12, Pin.OUT)

# LDR pin
LDR_PIN = ADC(Pin(34))
LDR_PIN.atten(ADC.ATTN_11DB)  # Full range: 3.3v

def measure_distance():
    # Send a 10us pulse to trigger the measurement
    TRIG_PIN.value(0)
    time.sleep_us(2)
    TRIG_PIN.value(1)
    time.sleep_us(10)
    TRIG_PIN.value(0)

    # Measure the duration of the echo pulse
    duration = machine.time_pulse_us(ECHO_PIN, 1, 30000)
    if duration < 0:
        return None  # Timeout

    # Calculate distance in cm (speed of sound is 34300 cm/s)
    distance = (duration / 2) / 29.1
    return distance

def measure_light():
    return LDR_PIN.read()

print("Connecting to WiFi", end="")
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect('Wokwi-GUEST', '')
while not sta_if.isconnected():
    print(".", end="")
    time.sleep(0.1)
print(" Connected!")

# Initialize Blynk
blynk = BlynkLib.Blynk(BLYNK_AUTH)

print("Connecting to MQTT server... ", end="")
client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, user=MQTT_USER, password=MQTT_PASSWORD)
client.connect()

print("Connected!")

prev_weather = ""
lcd_update_interval = 5  # Update LCD every 5 seconds
lcd_last_update = time.time()

while True:
    blynk.run()  # Run Blynk background tasks

    print("Measuring weather conditions... ", end="")
    sensor.measure()
    temperature = sensor.temperature()
    humidity = sensor.humidity()

    print("Measuring distance... ", end="")
    distance = measure_distance()
    if distance is not None:
        print("Distance: {} cm".format(distance))
        if distance < 100:
            LED_PIN2.value(1)  # Turn on the LED
        else:
            LED_PIN2.value(0)  # Turn off the LED
    else:
        print("Distance measurement failed!")
        LED_PIN2.value(0)  # Turn off the LED if measurement fails

    print("Measuring light level... ", end="")
    light_level = measure_light()
    print("Light level: {}".format(light_level))

    # Check if light level is below 500
    if light_level < 500:
        LED_PIN1.value(1)  # Turn on the LED
    else:
        LED_PIN1.value(0)  # Turn off the LED

    message = ujson.dumps({
        "temp": temperature,
        "humidity": humidity,
        "distance": distance if distance is not None else "N/A",
        "light": light_level
    })

    if message != prev_weather:
        print("Updated!")
        print("Temperature: {}".format(temperature))
        print("Humidity: {}".format(humidity))
        print("Distance: {}".format(distance if distance is not None else "N/A"))
        print("Light level: {}".format(light_level))
        print("Reporting to MQTT topic {}: {}".format(MQTT_TOPIC, message))
        client.publish(MQTT_TOPIC, message)
        prev_weather = message
        # Send data to Blynk
        blynk.virtual_write(0, temperature)  # Virtual pin 0 for temperature
        blynk.virtual_write(1, humidity)  # Virtual pin 1 for humidity
        blynk.virtual_write(2, distance)  # Virtual pin 2 for distance
        blynk.virtual_write(3, light_level)  # Virtual pin 3 for light level

    # Update LCD every lcd_update_interval seconds
    if time.time() - lcd_last_update >= lcd_update_interval:
        # Display data on LCD
        lcdI2C.move_to(0, 0)
        lcdI2C.putstr("Temp: {}C".format(temperature))
        time.sleep(5)  # Wait for 2 seconds

        lcdI2C.move_to(0, 0)
        lcdI2C.putstr("Humidity: {}%".format(humidity))
        time.sleep(5)  # Wait for 2 seconds
        
        lcd_last_update = time.time()

    time.sleep(1)