from machine import Pin, ADC, PWM, I2C
from lcd_i2c import LCD
import time, math
# ==============================
# Configuración de hardware
# ==============================
# ADC para NTCs
adc_int = ADC(Pin(34))
adc_int.atten(ADC.ATTN_11DB)
adc_int.width(ADC.WIDTH_12BIT)
adc_ext = ADC(Pin(35))
adc_ext.atten(ADC.ATTN_11DB)
adc_ext.width(ADC.WIDTH_12BIT)
# Servo
servo = PWM(Pin(23), freq=50)
# LCD I2C
sda = Pin(21, Pin.OUT)
scl = Pin(22, Pin.OUT)
i2c = I2C(0, sda=sda, scl=scl, freq=400000)
NUM_ROWS = 2
NUM_COLS = 16
lcd_addr = i2c.scan()[0]
lcd = LCD(addr=lcd_addr, cols=NUM_COLS, rows=NUM_ROWS, i2c=i2c)
lcd.begin()
# Botón modo (con pull-up)
boton_modo = Pin(18, Pin.IN, Pin.PULL_UP)
# ==============================
# Parámetros del sistema
# ==============================
T_MIN = 22.0
T_MAX = 24.0
HYST = 0.5
BETA = 3950
R0 = 10000
T0 = 298.15
# Para duty_u16: 0.5ms a 2.5ms a 50Hz (0-100%)
DUTY_MIN = int(65535 * 0.025) # 0.5 ms
DUTY_MAX = int(65535 * 0.128) # 2.5 ms
# ==============================
# Variables globales
# ==============================
modo_manual = False
manual_pos = 0 # 0%, 50%, 100%
apertura = 0
# ==============================
# Funciones auxiliares
# ==============================
def ntc_to_temp(adc_val, r_series=10000):
"""Convierte lectura ADC a temperatura °C (NTC con resistencia arriba)"""
if adc_val <= 0:
return 0
v = adc_val / 4095.0 * 3.3
r_ntc = r_series * v / (3.3 - v)
t = 1.0 / (1.0/T0 + (1.0/BETA) * math.log(r_ntc/R0)) - 273.15
return t
def clamp(val, vmin, vmax):
return max(vmin, min(val, vmax))
def set_servo(percent):
"""Mueve servo al porcentaje indicado (0-100%)"""
duty = DUTY_MIN + int((DUTY_MAX - DUTY_MIN) * percent / 100)
servo.duty_u16(duty)
def mostrar_en_lcd(linea1, linea2=""):
lcd.clear()
lcd.print(linea1[:16])
lcd.set_cursor(0, 1)
lcd.print(linea2[:16])
def decidir_apertura(t_int, t_ext, apertura_prev):
apertura = apertura_prev
if t_int < T_MIN - HYST:
apertura = 0
elif t_int > T_MAX + HYST and t_ext >= t_int:
apertura = 0
elif t_int > T_MIN:
delta = t_int - t_ext
if delta > 0:
apertura = int((delta / 8.0) * 100)
apertura = clamp(apertura, 0, 100)
apertura = round(apertura / 10) * 10
apertura = clamp(apertura, 0, 100)
return apertura
# ==============================
# Bucle principal
# ==============================
set_servo(apertura)
while True:
# Leer temperaturas
t_int = ntc_to_temp(adc_int.read())
t_ext = ntc_to_temp(adc_ext.read())
# Chequear botón de modo
if boton_modo.value() == 0: # presionado
time.sleep_ms(300) # anti-rebote
if boton_modo.value() == 0:
if not modo_manual:
modo_manual = True
manual_pos = 0
else:
manual_pos += 50
if manual_pos > 100:
modo_manual = False
time.sleep(1)
if modo_manual:
apertura = manual_pos
set_servo(apertura)
linea1 = "MODO MANUAL"
linea2 = "Persiana:{}%".format(apertura)
else:
nueva_apertura = decidir_apertura(t_int, t_ext, apertura)
if nueva_apertura != apertura:
apertura = nueva_apertura
set_servo(apertura)
linea1 = "Int:{:.1f} Ext:{:.1f}".format(t_int, t_ext)
linea2 = "Auto:{}%".format(apertura)
mostrar_en_lcd(linea1, linea2)
time.sleep(2)