from machine import Pin, I2C
import ssd1306
import time

# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(22), sda=Pin(21))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

# Original tank dimensions (inches)
original_tank_height = 52  # Original height of the tank in inches
original_tank_width = 41    # Original width of the tank in inches

# Conversion factor for inches to centimeters (1 inch = 2.54 cm)
inches_to_cm = 2.54

# Convert original dimensions to centimeters
max_tank_height_cm = original_tank_height * inches_to_cm  # Convert height to cm
max_tank_width_cm = original_tank_width * inches_to_cm    # Convert width to cm

# Tank parameters for display (scaled for visual representation)
tank_x = 10
tank_y = 5  # Position tank slightly higher for visibility
tank_width = 30  # Graphical width for display
tank_height = 50  # Graphical height for display

# Ultrasonic sensor pins
TRIG_PIN = 19
ECHO_PIN = 18

# Set up pins for ultrasonic sensor
trigger = Pin(TRIG_PIN, Pin.OUT)
echo = Pin(ECHO_PIN, Pin.IN)

# Variables to track fill rate
previous_water_level = 0
previous_time = time.time()  # Get current time in seconds
fill_rates = []  # List to store fill rates for averaging

def measure_distance():
    # Send a 10us pulse to trigger
    trigger.value(0)  # Set trigger LOW
    time.sleep_us(2)
    trigger.value(1)  # Set trigger HIGH
    time.sleep_us(10)
    trigger.value(0)  # Set trigger LOW

    # Measure the duration of the echo pulse
    pulse_start = 0
    pulse_end = 0

    while echo.value() == 0:
        pulse_start = time.ticks_us()
    
    while echo.value() == 1:
        pulse_end = time.ticks_us()

    # Calculate the distance in cm (speed of sound is 34300 cm/s)
    pulse_duration = time.ticks_diff(pulse_end, pulse_start)
    distance = (pulse_duration * 0.0343) / 2  # cm
    return distance

def draw_tank(water_level):
    global previous_water_level, previous_time, fill_rates  # Access global variables

    # Clear the screen
    oled.fill(0)

    # Draw the tank outline (visual representation)
    for x in range(tank_width):
        oled.pixel(tank_x + x, tank_y, 1)  # Top
        oled.pixel(tank_x + x, tank_y + tank_height, 1)  # Bottom

    for y in range(tank_height):
        oled.pixel(tank_x, tank_y + y, 1)  # Left
        oled.pixel(tank_x + tank_width - 1, tank_y + y, 1)  # Right

    # Draw the water
    water_fill_height = int((water_level / max_tank_height_cm) * tank_height)
    for h in range(water_fill_height):
        for x in range(1, tank_width - 1):
            oled.pixel(tank_x + x, tank_y + (tank_height - 1 - h), 1)  # Fill water

    # Calculate percentage 
    percentage = (water_level / max_tank_height_cm) * 100

    # Calculate current fill rate
    current_time = time.time()
    elapsed_time = current_time - previous_time
    if elapsed_time > 0:  # Avoid division by zero
        current_fill_rate = (water_level - previous_water_level) / elapsed_time

        # Only consider fill rates that are positive and store them for averaging
        if current_fill_rate > 0:
            fill_rates.append(current_fill_rate)

        # Keep only the last 5 fill rates for smoothing
        if len(fill_rates) > 5:
            fill_rates.pop(0)

    else:
        current_fill_rate = 0

    # Estimate remaining time based on average fill rate
    if fill_rates:
        average_fill_rate = sum(fill_rates) / len(fill_rates)
        remaining_fill_height = max_tank_height_cm - water_level
        if average_fill_rate > 0:
            remaining_time = remaining_fill_height / average_fill_rate
        else:
            remaining_time = float('inf')  # If not filling, indicate it
    else:
        remaining_time = float('inf')  # No fill rates available

    # Convert remaining time to minutes and seconds
    if remaining_time != float('inf'):
        minutes = int(remaining_time // 60)
        seconds = int(remaining_time % 60)
    else:
        minutes = 0
        seconds = 0

    # Display compact text on the right
    oled.text('Tank', 50, 5)
    oled.text('Lvl: {:.0f}%'.format(percentage), 50, 20)
    oled.text('T: {}m {}s'.format(minutes, seconds), 50, 35)
    oled.show()

    # Update previous values for the next iteration
    previous_water_level = water_level
    previous_time = current_time

# Main loop for measuring and displaying water level
while True:
    distance = measure_distance()  # Get distance from the ultrasonic sensor
    water_level = max(0, max_tank_height_cm - distance)  # Calculate water level based on distance
    draw_tank(water_level)  # Display the current water level
    time.sleep(1)  # Update every second