from machine import Pin, I2C, PWM
import time
import dht
import ssd1306
# ----------------------------
# Pines
# ----------------------------
PIN_DHT = 13
I2C_SDA = 21
I2C_SCL = 22
# LED normal (actuador)
PIN_LED_ACT = 33
# LED RGB (indicador)
PIN_R = 18
PIN_G = 19
PIN_B = 23
COMMON_ANODE = False # True si tu RGB es anodo comun (COM a 3V3)
# ----------------------------
# Control
# ----------------------------
SETPOINT_C = 20.0
TOLERANCIA_C = 1.0 # OK si T esta entre [19, 21]
MIN_OK = SETPOINT_C - TOLERANCIA_C
MAX_OK = SETPOINT_C + TOLERANCIA_C
ERROR_FULL_SCALE_C = 10.0 # a 10°C de error => actuador 100% (ajustable)
# ----------------------------
# OLED
# ----------------------------
i2c = I2C(0, scl=Pin(I2C_SCL), sda=Pin(I2C_SDA), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# ----------------------------
# DHT22
# ----------------------------
dht_sensor = dht.DHT22(Pin(PIN_DHT))
# ----------------------------
# PWM helpers
# ----------------------------
def pwm_write_percent(pwm_obj, pct: int):
"""PWM en % (0..100) compatible con duty_u16 o duty."""
pct = 0 if pct < 0 else 100 if pct > 100 else pct
if hasattr(pwm_obj, "duty_u16"):
pwm_obj.duty_u16(int(pct * 65535 / 100))
else:
pwm_obj.duty(int(pct * 1023 / 100))
def rgb_write_255(pwm_obj, val: int):
"""PWM RGB 0..255, invierte si es anodo comun."""
val = 0 if val < 0 else 255 if val > 255 else val
if COMMON_ANODE:
val = 255 - val
if hasattr(pwm_obj, "duty_u16"):
pwm_obj.duty_u16(int(val * 65535 / 255))
else:
pwm_obj.duty(int(val * 1023 / 255))
def set_rgb(r, g, b):
rgb_write_255(pwm_r, r)
rgb_write_255(pwm_g, g)
rgb_write_255(pwm_b, b)
def actuator_from_temp(temp_c: float) -> int:
"""
Brillo actuador segun error respecto a SP:
- error = |T - SP|
- 0% si error=0
- 100% si error >= ERROR_FULL_SCALE_C
"""
err = abs(temp_c - SETPOINT_C)
if err <= 0:
return 0
if err >= ERROR_FULL_SCALE_C:
return 100
return int(err * 100 / ERROR_FULL_SCALE_C)
# ----------------------------
# PWM devices
# ----------------------------
pwm_act = PWM(Pin(PIN_LED_ACT), freq=1000)
pwm_r = PWM(Pin(PIN_R), freq=1000)
pwm_g = PWM(Pin(PIN_G), freq=1000)
pwm_b = PWM(Pin(PIN_B), freq=1000)
# Inicial (por defecto OK = verde)
pwm_write_percent(pwm_act, 0)
set_rgb(0, 255, 0)
# ----------------------------
# Timers
# ----------------------------
DHT_PERIOD_MS = 2000
UI_PERIOD_MS = 200
last_dht_ms = time.ticks_ms()
last_ui_ms = time.ticks_ms()
temp_c = None
hum = None
act_pct = 0
estado = "----"
oled.fill(0)
oled.text("Control Sala", 0, 0)
oled.text("SP:20.0C", 0, 16)
oled.text("Init...", 0, 32)
oled.show()
while True:
now = time.ticks_ms()
# 1) Leer DHT22 cada ~2s
if time.ticks_diff(now, last_dht_ms) >= DHT_PERIOD_MS:
last_dht_ms = now
try:
dht_sensor.measure()
temp_c = dht_sensor.temperature()
hum = dht_sensor.humidity()
# LED normal = actuador
act_pct = actuator_from_temp(temp_c)
pwm_write_percent(pwm_act, act_pct)
# RGB siempre encendido:
# OK => verde 100%, ALARMA => rojo 100%
if temp_c < MIN_OK or temp_c > MAX_OK:
set_rgb(255, 0, 0) # alarma rojo
estado = "ALARM"
else:
set_rgb(0, 255, 0) # ok verde
estado = "OK"
except Exception:
temp_c = None
hum = None
act_pct = 0
pwm_write_percent(pwm_act, 0)
set_rgb(0, 0, 255) # error azul (opcional)
estado = "ERR"
# 2) OLED refresco rapido
if time.ticks_diff(now, last_ui_ms) >= UI_PERIOD_MS:
last_ui_ms = now
oled.fill(0)
oled.text("SP:{:>4.1f}C".format(SETPOINT_C), 0, 0)
oled.text("OK:{:>4.1f}-{:>4.1f}".format(MIN_OK, MAX_OK), 0, 12)
if temp_c is None or hum is None:
oled.text("T: --.-C", 0, 28)
oled.text("H: --- %", 0, 40)
else:
oled.text("T:{:>5.1f}C".format(temp_c), 0, 28)
oled.text("H:{:>5d}%".format(int(hum)), 0, 40)
oled.text("ACT:{:>3d}% {}".format(act_pct, estado), 0, 54)
oled.show()
time.sleep_ms(20)