#!/usr/bin/env python 
from machine import Pin, I2C, PWM
import utime
import dht
from pico_i2c_lcd import I2cLcd
import os
import stepper
import network
import socket
# --- Credenciais de Wi-Fi ---
# ATENÇÃO: Substitua 'SUA_REDE' e 'SUA_SENHA' pelas suas credenciais reais.
SSID = "Wokwi-GUEST"
PASSWORD = ""
# --- Constantes de Pinos ---
DHT_PIN = 2
RELAY_HEAT_PIN = 3
FAN_PIN = 4
UMIDIFICADOR_PIN = 6
SPEAKER_PIN = 7
STATUS_LED_PIN = 13
OVOSCOPIO_LED_PIN = 5
STEPPER_PINS = [28, 27, 26, 22] 
# --- Constantes de Controle da Incubadora ---
TEMP_MIN = 37.0
TEMP_MAX = 38.0
UMIDADE_MIN = 45.0
UMIDADE_MAX = 55.0
TEMPO_INCUBACAO_DIAS = 21
DIAS_ALARME_ECLOSAO = 3
# --- Constantes de Tempo em Milissegundos ---
INTERVALO_VIRADA = 60 * 1000  
ALARME_DELAY = 1 * 60 * 1000           
UPDATE_LCD_INTERVAL = 2000             
MILISSEGUNDOS_POR_DIA = 8640
WEB_SERVER_PORT = 80
# --- Variáveis de Estado (globais) ---
ultima_virada = 0
ultimo_alarme = 0
ultimo_update_lcd = 0
alarme_ativado = False
alarme_eclosao_ativado = False
data_inicio_ms = 0
# --- Inicialização de Hardware ---
dht_sensor = dht.DHT22(Pin(DHT_PIN, Pin.PULL_UP))
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
buzzer_pwm = PWM(Pin(SPEAKER_PIN))
stepper_motor = stepper.HalfStepMotor.frompins(*STEPPER_PINS)
pin_heat = Pin(RELAY_HEAT_PIN, Pin.OUT)
pin_fan = Pin(FAN_PIN, Pin.OUT)
pin_umidificador = Pin(UMIDIFICADOR_PIN, Pin.OUT)
pin_status_led = Pin(STATUS_LED_PIN, Pin.OUT)
pin_ovoscopio_led = Pin(OVOSCOPIO_LED_PIN, Pin.OUT)
# --- Funções de Controle ---
def play_alarm():
    buzzer_pwm.duty_u16(50000)
def stop_alarm():
    buzzer_pwm.duty_u16(0)
def load_start_time():
    global data_inicio_ms
    try:
        with open('start_time.txt', 'r') as f:
            data_inicio_ms = int(f.read())
            print(f"Data de início carregada: {data_inicio_ms}")
    except (OSError, ValueError):
        data_inicio_ms = utime.ticks_ms()
        with open('start_time.txt', 'w') as f:
            f.write(str(data_inicio_ms))
        print("Nova data de início salva.")
def connect_to_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(SSID, PASSWORD)
    max_wait = 20
    while max_wait > 0:
        if wlan.status() < 0 or wlan.status() >= 3:
            break
        max_wait -= 1
        print('Esperando por conexao...')
        utime.sleep(1)
    if wlan.status() != 3:
        raise RuntimeError('Conexao falhou')
    else:
        status = wlan.ifconfig()
        print('Conectado a rede:', SSID)
        print('IP Address:', status[0])
        return wlan, status[0]
def start_web_server(ip):
    addr = socket.getaddrinfo(ip, WEB_SERVER_PORT)[0][-1]
    s = socket.socket()
    s.bind(addr)
    s.listen(1)
    return s
