Encender el radiador según la temperatura
Recorrido del nivel
Esa barra de arriba te dice en qué paso estás. Tocala para ver el índice completo y saltar a cualquier paso.
Conectar el sensor de temperatura y humedad (DHT11)
Sumamos el sensor que mide el ambiente: el sensor DHT11Sensor DHT11Sensor digital que mide temperatura y humedad del aire en un mismo integrado. Digitaliza la lectura internamente y la entrega por un pin digital (no es analógico: no va a A0). Necesita una librería para leerlo y no admite más de una lectura por segundo., que registra la temperatura y la humedad del aire.
Un dato importante: el DHT11 es un sensor digital. A diferencia de un sensor analógico (que se conecta a una entrada A0–A5), el DHT11 tiene electrónica interna que digitaliza la medición y la transmite a la placa como un paquete de datos por un pin digital. Por eso no va a A0: va a un pin digital, y para leerlo hace falta una librería que gestione esa comunicación.
El DHT11 tiene tres conexiones útiles: alimentación (VCC y GND) y datos.
En el Arduino UNO seguimos el esquema original: el pin de datos del DHT11 va al pin digital 2
(la constante PIN_DHT del código), VCC al pin 5V y GND a GND. El relé sigue en el
pin 3 (PIN_RELE), como en el nivel inicial.
En el ESP32 la conexión es equivalente, con un cuidado propio de la placa: el pin de datos del
DHT11 va al GPIO 4 (la constante PIN_DHT del código) y la alimentación se toma de 3,3 V
(no de 5 V) y GND. El DHT11 funciona sin problemas a 3,3 V. El relé sigue en el GPIO 25
(PIN_RELE).
Para poder leer el sensor necesitamos instalar su librería. En el Arduino IDE 2.x abrimos el
Gestor de bibliotecas (el ícono de los libritos en la barra lateral), buscamos “DHT sensor
library” de Adafruit y la instalamos; el IDE ofrecerá instalar también su dependencia
“Adafruit Unified Sensor”: aceptá, porque el sensor no funciona sin ella. Esta librería
reemplaza a la vieja DHT.h genérica del original, que ya no se consigue igual.
Encender el radiador según la temperatura
Ahora el sistema deja de encender por reloj y pasa a comportarse como un termostato: enciende el radiador cuando hace frío y lo apaga cuando la habitación llegó a la temperatura deseada.
La lógica es un if/else que compara la temperatura medida contra una temperatura objetivo.
Como es un sistema de calefacción, la comparación va al revés que la intuición del “supera el
umbral”: el radiador se enciende cuando la temperatura medida está por debajo del objetivo, y
se apaga cuando lo alcanza o supera. Por ahora usamos un objetivo fijo, en la constante
UMBRAL_TEMP_C, con el valor del original: 22 °C (en el paso 4 lo haremos ajustable con el
potenciómetro).
Entre medición y medición, el programa espera el tiempo de la constante INTERVALO_MEDICION_MS,
que está en 2 segundos, porque el DHT11 no admite más de una lectura por segundo (y la
librería de Adafruit, además, guarda la última lectura durante 2 segundos). En este nivel usamos
delay(), que es lo más simple; en el nivel avanzado lo cambiaremos por código no bloqueante.
Conectar y probar el display LCD
Para ver la temperatura sumamos un display LCDDisplay LCDPantalla de cristal líquido que muestra texto en filas y columnas; el clásico es de 16 columnas por 2 filas. Se puede conectar en modo paralelo (muchos pines) o por I2C (solo dos cables, SDA y SCL). No representa bien las tildes ni la letra Ñ. de 16 columnas por 2 filas. Acá aparece la primera diferencia real entre las dos plataformas de este proyecto, y vale la pena entenderla, porque es muy didáctica: el display se conecta de una manera distinta en cada una.
En el Arduino UNO usamos el LCD Keypad Shield del original, un shieldShieldPlaca modular que se monta encimada sobre una placa Arduino, encastrando en sus pines, para agregarle funciones (pantalla, sensores, comunicación). Suele diseñarse para un modelo concreto: el LCD Keypad Shield tiene el formato del Arduino UNO y no encaja en un ESP32. que tiene el formato de la placa y se apila directamente encima del UNO, encastrando en sus pines: no hay que cablear nada en la protoboard. Al apilarlo, el display queda conectado en modo paralelo, usando seis pines de datos (los pines 8, 9, 4, 5, 6 y 7). Es importante no forzar las piezas: todos los pines deben coincidir. Como el shield ocupa varios pines, el DHT11, el relé y el potenciómetro se cablean a los pines que quedan libres.
Se maneja con la librería LiquidCrystal, que ya viene incluida con el Arduino IDE (no hay
que instalar nada). En el código, el display se declara indicándole sus seis pines:
LiquidCrystal lcd(8, 9, 4, 5, 6, 7).
El LCD Keypad Shield tiene el formato del Arduino UNO y no se enchufa en un ESP32. Por eso, en el ESP32 usamos un LCD 1602 con módulo I2C: la misma pantalla de 16x2, pero con una plaquita (el módulo I2C) pegada atrás que reduce el cableado a solo dos cables de señal. Conectamos VCC a 5V (o 3,3 V según el módulo), GND a GND, SDA al GPIO 21 y SCL al GPIO 22 (los pines I2C por defecto del ESP32).
Se maneja con la librería LiquidCrystal_I2C, que se instala desde el Gestor de
bibliotecas del IDE (buscá “LiquidCrystal_I2C”). En el código, el display se declara con su
dirección I2C —típicamente 0x27— y su tamaño: LiquidCrystal_I2C lcd(0x27, 16, 2). Si al
probar la pantalla no muestra nada, es posible que tu módulo use la dirección 0x3F: probá con ese
valor.
Para familiarizarnos con el display, conviene un primer programa de prueba que escriba un
saludo: tras inicializar la pantalla, con lcd.print("Buen dia") aparece ese texto en la
primera fila. Un detalle a tener siempre presente: el LCD no representa bien las tildes ni la
letra Ñ, así que los textos se escriben sin ellas (por eso “Buen dia”, y más adelante “Temp Act”
y “Temp Set”).
Si en el UNO el saludo no se lee (pantalla en blanco o todo encendido), ajustá el contraste con el potenciómetro azul (RP1) que está en la esquina superior izquierda del shield, usando un destornillador chico.
Fijar la temperatura objetivo con un potenciómetro
Hasta ahora la temperatura objetivo era fija (22 °C). Ahora la hacemos ajustable a mano con un potenciómetroPotenciómetroResistor cuyo valor de resistencia se ajusta de forma manual con una perilla. Conectando sus extremos a VCC y GND, la pata central entrega un nivel de tensión que representa la posición de la perilla y se lee por una entrada analógica del Arduino.: girando la perilla, Martina elige a qué temperatura quiere la habitación, y el display le muestra tanto la temperatura actual como la que está configurando.
El potenciómetro es analógico, así que va a una entrada analógica, y la placa lo lee como un
número que hay que convertir a una escala de temperatura. Como en el original, mapeamos el
recorrido de la perilla a un rango de 0 a 30 °C: con la perilla al mínimo, el objetivo es 0 °C;
al máximo, 30 °C. Ese valor convertido reemplaza a la constante fija del paso 2 y queda en la
variable tempObjetivo, que es la que el if/else compara contra la temperatura medida.
En el Arduino UNO el potenciómetro va al pin analógico A1 (la constante PIN_POT del código).
La placa lo lee con analogRead() como un número de 0 a 1023 (0 V a 5 V), y el código lo
convierte a la escala de 0 a 30 °C con map().
En el ESP32 el potenciómetro va al GPIO 34 (la constante PIN_POT del código), un pin de solo
entrada, ideal para leer un sensor o una perilla. Ojo con un detalle real: el conversor del ESP32
es de mayor resolución, así que analogRead() devuelve un número de 0 a 4095 (no de 0 a
1023 como en el UNO). El código lo contempla en el map() usando el rango correcto de la placa;
por lo demás, la lógica es idéntica.
Aprovechamos las dos filas del display: en la primera mostramos la temperatura actual
(Temp Act) y en la segunda la temperatura objetivo (Temp Set) que fija el potenciómetro.
Ver el código completo · termostato-lcd.ino
// Termostato con display: el sensor DHT11 mide la temperatura del ambiente y
// un potenciometro fija la temperatura OBJETIVO. Si hace frio (temperatura
// por debajo del objetivo) el rele enciende el radiador; si ya se alcanzo el
// objetivo, lo apaga. El LCD 16x2 muestra la temperatura actual y la objetivo.
//
// !! SEGURIDAD 220V !! El radiador es una carga de 220V conmutada por el
// rele. En el aula, usar una carga de BAJA TENSION (o el LED de prueba del
// pin 13) para probar sin riesgo. La conexion real a 220V queda reservada al
// docente o a una instalacion supervisada, y el rele con su cableado de 220V
// van SIEMPRE fuera de la protoboard (ver README).
//
// Plataforma: Arduino UNO.
// Conexion (la del esquema original): dato del DHT11 -> pin 2; senal del rele
// -> pin 3; potenciometro (cursor) -> A1; display LCD Keypad Shield en modo
// paralelo por los pines 8, 9, 4, 5, 6, 7. El shield se apila sobre el UNO;
// su preset RP1 ajusta el contraste si no se lee el texto.
//
// Nota del original: en el LCD no usar tildes ni la letra Ñ.
//
// Basado en: Fragmento de codigo del Nivel Intermedio (Paso 14, codigo
// completo) - Sistema de Calefaccion Automatico, Saberes Digitales
// (INET/EDUCAR 2019)
// Modernizacion: (1) constantes nombradas en vez del C de mBlock3. (2)
// Libreria "DHT sensor library" de Adafruit (instalar desde el Library
// Manager; arrastra "Adafruit Unified Sensor"), con dht.begin() en setup().
// (3) La temperatura se lee UNA vez por vuelta a una variable y ese valor se
// usa para el rele y para el display. (4) Se ignora una lectura fallida del
// DHT11 (isnan) para no accionar el radiador con un valor no numerico.
#include "DHT.h"
#include <LiquidCrystal.h>
// Pin de datos del sensor y tipo de sensor (DHT11).
const int PIN_DHT = 2;
#define DHTTYPE DHT11
// Senal del modulo rele del radiador.
const int PIN_RELE = 3;
// Cursor del potenciometro que fija la temperatura objetivo (entrada analogica).
const int PIN_POTENCIOMETRO = A1;
// El modulo rele es ACTIVO-ALTO: HIGH enciende el radiador. Si tu modulo es
// activo-bajo, invertí estas dos constantes (ENCENDIDO = LOW / APAGADO = HIGH).
const int RELE_ENCENDIDO = HIGH;
const int RELE_APAGADO = LOW;
// El potenciometro fija la temperatura objetivo. La entrada analogica del UNO
// lee 0..1023 (10 bits); la mapeamos a 0..30 grados con una regla de tres,
// tal como el original (potenciometro en 0 -> 0 grados; en el maximo -> 30).
const float TEMP_OBJETIVO_MAX_C = 30.0;
const int ADC_MAX = 1023;
// El DHT11 no admite mas de una lectura por segundo; medimos cada 2 s (el
// original pedia 1 s, pero 2 s es el valor honesto para este sensor y la
// libreria de Adafruit ademas cachea la lectura).
const unsigned long INTERVALO_MEDICION_MS = 2000;
// Objeto del sensor y display paralelo (RS, E, D4, D5, D6, D7).
DHT dht(PIN_DHT, DHTTYPE);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() {
pinMode(PIN_RELE, OUTPUT);
digitalWrite(PIN_RELE, RELE_APAGADO); // arranca apagado
pinMode(PIN_POTENCIOMETRO, INPUT);
dht.begin();
lcd.begin(16, 2);
lcd.clear();
}
void loop() {
// Temperatura actual del ambiente (grados Celsius).
float temperatura = dht.readTemperature();
// Temperatura objetivo fijada a mano con el potenciometro (0..30 grados).
float objetivo = (float)analogRead(PIN_POTENCIOMETRO) * TEMP_OBJETIVO_MAX_C / ADC_MAX;
// Una lectura fallida del DHT11 devuelve "no numerico": la salteamos para
// no accionar el radiador con basura (el rele conserva su ultimo estado).
if (!isnan(temperatura)) {
// Termostato: si la temperatura esta por debajo del objetivo, hace frio y
// encendemos el radiador; si ya se alcanzo el objetivo, lo apagamos.
if (temperatura < objetivo) {
digitalWrite(PIN_RELE, RELE_ENCENDIDO);
} else {
digitalWrite(PIN_RELE, RELE_APAGADO);
}
}
// Fila 0: temperatura actual. Fila 1: temperatura objetivo. Sin tildes ni Ñ.
lcd.setCursor(0, 0);
lcd.print("Temp Act: ");
lcd.print(temperatura);
lcd.setCursor(0, 1);
lcd.print("Temp Set: ");
lcd.print(objetivo);
delay(INTERVALO_MEDICION_MS);
} // Termostato con display: el sensor DHT11 mide la temperatura del ambiente y
// un potenciometro fija la temperatura OBJETIVO. Si hace frio (temperatura
// por debajo del objetivo) el rele enciende el radiador; si ya se alcanzo el
// objetivo, lo apaga. El LCD 16x2 muestra la temperatura actual y la objetivo.
//
// !! SEGURIDAD 220V !! El radiador es una carga de 220V conmutada por el
// rele. En el aula, usar una carga de BAJA TENSION (o un LED de prueba) para
// probar sin riesgo. La conexion real a 220V queda reservada al docente o a
// una instalacion supervisada, y el rele con su cableado de 220V van SIEMPRE
// fuera de la protoboard (ver README).
//
// Plataforma: ESP32 (DevKit v1).
// Conexion: dato del DHT11 -> GPIO 4; senal del rele -> GPIO 25; cursor del
// potenciometro -> GPIO 34 (entrada del ADC1, solo entrada); display LCD 1602
// por I2C: SDA -> GPIO 21, SCL -> GPIO 22.
//
// ADAPTACION DE PLATAFORMA (ver README): el original usa un LCD Keypad Shield
// paralelo (6 pines: 8, 9, 4, 5, 6, 7) que NO entra en un ESP32. Aca usamos un
// LCD 1602 con modulo I2C: solo DOS cables de senal (SDA y SCL) y la libreria
// LiquidCrystal_I2C. La direccion I2C tipica es 0x27 (algunos son 0x3F: si la
// pantalla no muestra nada, probá 0x3F o escaneá el bus I2C).
//
// ADAPTACION DEL ADC (¡importante!): el ADC del ESP32 es de 12 bits
// (0..4095), no de 10 bits (0..1023) como el UNO. Por eso ADC_MAX = 4095 al
// mapear el potenciometro a 0..30 grados. GPIO 34 es solo-entrada y pertenece
// al ADC1 (el unico usable con WiFi encendido).
// Nota electrica: el ESP32 trabaja a 3,3 V; DHT11 y el modulo I2C funcionan a
// 3,3 V, y el potenciometro se alimenta entre 3,3 V y GND.
//
// Nota del original: en el LCD no usar tildes ni la letra Ñ.
//
// Librerias requeridas (Library Manager): "DHT sensor library" (Adafruit) +
// "Adafruit Unified Sensor", y "LiquidCrystal_I2C".
//
// Basado en: Fragmento de codigo del Nivel Intermedio (Paso 14, codigo
// completo) - Sistema de Calefaccion Automatico, Saberes Digitales
// (INET/EDUCAR 2019)
// Modernizacion: constantes nombradas; DHT de Adafruit con dht.begin(); LCD
// paralelo del original adaptado a I2C para ESP32; ADC de 12 bits; una sola
// lectura por vuelta reutilizada para el rele y el display; se ignora la
// lectura fallida del DHT11 (isnan).
#include "DHT.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Pin de datos del sensor y tipo de sensor (DHT11).
const int PIN_DHT = 4;
#define DHTTYPE DHT11
// Senal del modulo rele del radiador (GPIO seguro del contrato ESP32).
const int PIN_RELE = 25;
// Cursor del potenciometro que fija la temperatura objetivo (ADC1, GPIO 34).
const int PIN_POTENCIOMETRO = 34;
// El modulo rele es ACTIVO-ALTO: HIGH enciende el radiador. Si tu modulo es
// activo-bajo, invertí estas dos constantes (ENCENDIDO = LOW / APAGADO = HIGH).
const int RELE_ENCENDIDO = HIGH;
const int RELE_APAGADO = LOW;
// Direccion I2C y tamaño del display (16 columnas x 2 filas).
const uint8_t LCD_DIRECCION_I2C = 0x27;
const uint8_t LCD_COLUMNAS = 16;
const uint8_t LCD_FILAS = 2;
// El potenciometro fija la temperatura objetivo. El ADC del ESP32 lee 0..4095
// (12 bits); lo mapeamos a 0..30 grados con una regla de tres.
const float TEMP_OBJETIVO_MAX_C = 30.0;
const int ADC_MAX = 4095;
// El DHT11 no admite mas de una lectura por segundo; medimos cada 2 s.
const unsigned long INTERVALO_MEDICION_MS = 2000;
// Objeto del sensor y display por I2C.
DHT dht(PIN_DHT, DHTTYPE);
LiquidCrystal_I2C lcd(LCD_DIRECCION_I2C, LCD_COLUMNAS, LCD_FILAS);
void setup() {
pinMode(PIN_RELE, OUTPUT);
digitalWrite(PIN_RELE, RELE_APAGADO); // arranca apagado
dht.begin();
// Con I2C, primero arranca el bus y el display; backlight enciende la luz.
lcd.init();
lcd.backlight();
lcd.clear();
}
void loop() {
// Temperatura actual del ambiente (grados Celsius).
float temperatura = dht.readTemperature();
// Temperatura objetivo fijada a mano con el potenciometro (0..30 grados).
float objetivo = (float)analogRead(PIN_POTENCIOMETRO) * TEMP_OBJETIVO_MAX_C / ADC_MAX;
// Una lectura fallida del DHT11 devuelve "no numerico": la salteamos para
// no accionar el radiador con basura (el rele conserva su ultimo estado).
if (!isnan(temperatura)) {
// Termostato: si la temperatura esta por debajo del objetivo, hace frio y
// encendemos el radiador; si ya se alcanzo el objetivo, lo apagamos.
if (temperatura < objetivo) {
digitalWrite(PIN_RELE, RELE_ENCENDIDO);
} else {
digitalWrite(PIN_RELE, RELE_APAGADO);
}
}
// Fila 0: temperatura actual. Fila 1: temperatura objetivo. Sin tildes ni Ñ.
lcd.setCursor(0, 0);
lcd.print("Temp Act: ");
lcd.print(temperatura);
lcd.setCursor(0, 1);
lcd.print("Temp Set: ");
lcd.print(objetivo);
delay(INTERVALO_MEDICION_MS);
} Fijate, al comparar las dos plataformas con el selector, que el corazón del programa es el mismo: cambian solo la parte del display (qué librería se incluye y cómo se declara) y el rango del potenciómetro. La lógica del termostato es idéntica.
Podés ver el sistema completo —el sensor midiendo, la perilla fijando el objetivo y el radiador encendiéndose cuando hace frío— en el simulador:
Simulación del comportamiento
La misma máquina de estados del firmware, portada a TypeScript, maneja el
comportamiento del dispositivo. No se ejecuta el binario real: la ejecución
del .hex con avr8js está en el roadmap.
Simulación lista. Presioná «Iniciar» para arrancar la secuencia.
El traqueteo del relé
Discutan en grupo: si la temperatura de la habitación se queda justo en el valor objetivo, las
pequeñas variaciones de la medición (el DHT11 tiene ruido) pueden hacer que el relé encienda y
apague sin parar. ¿Cómo evitarían ese “traqueteo”? Pista: pensá en dejar una pequeña franja entre
la temperatura a la que enciende y la temperatura a la que apaga (lo que en el proyecto de tanques
se llamó histéresis). ¿Dónde iría ese margen en el if/else?
Verificá
Con el programa cargado, el display tiene que mostrar Temp Act con la temperatura medida en la
primera fila y Temp Set con la temperatura objetivo en la segunda. Girá la perilla del
potenciómetro: el valor de Temp Set tiene que cambiar entre 0 y 30 °C. Poné el objetivo por
encima de la temperatura ambiente: el radiador (LED del pin 13 o lámpara de baja tensión) tiene que
encenderse. Bajá el objetivo por debajo de la temperatura ambiente: tiene que apagarse. Si
la pantalla se ve en blanco, revisá el contraste (RP1 en el UNO) o la dirección I2C (0x27 o
0x3F en el ESP32).
Cierre del nivel
Al terminar este nivel, la calefacción de Martina dejó de encender por reloj: ahora mide la temperatura con el DHT11 y se comporta como un termostato, encendiendo el radiador solo cuando la habitación está por debajo de la temperatura que ella misma fija con el potenciómetro, y mostrando todo en el display. En el camino apareció una diferencia real y muy formativa entre las plataformas —el shield paralelo del UNO frente al LCD por I2C del ESP32, y el potenciómetro leído en 10 o en 12 bits—. En el nivel avanzado vamos a poder mirar todo esto a distancia: publicaremos la temperatura y la humedad por Internet de las Cosas para monitorear la calefacción desde el celular.
Evidencia de cierre
Al terminar este nivel, esto tiene que estar funcionando:
- El sensor DHT11 está conectado a su pin de datos y el sistema funciona como termostato: enciende el radiador cuando la temperatura medida está por debajo de la temperatura objetivo y lo apaga cuando la alcanza.
- El display muestra la temperatura actual y la temperatura objetivo, según la plataforma (LCD Keypad Shield en modo paralelo en el UNO, LCD 1602 por I2C en el ESP32), sin tildes ni Ñ.
- El potenciómetro fija a mano la temperatura objetivo (de 0 a 30 °C) y el display refleja ese valor al girar la perilla.