def get_web_page(temperatura, umidade, dias_restantes):
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>Incubadora Status</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
            body {{ font-family: sans-serif; text-align: center; background-color: #f0f0f0; }}
            .container {{ margin: 20px; padding: 20px; border-radius: 10px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }}
            h1 {{ color: #333; }}
            .status {{ font-size: 1.5em; margin: 10px 0; }}
            .controls button {{ background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px; cursor: pointer; }}
            .controls button:hover {{ background-color: #0056b3; }}
        </style>
    </head>
    <body>
        <div class="container">
            <h1>Incubadora de Ovos</h1>
            <p>Conectado via Wi-Fi</p>
            <div class="status">
                <p>Temperatura: {temperatura:.1f}°C</p>
                <p>Umidade: {umidade:.1f}%</p>
                <p>Faltam {dias_restantes} dias</p>
            </div>
            <hr>
            <h2>Controle Manual</h2>
            <div class="controls">
                <p>
                    <a href="/turn_on_fan">
                        <button>Ligar Ventilador</button>
                    </a>
                    <a href="/turn_off_fan">
                        <button>Desligar Ventilador</button>
                    </a>
                </p>
                <p>
                    <a href="/turn_on_heat">
                        <button>Ligar Aquecedor</button>
                    </a>
                    <a href="/turn_off_heat">
                        <button>Desligar Aquecedor</button>
                    </a>
                </p>
                <p>
                    <a href="/turn_on_humidifier">
                        <button>Ligar Umidificador</button>
                    </a>
                    <a href="/turn_off_humidifier">
                        <button>Desligar Umidificador</button>
                    </a>
                </p>
                <p>
                    <a href="/turn_eggs">
                        <button>Virar Ovos Manualmente</button>
                    </a>
                </p>
            </div>
        </div>
    </body>
    </html>
    """
    return html
def handle_web_request(conn):
    try:
        request = conn.recv(1024).decode('utf-8')
        request_line = request.split('\n')[0].strip()
        print('Request:', request_line)
        
        # Parse a URL para encontrar a ação
        action = request_line.split(' ')[1]
        
        # --- Controle manual dos atuadores ---
        if action == '/turn_on_fan':
            pin_fan.value(1)
        elif action == '/turn_off_fan':
            pin_fan.value(0)
        elif action == '/turn_on_heat':
            pin_heat.value(1)
        elif action == '/turn_off_heat':
            pin_heat.value(0)
        elif action == '/turn_on_humidifier':
            pin_umidificador.value(1)
        elif action == '/turn_off_humidifier':
            pin_umidificador.value(0)
        elif action == '/turn_eggs':
            stepper_motor.step(180)
            
        temperatura = dht_sensor.temperature()
        umidade = dht_sensor.humidity()
        dias_passados = utime.ticks_diff(utime.ticks_ms(), data_inicio_ms) // MILISSEGUNDOS_POR_DIA
        dias_restantes = TEMPO_INCUBACAO_DIAS - dias_passados
        
        response_html = get_web_page(temperatura, umidade, dias_restantes)
        conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
        conn.send(response_html)
        conn.close()
    except Exception as e:
        print(f"Erro ao processar requisicao: {e}")
        conn.close()
def setup():
    global ultima_virada, ultimo_alarme, ultimo_update_lcd
    
    lcd.backlight_on()
    lcd.clear()
    lcd.move_to(0, 0)
    lcd.putstr("ChacadeiraDe0v0s")
    utime.sleep(2)
    lcd.clear()
    
    load_start_time()
    
    pin_heat.value(0)
    pin_fan.value(0)
    pin_umidificador.value(0)
    pin_status_led.value(0)
    stepper_motor.reset()
    ultima_virada = utime.ticks_ms()
    ultimo_alarme = utime.ticks_ms()
    ultimo_update_lcd = utime.ticks_ms()
    buzzer_pwm.freq(1000)
    buzzer_pwm.duty_u16(0)
# --- LOOP PRINCIPAL ---
def main_loop():
    global ultimo_update_lcd, ultimo_alarme, alarme_ativado, alarme_eclosao_ativado, ultima_virada
    
    # Inicia a conexao Wi-Fi e o servidor web
    wlan, ip_address = connect_to_wifi()
    if not ip_address:
        print("Nao foi possivel conectar ao Wi-Fi. Reinicie o dispositivo.")
        return
        
    server_socket = start_web_server(ip_address)
    server_socket.settimeout(0.5) # Define um tempo limite nao-bloqueante
    while True:
        tempo_atual = utime.ticks_ms()
        # Leitura do sensor
        try:
            dht_sensor.measure()
            temperatura = dht_sensor.temperature()
            umidade = dht_sensor.humidity()
        except (OSError, ValueError) as e:
            print(f"Erro no sensor DHT: {e}")
            lcd.clear()
            lcd.putstr("Erro no sensor")
            utime.sleep(2)
            continue
        
        # --- Lógica de controle de aquecimento, ventilação e umidade ---
        if temperatura < TEMP_MIN:
            pin_heat.value(1)
            pin_fan.value(0)
        else:
            pin_heat.value(0)
            
        if temperatura > TEMP_MAX:
            pin_fan.value(1)
            pin_heat.value(0)
        else:
            pin_fan.value(0)
            
        if umidade < UMIDADE_MIN:
            pin_umidificador.value(1)
        else:
            pin_umidificador.value(0)
        
        # --- Atualização do LCD (não bloqueante) ---
        if utime.ticks_diff(tempo_atual, ultimo_update_lcd) >= UPDATE_LCD_INTERVAL:
            print(f"Temperatura: {temperatura:.2f}°C, Umidade: {umidade:.2f}%")
            lcd.clear()
            lcd.putstr(f"T:{temperatura:.1f}C U:{umidade:.1f}%")
            
            dias_passados = utime.ticks_diff(tempo_atual, data_inicio_ms) // MILISSEGUNDOS_POR_DIA
            dias_restantes = TEMPO_INCUBACAO_DIAS - dias_passados
            
            lcd.move_to(0, 1)
            if dias_restantes > 0:
                lcd.putstr(f"Aguarde: {dias_restantes} dias")
            
                if dias_restantes == 14:
                    lcd.move_to(0, 1)                    
                    lcd.putstr(f"Ovoscopio:{dias_restantes} dia")
                    pin_ovoscopio_led.value(1)
                    play_alarm()
                if dias_restantes < 14:                   
                    pin_ovoscopio_led.value(0)
                    stop_alarm()
                if dias_restantes == 11:
                    lcd.move_to(0, 1)                    
                    lcd.putstr(f"Ovoscopio:{dias_restantes} dia")
                    pin_ovoscopio_led.value(1)
                    play_alarm()
                if dias_restantes < 11:
                    pin_ovoscopio_led.value(0)
                    stop_alarm()
            if dias_restantes == 0:
                lcd.putstr("Eclosao!")
                utime.sleep(2)
                lcd.move_to(1, 0)
                lcd.clear()
                lcd.putstr("Incubando:::")
                utime.sleep(2)
            ultimo_update_lcd = tempo_atual
        # --- Lógica de Alarme de Faixa (Temperatura/Umidade) ---
        fora_da_faixa = (temperatura < TEMP_MIN or temperatura > TEMP_MAX or umidade < UMIDADE_MIN or umidade > UMIDADE_MAX)
        if fora_da_faixa:
            if utime.ticks_diff(tempo_atual, ultimo_alarme) >= ALARME_DELAY:
                if not alarme_ativado:
                    play_alarm()
                    alarme_ativado = True
                    pin_status_led.value(1)
                    lcd.clear()
                    lcd.putstr("ALARME: Fora da faixa")
        else:
            ultimo_alarme = tempo_atual
            if alarme_ativado:
                stop_alarm()
                alarme_ativado = False
                pin_status_led.value(0)
        # --- Lógica de Alarme de Eclosão ---
        dias_passados = utime.ticks_diff(tempo_atual, data_inicio_ms) // MILISSEGUNDOS_POR_DIA
        dias_restantes = TEMPO_INCUBACAO_DIAS - dias_passados
        if dias_restantes <= DIAS_ALARME_ECLOSAO and dias_restantes > 0 and not alarme_eclosao_ativado:
            play_alarm()
            alarme_eclosao_ativado = True
            lcd.clear()
            lcd.putstr("Alerta: Eclosao!")
            lcd.move_to(0, 1)
            lcd.putstr("Ini_Incuba_Seca")
            utime.sleep(2)
        # --- Viragem dos ovos (não bloqueante) ---
        if utime.ticks_diff(tempo_atual, ultima_virada) >= INTERVALO_VIRADA:
            stepper_motor.step(180) 
            ultima_virada = tempo_atual
        # --- Verificacao de requisição web (não bloqueante) ---
        try:
            conn, addr = server_socket.accept()
            print(f'Conexao de {addr}')
            handle_web_request(conn)
        except OSError as e:
            if e.args[0] == 11:  # errno.EAGAIN
                pass
            else:
                print(f"Erro no servidor socket: {e}")
                
# --- Início do programa ---
if __name__ == "__main__":
    setup()
    main_loop()