Manual del proyecto · Proyecto 09
Cerradura Automatizada
Diseño, construcción y programación de una cerradura domiciliaria automática que no necesita llave: primero se abre y se cierra con dos pulsadores y avisa su estado con dos LED (verde/rojo); luego suma un teclado matricial 4x4 para abrirla con una clave y un buzzer que confirma si el código es correcto; por último, se monitorea a distancia por Internet de las Cosas (IoT).
- Dificultad:
- Nivel educativo:
- Secundario. Ciclo Básico.
Contenido del manual
- 01 Materiales y equipamiento
- 02 Abrir y cerrar la cerradura con dos pulsadores Nivel inicial
- 03 Abrir la cerradura con una clave Nivel intermedio
- 04 Monitorear la cerradura con Internet de las Cosas Nivel avanzado
Materiales y equipamiento
Insumos
| Imagen | Insumo | Cantidad | Plataforma |
|---|---|---|---|
| | Servomotor SG5010 Mueve el mecanismo (piñón y cremallera) que traba y destraba la cerradura. En esta reedición gira a 90° para abrir y a 0° para cerrar, el mismo criterio en los tres niveles. En el UNO se controla con la librería Servo; en el ESP32, con ESP32Servo. Se alimenta con 5 V y GND común con la placa. | 1 | Todas |
| | Pulsadores Solo en el nivel inicial: uno abre y otro cierra la cerradura. En el nivel intermedio los reemplaza el teclado. Se conectan entre el pin y GND, usando la resistencia interna de pull-up de la placa (no necesitan resistencia externa). | 2 | Todas |
| | Teclado matricial 4x4 de membrana Desde el nivel intermedio: permite ingresar una clave de 4 dígitos. Sus 16 teclas se leen con solo 8 pines (4 filas + 4 columnas) mediante la librería Keypad. | 1 | Todas |
| | LED verde de 5 mm difuso Indica que la cerradura está ABIERTA. La pata larga (positiva) va al pin de la placa a través de una resistencia de 220 Ω. | 1 | Todas |
| | LED rojo de 5 mm difuso Indica que la cerradura está CERRADA (estado inicial al encender el sistema). | 1 | Todas |
| | Buzzer (zumbador) Desde el nivel intermedio: suena un pitido agudo cuando la clave es correcta y un doble pitido grave cuando es incorrecta. Se maneja con la función tone(). | 1 | Todas |
| | Resistencia de 220 Ω Dos limitan la corriente de los LED (verde y rojo). El original lista cinco; las restantes quedan de repuesto. | 5 | Todas |
| | Cable dupont macho-hembra | 20 | Todas |
| | Protoboard | 1 | Todas |
| Filamento para impresora 3D (PLA) Para imprimir las piezas del mecanismo de la cerradura (piñón, cremallera y soportes). | 1 | Todas | |
| | Arduino UNO R3 El original de 2019 sumaba, para el nivel avanzado, un módulo IoT OBLOQ que hoy está discontinuado. Con UNO, el nivel IoT se sigue de manera conceptual y la práctica se realiza con la variante ESP32. | 1 | Arduino UNO R3 |
| | Cable USB tipo B | 1 | Arduino UNO R3 |
| | Placa ESP32 DevKit v1 Reemplaza al conjunto Arduino UNO + módulo OBLOQ del original: el ESP32 trae WiFi integrado, necesario para el nivel avanzado (IoT). Trabaja a 3,3 V (no 5 V como el UNO) y maneja el servo con la librería ESP32Servo. | 1 | ESP32 DevKit |
| | Cable micro USB (con línea de datos) | 1 | ESP32 DevKit |
| | Fuente de 9 V 1 A (plug centro positivo, 5,5 x 2,1 mm) (opcional) Para alimentar el sistema sin la computadora. El ESP32 se alimenta por USB con una fuente de 5 V. | 1 | Arduino UNO R3 |
Equipamiento del taller
- Computadora
- Impresora 3D
- Soldador
- Estaño
- Alicate
- Pinza de punta
- Pinza brusela
Plano de conexionado en protoboard
Deslizá horizontalmente para ver el plano completo
Deslizá horizontalmente para ver el plano completo
| Desde | Hacia | Color de cable |
|---|---|---|
| Arduino UNO · 5V | Bus + de la protoboard | rojo |
| Arduino UNO · GND | Bus − de la protoboard | negro |
| Servo de la cerradura · VCC | Bus + de la protoboard | rojo |
| Servo de la cerradura · GND | Bus − de la protoboard | negro |
| Servo de la cerradura · señal | Arduino UNO · 10 | azul |
| LED verde · ánodo (+) | Arduino UNO · 11 | verde |
| LED verde · cátodo (−) | Resistencia 220 Ω (verde) | negro |
| Resistencia 220 Ω (verde) | Bus − de la protoboard | negro |
| LED rojo · ánodo (+) | Arduino UNO · 13 | rojo |
| LED rojo · cátodo (−) | Resistencia 220 Ω (rojo) | negro |
| Resistencia 220 Ω (rojo) | Bus − de la protoboard | negro |
| Teclado matricial 4x4 · filas | Arduino UNO · 9, 8, 7, 6 | amarillo |
| Teclado matricial 4x4 · columnas | Arduino UNO · 5, 4, 3, 2 | amarillo |
| Buzzer · (+) | Arduino UNO · 12 | violeta |
| Buzzer · (−) | Bus − de la protoboard | negro |
| Desde | Hacia | Color de cable |
|---|---|---|
| ESP32 DevKit v1 · 5V (VIN) | Bus + de la protoboard | rojo |
| ESP32 DevKit v1 · GND | Bus − de la protoboard | negro |
| Servo de la cerradura · VCC | Bus + de la protoboard | rojo |
| Servo de la cerradura · GND | Bus − de la protoboard | negro |
| Servo de la cerradura · señal | ESP32 DevKit v1 · GPIO13 | azul |
| LED verde · ánodo (+) | ESP32 DevKit v1 · GPIO25 | verde |
| LED verde · cátodo (−) | Resistencia 220 Ω (verde) | negro |
| Resistencia 220 Ω (verde) | Bus − de la protoboard | negro |
| LED rojo · ánodo (+) | ESP32 DevKit v1 · GPIO26 | rojo |
| LED rojo · cátodo (−) | Resistencia 220 Ω (rojo) | negro |
| Resistencia 220 Ω (rojo) | Bus − de la protoboard | negro |
| Teclado matricial 4x4 · filas | ESP32 DevKit v1 · GPIO16, 17, 18, 19 | amarillo |
| Teclado matricial 4x4 · columnas | ESP32 DevKit v1 · GPIO21, 22, 23, 27 | amarillo |
| Buzzer · (+) | ESP32 DevKit v1 · GPIO14 | violeta |
| Buzzer · (−) | Bus − de la protoboard | negro |
Abrir y cerrar la cerradura con dos pulsadores
Montar el servomotor y los pulsadores en la protoboard
El proyecto arranca por el circuito que controla el movimiento de la cerradura. El corazón es un servomotorServomotorMotor que se ubica en un ángulo específico (los comunes, de 0° a 180°) y se mantiene ahí. Se programa indicando el ángulo deseado; tiene tres cables: alimentación (VCC, GND) y señal.: un motor con un sistema de control que le permite ubicarse en un ángulo preciso (entre 0° y 180°) y quedarse ahí. Ese giro es el que, más adelante, va a mover el mecanismo que traba y destraba la puerta.
Sumamos también los dos pulsadoresPulsadorInterruptor que normalmente está abierto y, mientras se lo mantiene presionado, cierra el circuito entre sus patas. Se usa como botón para que el usuario interactúe con el dispositivo, o como sensor de contacto (fin de carrera). —ABRIR y CERRAR— sobre la protoboardProtoboardPlaca experimental para el prototipado rápido de circuitos: sus orificios permiten insertar y conectar componentes sin soldadura. Las líneas superior e inferior recorren la placa de punta a punta y suelen usarse para la alimentación.. Un pulsador está normalmente abierto y, mientras se lo mantiene apretado, cierra el circuito entre sus patas: así la placa detecta que lo presionamos.
En el Arduino UNO seguimos el esquema unificado de este proyecto: la señal del servo va al pin 10, el pulsador ABRIR al pin 2 y el pulsador CERRAR al pin 3. La alimentación del servo se toma de 5V y GND. Estos pines son los mismos en los tres niveles: al avanzar, el circuito no se rehace, solo se le suman componentes.
En el ESP32 la señal del servo va al GPIO 13, el pulsador ABRIR al GPIO 32 y el
CERRAR al GPIO 33 (GPIO de entrada seguros). El ESP32 trabaja a 3,3 V, pero el servo
se alimenta con 5 V compartiendo GND con la placa. Además, el servo en el ESP32 se maneja
con la librería ESP32Servo (la Servo.h clásica del UNO no funciona en esta placa).
Programar el movimiento del servomotor
Ahora escribimos la lógica de apertura y cierre. La programación la hacemos directamente en el
lenguaje de Arduino (C++), con el Arduino IDE en su versión 2.x, que se descarga gratis de
arduino.cc/en/software. Un programa de Arduino se llama
sketch y tiene dos bloques: setup(), que se ejecuta una sola vez al encender, y loop(),
que se repite después. Todo lo que va después de // es un comentario: no afecta al programa,
sirve para leerlo mejor.
La idea es simple: en cada vuelta de loop() el programa mira los pulsadores. Si está presionado
ABRIR, lleva el servo a la posición abierta; si está presionado CERRAR, lo lleva a la
cerrada. Al servo se le ordena un ángulo con cerradura.write(...). En esta reedición
unificamos los ángulos para todos los niveles: abierta = 90° y cerrada = 0°.
Un detalle sobre cómo leemos los botones: los conectamos con la resistencia interna de
pull-up de la placa (INPUT_PULLUP). Con eso, el pin lee ALTO en reposo y BAJO cuando
el botón está presionado. Por eso en el código preguntamos if (digitalRead(...) == LOW). Es la
forma moderna y con menos componentes que el “leer al aire” del material original.
Subir el código a la placa
Para subir el programa a la placa, conectala a la computadora con el cable USB, elegí en el Arduino IDE 2.x la placa y el puertoPuerto COMPuerto de comunicaciones por el cual el sistema operativo se comunica con un dispositivo externo, como la placa. Se asigna automáticamente al conectarla por USB y puede cambiar de número; en el IDE hay que indicar cuál usar. (en el IDE 2.x se eligen juntos en el menú desplegable de la barra superior) y apretá el botón Subir (la flecha →). Al terminar, aparece el mensaje de carga completada en la barra inferior.
Para el Arduino UNO: usá el cable USB tipo B, elegí la placa Arduino UNO y el puerto que se
agregó al conectarla (en Windows es un puerto COM numerado; en Linux suele ser /dev/ttyACM0 o
similar). La librería Servo ya viene incluida con el IDE, no hay que instalar nada.
Para el ESP32 hay dos pasos previos que se hacen una sola vez: instalar el soporte de placas ESP32 desde el gestor de placas (buscá “esp32” e instalá el paquete de Espressif) y, desde el Gestor de bibliotecas, la librería ESP32Servo. Si el puerto no aparece, instalá el driver del conversor USB-serie de tu placa (CP210x o CH340). Después elegí la placa ESP32 Dev Module y el puerto. Si al subir el IDE queda en “Connecting…”, mantené apretado el botón BOOT.
Conectar los LED indicadores
Sumamos ahora los dos LEDLEDComponente electrónico tipo diodo que emite luz. Tiene polaridad: la pata larga es la positiva y la corta (o el lado plano del encapsulado) la negativa. Conectado al revés no se daña, pero no enciende. que muestran el estado de la cerradura: el verde para abierta y el rojo para cerrada. El LED tiene polaridad: la pata larga es la positiva y va al pin de la placa; la pata corta (negativa) va a masa a través de una resistenciaResistenciaComponente que se opone al paso de la corriente eléctrica. Su valor se mide en ohm (Ω). Se usa para determinar la cantidad de corriente de una rama del circuito, por ejemplo para evitar que un LED se queme por exceso de corriente. de 220 Ω, que limita la corriente para que no se dañe.
En el Arduino UNO, el LED verde va al pin 11 y el LED rojo al pin 13 (las
constantes PIN_LED_VERDE y PIN_LED_ROJO del código). El pin 13 tiene además el LED integrado
de la placa, útil para ver el estado aun sin el LED externo.
En el ESP32, el LED verde va al GPIO 25 y el LED rojo al GPIO 26. Como el ESP32 trabaja a 3,3 V, la resistencia de 220 Ω es más que suficiente.
Programar los LED indicadores
Completamos el programa para que, además de mover el servo, encienda el LED que corresponde a cada estado. La regla es: cuando la cerradura queda abierta, se enciende el verde y se apaga el rojo; cuando queda cerrada, se enciende el rojo y se apaga el verde. El sistema arranca cerrado (LED rojo), su estado seguro por defecto.
Fijate en el código cómo las dos acciones —“abrir” y “cerrar”— quedan agrupadas en funciones
(abrirPuerta() y cerrarPuerta()): cada una mueve el servo y actualiza los dos LED de una vez.
Así el programa se lee como lo que hace.
Ver el código completo · cerradura-pulsador.ino
// Cerradura automatizada — Nivel Inicial (Arduino UNO).
//
// Dos pulsadores controlan el servomotor que traba/destraba la cerradura:
// - Pulsador ABRIR -> el servo va a la posicion "abierta" + LED verde.
// - Pulsador CERRAR -> el servo va a la posicion "cerrada" + LED rojo.
// Los LED indican de un vistazo el estado de la puerta (verde = abierta,
// rojo = cerrada), tal como pide la situacion-problema del abuelo de Josefina.
//
// Plataforma: Arduino UNO. Proyecto de BAJA TENSION (servo 5 V, LED,
// pulsadores): no hay 220 V ni agua, no lleva advertencias electricas.
//
// Nivel inicial: se permite delay() (fiel a la progresion original); el
// antirrebote se resuelve con una espera corta. El nivel avanzado pasa a
// millis() sin bloquear.
//
// Basado en: Fragmentos de codigo del Nivel Inicial (Pasos 2 y 5) — Cerradura
// Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios respecto del original (ver README):
// (1) PINES UNIFICADOS: el original ataba el servo al pin 0 (rompe USB/serial)
// y usaba pines de LED distintos por nivel. Aca el servo va al pin 10 y
// los LED a 11/13 — el MISMO esquema en los tres niveles.
// (2) Angulos del servo unificados (ABIERTA=90, CERRADA=0) para que el mismo
// criterio valga en inicial, intermedio y avanzado.
// (3) Pulsadores con INPUT_PULLUP (activo en LOW): el original leia entradas
// "al aire"; el pull-up interno evita lecturas erraticas sin resistencias.
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (esquema UNIFICADO del contrato) ---
const int PIN_SERVO = 10; // senal del servomotor de la cerradura
const int PIN_LED_VERDE = 11; // encendido = cerradura ABIERTA
const int PIN_LED_ROJO = 13; // encendido = cerradura CERRADA
// En el nivel inicial NO hay teclado, asi que los botones ocupan dos pines
// libres (2 y 3). En intermedio, el teclado matricial los reemplaza.
const int PIN_BOTON_ABRIR = 2;
const int PIN_BOTON_CERRAR = 3;
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// Antirrebote simple del pulsador (ms).
const unsigned long ANTIRREBOTE_MS = 200;
Servo cerradura;
void abrirPuerta() {
cerradura.write(ANGULO_ABIERTA);
digitalWrite(PIN_LED_VERDE, HIGH);
digitalWrite(PIN_LED_ROJO, LOW);
}
void cerrarPuerta() {
cerradura.write(ANGULO_CERRADA);
digitalWrite(PIN_LED_VERDE, LOW);
digitalWrite(PIN_LED_ROJO, HIGH);
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
// Con INPUT_PULLUP el pulsador lee HIGH en reposo y LOW al presionarse.
pinMode(PIN_BOTON_ABRIR, INPUT_PULLUP);
pinMode(PIN_BOTON_CERRAR, INPUT_PULLUP);
cerradura.attach(PIN_SERVO);
// Estado inicial: CERRADA (LED rojo). Es el estado seguro por defecto de
// una cerradura.
cerrarPuerta();
}
void loop() {
// Pulsador presionado = LOW (por el pull-up interno).
if (digitalRead(PIN_BOTON_ABRIR) == LOW) {
abrirPuerta();
delay(ANTIRREBOTE_MS);
}
if (digitalRead(PIN_BOTON_CERRAR) == LOW) {
cerrarPuerta();
delay(ANTIRREBOTE_MS);
}
} // Cerradura automatizada — Nivel Inicial (ESP32 DevKit v1).
//
// Dos pulsadores controlan el servomotor que traba/destraba la cerradura:
// - Pulsador ABRIR -> el servo va a la posicion "abierta" + LED verde.
// - Pulsador CERRAR -> el servo va a la posicion "cerrada" + LED rojo.
// Los LED indican de un vistazo el estado (verde = abierta, rojo = cerrada).
//
// Plataforma: ESP32 (DevKit v1). El servo se maneja con la libreria
// ESP32Servo (la Servo.h clasica no funciona en ESP32). Nota electrica: el
// ESP32 trabaja a 3,3 V (el UNO a 5 V); alimentar el servo desde 5 V con GND
// comun. Proyecto de BAJA TENSION: sin 220 V ni agua.
//
// Nivel inicial: se permite delay() (fiel a la progresion original).
//
// Basado en: Fragmentos de codigo del Nivel Inicial (Pasos 2 y 5) — Cerradura
// Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): pines GPIO seguros del contrato; angulos del servo
// unificados (ABIERTA=90, CERRADA=0); pulsadores con INPUT_PULLUP.
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (GPIO seguros del contrato) ---
const int PIN_SERVO = 13; // ESP32Servo
const int PIN_LED_VERDE = 25; // encendido = cerradura ABIERTA
const int PIN_LED_ROJO = 26; // encendido = cerradura CERRADA
// En el nivel inicial no hay teclado: los botones ocupan dos GPIO libres de
// entrada (32 y 33). En intermedio los reemplaza el teclado matricial.
const int PIN_BOTON_ABRIR = 32;
const int PIN_BOTON_CERRAR = 33;
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// Antirrebote simple del pulsador (ms).
const unsigned long ANTIRREBOTE_MS = 200;
Servo cerradura;
void abrirPuerta() {
cerradura.write(ANGULO_ABIERTA);
digitalWrite(PIN_LED_VERDE, HIGH);
digitalWrite(PIN_LED_ROJO, LOW);
}
void cerrarPuerta() {
cerradura.write(ANGULO_CERRADA);
digitalWrite(PIN_LED_VERDE, LOW);
digitalWrite(PIN_LED_ROJO, HIGH);
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
// Con INPUT_PULLUP el pulsador lee HIGH en reposo y LOW al presionarse.
pinMode(PIN_BOTON_ABRIR, INPUT_PULLUP);
pinMode(PIN_BOTON_CERRAR, INPUT_PULLUP);
cerradura.attach(PIN_SERVO);
// Estado inicial: CERRADA (LED rojo), estado seguro por defecto.
cerrarPuerta();
}
void loop() {
// Pulsador presionado = LOW (por el pull-up interno).
if (digitalRead(PIN_BOTON_ABRIR) == LOW) {
abrirPuerta();
delay(ANTIRREBOTE_MS);
}
if (digitalRead(PIN_BOTON_CERRAR) == LOW) {
cerrarPuerta();
delay(ANTIRREBOTE_MS);
}
} Podés ver el comportamiento —los botones moviendo el servo y los LED cambiando de estado— en el simulador, antes de armar el circuito real:
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.
Verificá
Con el programa cargado, presioná ABRIR: el servo tiene que girar a la posición abierta y el LED verde encenderse (el rojo se apaga). Presioná CERRAR: el servo vuelve a la posición cerrada y se enciende el LED rojo. Al encender el sistema, debería arrancar en cerrado (rojo). Si un pulsador no responde, revisá que esté conectado entre el pin y GND; si un LED no enciende, revisá su polaridad (pata larga al pin).
Comenzar a armar la maqueta
Con el circuito probado, decidimos si cerramos el proyecto en este nivel o si avanzamos al intermedio para sumarle el teclado. Si vamos a dejar la maqueta armada, conviene pasar de la protoboard a un montaje más firme: soldar los cables a los componentes para que las conexiones no se suelten cuando el mecanismo se mueva.
Para el aula
Aunque el grupo piense avanzar al nivel intermedio, no hace falta soldar todavía: el teclado y el buzzer se suman sobre el mismo circuito. Conviene soldar recién cuando el sistema completo esté probado en protoboard. Este paso es un buen momento para planificar la maqueta: dónde van el teclado, los LED y la manija respecto de la puerta.
Imprimir las piezas 3D
El mecanismo de la cerradura se fabrica con piezas impresas en 3D. La idea es un piñón-cremallera: el servo hace girar un piñón (un engranaje) que empuja una cremallera (una barra dentada), y esa barra es la que se corre para trabar o destrabar la puerta, como un pasador.
Estas piezas forman parte de los paquetes de fabricación del proyecto, que se descargan desde su ficha. Si querés modificar el modelo, sea cual sea el programa de diseño 3D que uses, tenés que exportar las piezas en formato STLFormato .stlFormato de archivo que contiene la forma de un objeto sólido 3D, sin color, texturas ni propiedades físicas. Es el más usado para intercambiar e imprimir modelos 3D; funciona como la 'exportación final' de un diseño, como un PDF de un documento de texto., el estándar para intercambiar e imprimir modelos sólidos.
Ensamblar el sistema mecánico con el servomotor
Con las piezas impresas, ensamblamos el mecanismo sobre el servomotor: el piñón se calza en el eje del servo y engrana con la cremallera, de modo que al girar el servo (a 90° o a 0°) la barra se corra y trabe o destrabe la cerradura.
Una vez armado, el sistema se fija a la puerta (o a una apertura similar) y allí se ubican los LED y los pulsadores, al alcance de quien la va a usar.
Un solo botón
Hoy hacen falta dos botones (abrir y cerrar). ¿Cómo harían para que un solo pulsador alterne el estado: si está cerrada la abre, y si está abierta la cierra? Pista: hace falta que el programa recuerde el estado actual en una variable. Es exactamente la idea que van a usar en el nivel intermedio, cuando la clave correcta alterne la cerradura.
Cierre del nivel
Al terminar este nivel, tu cerradura ya funciona: se abre y se cierra con dos pulsadores, el servo mueve el mecanismo impreso en 3D y los LED muestran el estado (verde = abierta, rojo = cerrada). En el camino armaste el circuito, subiste el programa desde el Arduino IDE 2.x y ensamblaste el mecanismo. Pero abrir con un botón es poco seguro: cualquiera que lo presione entra. En el nivel intermedio vamos a reemplazar los pulsadores por un teclado para exigir una clave, y sumaremos un buzzer que avise si el código es correcto o no.
Evidencia de cierre
Al terminar este nivel, esto tiene que estar funcionando:
- El mecanismo de la cerradura está impreso y montado sobre el servomotor, y el circuito armado en la protoboard: el servo, los dos pulsadores (ABRIR y CERRAR) y los dos LED (verde y rojo) con sus resistencias de 220 Ω.
- Sabés subir un programa desde el Arduino IDE 2.x: elegir la placa, elegir el puerto e instalar la librería del servo (Servo en el UNO, ESP32Servo en el ESP32).
- Al presionar ABRIR, el servo gira a 90° y se enciende el LED verde; al presionar CERRAR, gira a 0° y se enciende el LED rojo. El sistema arranca cerrado (LED rojo).
- Entendés que los pulsadores usan la resistencia interna de pull-up (presionado = BAJO) y que los pines y los ángulos del servo son los mismos en los tres niveles del proyecto.
Abrir la cerradura con una clave
Situación problema
Por un tema de seguridad, ahora Josefina quiere que la cerradura se abra con una clave y no simplemente oprimiendo un botón. Para eso incluye en el sistema un teclado que le permita establecer la clave e ingresarla cada vez. Además, va a sumar un dispositivo que suene cada vez que se ingrese un código, indicando si es correcto o incorrecto. El abuelo está de acuerdo, pero le pidió conservar los LED que señalizan si la puerta está abierta o cerrada, porque le resultaron muy útiles.
En esta segunda instancia se agregan al sistema dos dispositivos: un teclado de membrana, que permite establecer una clave para habilitar la apertura; y un zumbador (buzzer), que emite un sonido cuando el código es correcto y otro cuando es incorrecto.
Conectar el teclado matricial 4x4 y el buzzer
Sumamos un teclado matricial 4x4Teclado matricial (de membrana)Teclado cuyas teclas se organizan como una matriz de filas y columnas: cada tecla es el cruce de una fila con una columna. En el de membrana, dos láminas flexibles con pistas conductoras hacen contacto al presionar. Se lee con la librería Keypad. de membrana (16 teclas) y un buzzerBuzzer (zumbador)Dispositivo que genera sonido a una frecuencia determinada. Al recibir alimentación comienza a sonar; controlándola se pueden generar sonidos continuos o pulsos. Sirve como mecanismo de señalización o aviso.. El teclado reemplaza a los pulsadores del nivel inicial: en vez de dos botones sueltos, ahora tenemos las 16 teclas para ingresar una clave. El servo y los dos LED siguen conectados igual que antes (por eso unificamos los pines: el circuito no se rehace, solo se le suman el teclado y el buzzer).
Lo interesante del teclado es su diseño matricial. En lugar de un cable por tecla (que serían 16), las teclas se organizan como una matriz de 4 filas por 4 columnas: cada tecla es el cruce de una fila con una columna. Así, con solo 8 pines (4 filas + 4 columnas) la placa puede distinguir las 16 teclas: recorre filas y columnas y detecta en qué cruce se cerró el contacto.
En el Arduino UNO, el teclado usa las filas en los pines 9, 8, 7 y 6 y las columnas en los pines 5, 4, 3 y 2. El buzzer va al pin 12. El servo (pin 10) y los LED verde (11) y rojo (13) quedan donde estaban.
En el ESP32, el teclado usa las filas en los GPIO 16, 17, 18 y 19 y las columnas en los GPIO 21, 22, 23 y 27. El buzzer va al GPIO 14. El servo (GPIO 13) y los LED verde (GPIO 25) y rojo (GPIO 26) quedan donde estaban.
Incluir la librería Keypad
Leer un teclado matricial “a mano” —recorrer filas y columnas para ver qué cruce se cerró— es tedioso. Por suerte existe una librería que lo resuelve: Keypad. En el Arduino IDE 2.x abrimos el Gestor de bibliotecas (el ícono de los libritos en la barra lateral), buscamos “Keypad” (la de Mark Stanley y Alexander Brevig) y la instalamos.
Con la librería instalada, en el código describimos el teclado una sola vez: el mapa de teclas
(qué carácter tiene cada posición de la matriz) y los pines de filas y columnas. A partir de
ahí, con teclado.getKey() obtenemos directamente la tecla presionada, sin ocuparnos del escaneo.
Programar el teclado y la clave
Ahora la lógica de la clave. Primero definimos cuántos dígitos tiene y cuáles son: en este caso,
4 dígitos y la clave del original, 1 5 9 D. La guardamos en una lista (clave) y usamos
otra (tecleado) para ir juntando lo que la persona ingresa.
El programa acumula las teclas que se presionan. Cuando junta 4, las compara con la clave:
- Si coinciden: suena un pitido agudo (clave correcta) y la cerradura conmuta su estado —si estaba cerrada, abre; si estaba abierta, cierra—, moviendo el servo y actualizando los LED.
- Si no coinciden: suena un doble pitido grave (clave incorrecta) y la cerradura no se mueve. El sistema queda listo para un nuevo intento de 4 teclas.
Para el aviso sonoro usamos la función tone(pin, frecuencia, duración): un tono agudo
(1000 Hz) para el acierto y uno grave (400 Hz) para el error. Así los dos avisos se distinguen
de oído, algo que el simple encendido/apagado del buzzer del original no permitía.
Ver el código completo · cerradura-teclado.ino
// Cerradura automatizada — Nivel Intermedio (Arduino UNO).
//
// Se reemplazan los dos pulsadores por un TECLADO MATRICIAL 4x4 y se suma un
// ZUMBADOR (buzzer). El usuario tipea una clave de 4 digitos; si coincide con
// la clave guardada, la cerradura conmuta su estado (abre si estaba cerrada,
// cierra si estaba abierta) y suena un pitido de OK. Si la clave es incorrecta,
// suena un doble pitido de error y no se mueve la cerradura. Los LED siguen
// indicando el estado (verde = abierta, rojo = cerrada), como pidio el abuelo.
//
// Clave del original: '1' '5' '9' 'D'.
//
// Plataforma: Arduino UNO. Proyecto de BAJA TENSION (servo 5 V, teclado, LED,
// buzzer): sin 220 V ni agua.
//
// Nivel intermedio: se permite delay() (fiel al original) para los pitidos.
//
// Basado en: Fragmento de codigo del Nivel Intermedio (Paso 3) — Cerradura
// Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios respecto del original (ver README):
// (1) PINES UNIFICADOS: servo=10, LED verde=11, buzzer=12, LED rojo=13, y el
// teclado en filas {9,8,7,6} / columnas {5,4,3,2} — el MISMO esquema que
// inicial y avanzado (el original cambiaba pines entre niveles).
// (2) BUZZER con tone(): el original prendia/apagaba el buzzer con
// digitalWrite (un buzzer "activo"); tone() genera una frecuencia audible
// y sirve para cualquier buzzer, ademas de permitir distinguir el tono de
// OK del de error.
// (3) Angulos del servo unificados (ABIERTA=90, CERRADA=0). El sistema arranca
// CERRADO (LED rojo), estado seguro por defecto.
#include <Keypad.h>
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (esquema UNIFICADO del contrato) ---
const int PIN_SERVO = 10;
const int PIN_LED_VERDE = 11; // encendido = ABIERTA
const int PIN_BUZZER = 12;
const int PIN_LED_ROJO = 13; // encendido = CERRADA
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// --- Teclado matricial 4x4 ---
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {9, 8, 7, 6};
byte pinesColumnas[COLUMNAS] = {5, 4, 3, 2};
Keypad teclado = Keypad(makeKeymap(teclas), pinesFilas, pinesColumnas,
FILAS, COLUMNAS);
// --- Clave (la del original) y buffer de lo tecleado ---
const byte LARGO_CLAVE = 4;
char clave[LARGO_CLAVE] = {'1', '5', '9', 'D'};
char tecleado[LARGO_CLAVE] = {0, 0, 0, 0};
byte indice = 0;
// --- Buzzer: tonos distintos para OK y error ---
const unsigned int FREQ_OK = 1000; // Hz, pitido agudo = correcta
const unsigned int FREQ_ERROR = 400; // Hz, pitido grave = incorrecta
const unsigned long DURACION_BEEP_MS = 200;
const unsigned long PAUSA_BEEP_MS = 300;
// Estado de la puerta: true = abierta, false = cerrada.
bool puertaAbierta = false;
Servo cerradura;
void mostrarEstadoEnLeds() {
digitalWrite(PIN_LED_VERDE, puertaAbierta ? HIGH : LOW);
digitalWrite(PIN_LED_ROJO, puertaAbierta ? LOW : HIGH);
}
void beepCorrecta() {
tone(PIN_BUZZER, FREQ_OK, DURACION_BEEP_MS);
delay(DURACION_BEEP_MS);
noTone(PIN_BUZZER);
}
void beepIncorrecta() {
// Doble pitido grave.
for (byte i = 0; i < 2; i++) {
tone(PIN_BUZZER, FREQ_ERROR, DURACION_BEEP_MS);
delay(DURACION_BEEP_MS);
noTone(PIN_BUZZER);
delay(PAUSA_BEEP_MS);
}
}
// Compara lo tecleado con la clave guardada.
bool claveCorrecta() {
for (byte i = 0; i < LARGO_CLAVE; i++) {
if (tecleado[i] != clave[i]) {
return false;
}
}
return true;
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
cerradura.attach(PIN_SERVO);
// Arranca CERRADA (LED rojo).
puertaAbierta = false;
cerradura.write(ANGULO_CERRADA);
mostrarEstadoEnLeds();
}
void loop() {
char tecla = teclado.getKey();
if (!tecla) {
return; // no se presiono ninguna tecla en esta vuelta
}
// Guarda la tecla y avanza el indice.
tecleado[indice] = tecla;
indice++;
// Cuando se completan 4 digitos, se evalua la clave.
if (indice == LARGO_CLAVE) {
indice = 0;
if (claveCorrecta()) {
beepCorrecta();
// Conmuta el estado de la cerradura.
puertaAbierta = !puertaAbierta;
cerradura.write(puertaAbierta ? ANGULO_ABIERTA : ANGULO_CERRADA);
mostrarEstadoEnLeds();
} else {
beepIncorrecta(); // no toca la cerradura
}
}
} // Cerradura automatizada — Nivel Intermedio (ESP32 DevKit v1).
//
// Se reemplazan los dos pulsadores por un TECLADO MATRICIAL 4x4 y se suma un
// ZUMBADOR (buzzer). El usuario tipea una clave de 4 digitos; si coincide con
// la clave guardada, la cerradura conmuta su estado (abre si estaba cerrada,
// cierra si estaba abierta) y suena un pitido de OK. Si es incorrecta, suena
// un doble pitido de error y la cerradura no se mueve. Los LED indican el
// estado (verde = abierta, rojo = cerrada).
//
// Clave del original: '1' '5' '9' 'D'.
//
// Plataforma: ESP32 (DevKit v1). Servo con ESP32Servo; el buzzer usa tone(),
// disponible en el core de ESP32. Nota electrica: el ESP32 trabaja a 3,3 V
// (alimentar el servo desde 5 V con GND comun). Proyecto de BAJA TENSION.
//
// Nivel intermedio: se permite delay() (fiel al original) para los pitidos.
//
// Basado en: Fragmento de codigo del Nivel Intermedio (Paso 3) — Cerradura
// Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): pines GPIO seguros del contrato; buzzer con tone();
// angulos unificados (ABIERTA=90, CERRADA=0); arranque CERRADO (LED rojo).
#include <Keypad.h>
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (GPIO seguros del contrato) ---
const int PIN_SERVO = 13;
const int PIN_LED_VERDE = 25; // encendido = ABIERTA
const int PIN_LED_ROJO = 26; // encendido = CERRADA
const int PIN_BUZZER = 14;
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// --- Teclado matricial 4x4 ---
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {16, 17, 18, 19};
byte pinesColumnas[COLUMNAS] = {21, 22, 23, 27};
Keypad teclado = Keypad(makeKeymap(teclas), pinesFilas, pinesColumnas,
FILAS, COLUMNAS);
// --- Clave (la del original) y buffer de lo tecleado ---
const byte LARGO_CLAVE = 4;
char clave[LARGO_CLAVE] = {'1', '5', '9', 'D'};
char tecleado[LARGO_CLAVE] = {0, 0, 0, 0};
byte indice = 0;
// --- Buzzer: tonos distintos para OK y error ---
const unsigned int FREQ_OK = 1000; // Hz, pitido agudo = correcta
const unsigned int FREQ_ERROR = 400; // Hz, pitido grave = incorrecta
const unsigned long DURACION_BEEP_MS = 200;
const unsigned long PAUSA_BEEP_MS = 300;
// Estado de la puerta: true = abierta, false = cerrada.
bool puertaAbierta = false;
Servo cerradura;
void mostrarEstadoEnLeds() {
digitalWrite(PIN_LED_VERDE, puertaAbierta ? HIGH : LOW);
digitalWrite(PIN_LED_ROJO, puertaAbierta ? LOW : HIGH);
}
void beepCorrecta() {
tone(PIN_BUZZER, FREQ_OK, DURACION_BEEP_MS);
delay(DURACION_BEEP_MS);
noTone(PIN_BUZZER);
}
void beepIncorrecta() {
// Doble pitido grave.
for (byte i = 0; i < 2; i++) {
tone(PIN_BUZZER, FREQ_ERROR, DURACION_BEEP_MS);
delay(DURACION_BEEP_MS);
noTone(PIN_BUZZER);
delay(PAUSA_BEEP_MS);
}
}
// Compara lo tecleado con la clave guardada.
bool claveCorrecta() {
for (byte i = 0; i < LARGO_CLAVE; i++) {
if (tecleado[i] != clave[i]) {
return false;
}
}
return true;
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
cerradura.attach(PIN_SERVO);
// Arranca CERRADA (LED rojo).
puertaAbierta = false;
cerradura.write(ANGULO_CERRADA);
mostrarEstadoEnLeds();
}
void loop() {
char tecla = teclado.getKey();
if (!tecla) {
return; // no se presiono ninguna tecla en esta vuelta
}
tecleado[indice] = tecla;
indice++;
if (indice == LARGO_CLAVE) {
indice = 0;
if (claveCorrecta()) {
beepCorrecta();
puertaAbierta = !puertaAbierta;
cerradura.write(puertaAbierta ? ANGULO_ABIERTA : ANGULO_CERRADA);
mostrarEstadoEnLeds();
} else {
beepIncorrecta(); // no toca la cerradura
}
}
} Podés probar el ingreso de la clave —el pitido de acierto o de error y la cerradura conmutando— 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.
Para el aula
Acá aparece una idea central de programación: el sistema recuerda su estado en la variable
puertaAbierta, y la clave correcta lo alterna (puertaAbierta = !puertaAbierta). Es la misma
idea del desafío “un solo botón” del nivel inicial, ahora resuelta. Otra conversación rica: la
clave está escrita en el código. ¿Es seguro? ¿Qué pasa si alguien lee el programa? Es un buen
punto de partida para hablar de que una clave “quemada” en el firmware es apenas el primer paso, y
de por qué en sistemas reales las claves se guardan y comparan de otras maneras.
Verificá
Con el programa cargado, tipeá la clave 1 5 9 D: al presionar la cuarta tecla tiene que sonar un pitido agudo y la cerradura conmutar (el servo se mueve y cambian los LED). Probá ahora cualquier otra combinación de 4 teclas: tiene que sonar un doble pitido grave y la cerradura quedarse quieta. Si el teclado no responde, revisá el orden de los pines de filas y columnas; si el buzzer no suena, revisá que esté en el pin correcto.
Cierre del nivel
Al terminar este nivel, la cerradura dejó de abrirse con un botón cualquiera: ahora exige una clave de 4 dígitos que se ingresa por el teclado matricial 4x4, y un buzzer confirma con un tono si el código fue correcto o no. Viste cómo un teclado organiza 16 teclas en una matriz de filas y columnas para usar solo 8 pines, y cómo el programa recuerda el estado de la puerta para alternarlo. En el nivel avanzado vamos a que el abuelo pueda enterarse de la apertura o el cierre aunque no esté en la casa: sumaremos Internet de las Cosas para monitorear la cerradura a distancia.
Evidencia de cierre
Al terminar este nivel, esto tiene que estar funcionando:
- El teclado matricial 4x4 y el buzzer están conectados sobre el mismo circuito del nivel inicial (servo y LED): los pulsadores fueron reemplazados por el teclado.
- Instalaste la librería Keypad desde el Gestor de bibliotecas del Arduino IDE.
- Al tipear la clave correcta (1 5 9 D) suena un pitido agudo y la cerradura conmuta su estado (abre si estaba cerrada, cierra si estaba abierta), acompañada por los LED; con una clave incorrecta suena un doble pitido grave y la cerradura no se mueve.
Monitorear la cerradura con Internet de las Cosas
Situación problema
A Josefina se le ocurrió aumentar aún más la seguridad de la cerradura. Quiere que su abuelo pueda saber cuándo se abre o se cierra la puerta aunque no esté en la casa, para enterarse si alguien entra en su ausencia. Para lograrlo, decidió agregarle un sistema de Internet de las Cosas (IoT) que le permita monitorear esa información desde un dispositivo móvil.
En esta tercera instancia se programa el envío del estado de la cerradura —abierta o cerrada— a un dispositivo móvil, a través de Internet de las Cosas (IoT), para poder monitorearla a distancia.
Introducción a Internet de las Cosas (IoT)
Internet de las Cosas (en inglés Internet of Things, abreviado IoTIoT (Internet de las Cosas)Interconexión digital de objetos cotidianos con internet. Permite, por ejemplo, enviar los datos de un sensor a una central y visualizarlos desde un dispositivo móvil. Requiere un dispositivo capaz de conectarse a internet y un servidor que reciba y aloje los datos.) es un concepto que refiere a la interconexión digital de objetos cotidianos con internet. Por ejemplo, enviar el estado de una cerradura a una central donde se recopile la información, para verla desde el celular sin estar en la casa.
Para incorporar IoT a nuestro proyecto necesitamos:
- Un dispositivo capaz de conectarse a internet.
- Un servidor que reciba y aloje los datos.
Lo resolveremos con el WiFi integrado de la placa ESP32ESP32Placa con un microcontrolador de Espressif que trae WiFi (y Bluetooth) integrados. Se programa desde el Arduino IDE igual que un Arduino, con pines llamados GPIO. En este proyecto reemplaza al conjunto Arduino UNO + módulo OBLOQ para el nivel IoT. y con los servidores de Adafruit IOAdafruit IOPlataforma online de uso gratuito, orientada al uso educativo, que centraliza y gestiona la información que reportan los dispositivos IoT. Permite crear feeds y paneles de control para monitorear los datos desde cualquier dispositivo..
El material original de 2019 resolvía este punto agregando al Arduino UNO un módulo externo llamado OBLOQ, un adaptador WiFi-serie de DFRobot hoy discontinuado (igual que su biblioteca ObloqAdafruit). Como el UNO clásico no tiene conectividad propia, en esta reedición la práctica de IoT se realiza con la variante ESP32: podés seguir este nivel de manera conceptual con tu UNO (el paso 3, sobre código no bloqueante, funciona perfectamente en él) y cambiar al ESP32 con el selector de plataforma para la parte práctica de conexión, más simple en el circuito.
El ESP32 cumple los dos roles a la vez: es el microcontrolador que ya está leyendo el teclado, moviendo el servo y manejando los LED y el buzzer, y además trae radio WiFi integrada. No hace falta ningún módulo adicional ni cableado extra: la conexión a internet se resuelve por completo desde el código.
Crear un panel de control en Adafruit IO
Creamos un panel de control en Adafruit IOAdafruit IOPlataforma online de uso gratuito, orientada al uso educativo, que centraliza y gestiona la información que reportan los dispositivos IoT. Permite crear feeds y paneles de control para monitorear los datos desde cualquier dispositivo., una plataforma online con uso gratuito orientada a lo educativo, que centraliza la información que reportan los dispositivos. Empezamos con una cuenta en io.adafruit.com.
El trabajo tiene dos partes: crear el feed donde se publicarán los datos y armar el panel que los muestre. Un feedFeedFuente de datos en la que se puede publicar y a la que uno puede suscribirse. Permite enviar datos para que se almacenen en el tiempo, y también leerlos recibiendo las actualizaciones de quienes publican allí. es una fuente de datos en la que uno puede publicar y a la que puede suscribirse. Desde la sección Feeds creamos uno, con este nombre exacto (sin tildes, porque forma parte de la dirección con la que se publica):
cerradura: recibirá el estado de la cerradura, con la convención 1 = abierta, 0 = cerrada.
Luego, desde Dashboards, creamos un panel y le agregamos un bloque del tipo Indicator
(indicador): un círculo que cambia de color según el valor del feed. Lo configuramos para que esté
en verde cuando cerradura sea 1 (abierta) y en rojo cuando sea distinto de 1 (cerrada),
replicando a distancia los mismos colores de los LED del dispositivo.
Por último, necesitamos las credenciales para que el dispositivo pueda publicar: en Adafruit IO, tocá el ícono de la llave (API Key). Vas a ver el nombre de usuario (Username) y la clave (Active Key): anotalos, porque los copiaremos en el código del paso 5. La clave es secreta: cualquiera que la tenga puede publicar en tus feeds.
Verificá
Para probar el panel sin la placa, entrá al feed cerradura y agregale valores a mano (Add
Data): con 1 el indicador tiene que ponerse verde y con 0, rojo. Si no reacciona, revisá
que el bloque esté asociado al feed correcto.
Programar sin código bloqueante
Antes de sumar IoT hay que hacer una aclaración sobre la función delay() que venimos usando
para los pitidos del buzzer. delay() genera un código
bloqueanteCódigo bloqueanteCódigo que detiene todo el procesamiento hasta que se cumple una condición, como delay(), que deja al programa 'colgado' durante el tiempo indicado. En IoT es conflictivo: impide atender tareas simultáneas como la publicación de datos y la conexión a internet.: mientras el programa está dentro de esa función, se detiene todo el
procesamiento hasta que se cumple el tiempo. El programa queda “colgado” ese rato.
Al usar IoT, eso es un problema: mientras el procesamiento está detenido, el sistema no puede
atender otras tareas que corren en simultáneo, como mantener viva la conexión a internet y
publicar. Si el buzzer estuviera sonando con delay(), durante ese tiempo el dispositivo estaría
“sordo” a la red.
La alternativa no bloqueante es la función millis(), que devuelve el tiempo transcurrido
desde que arrancó el sistema, como un cronómetro que se consulta sin detener nada. Con eso armamos
una pequeña máquina de estadosMáquina de estadosForma de organizar un programa donde una variable guarda el estado actual del sistema y, en cada vuelta del loop, solo se ejecuta lo que corresponde a ese estado. El semáforo no bloqueante usa ocho estados que se recorren según el tiempo transcurrido. para el buzzer: en lugar de
“sonar y esperar”, el programa programa los pitidos (cuántos y de qué tono) y, en cada vuelta
de loop(), actualizarBuzzer() dispara el siguiente cuando corresponde. El loop() nunca se
frena: atiende el teclado y el buzzer a la vez.
Ver el código completo · cerradura-no-bloqueante.ino
// Cerradura automatizada — Nivel Avanzado (Arduino UNO), NO BLOQUEANTE.
//
// Misma cerradura por teclado del nivel intermedio, pero SIN delay(): los
// pitidos del buzzer se manejan con una pequena maquina de estados basada en
// millis(). Asi loop() nunca se detiene y el sistema queda listo para tareas
// concurrentes (como el monitoreo IoT). El teclado 4x4 lee la clave, la
// cerradura conmuta con clave correcta y los LED indican el estado
// (verde = abierta, rojo = cerrada).
//
// Clave del original: '1' '5' '9' 'D'.
//
// >>> IoT EN UNO: CONCEPTUAL <<<
// El nivel avanzado del original agrega IoT, pero su implementacion en UNO es
// IRREALIZABLE: usa el modulo OBLOQ sobre SoftwareSerial cableado a los pines
// 0/1 en el codigo (que son el USB/serial de programacion: lo rompe) y a los
// pines 8/9 en el texto (que CHOCAN con dos filas del teclado 4x4). Ademas el
// OBLOQ y su libreria ObloqAdafruit estan discontinuados. Por eso, en UNO el
// IoT queda como EXPLICACION CONCEPTUAL y la practica se hace con el ESP32
// (WiFi nativo), en esp32/nivel-avanzado/cerradura-iot.ino. Este sketch de UNO
// deja el sistema NO BLOQUEANTE justamente como base sobre la que el IoT
// podria montarse — la misma progresion didactica que ensena el original.
//
// Plataforma: Arduino UNO. Proyecto de BAJA TENSION: sin 220 V ni agua.
//
// Basado en: Fragmentos de codigo de los Niveles Intermedio y Avanzado —
// Cerradura Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): pines unificados; buzzer con tone(); pitidos NO
// bloqueantes con millis(); IoT conceptual (se resuelve en ESP32).
#include <Keypad.h>
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (esquema UNIFICADO del contrato) ---
const int PIN_SERVO = 10;
const int PIN_LED_VERDE = 11; // encendido = ABIERTA
const int PIN_BUZZER = 12;
const int PIN_LED_ROJO = 13; // encendido = CERRADA
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// --- Teclado matricial 4x4 ---
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {9, 8, 7, 6};
byte pinesColumnas[COLUMNAS] = {5, 4, 3, 2};
Keypad teclado = Keypad(makeKeymap(teclas), pinesFilas, pinesColumnas,
FILAS, COLUMNAS);
// --- Clave (la del original) y buffer de lo tecleado ---
const byte LARGO_CLAVE = 4;
char clave[LARGO_CLAVE] = {'1', '5', '9', 'D'};
char tecleado[LARGO_CLAVE] = {0, 0, 0, 0};
byte indice = 0;
// --- Buzzer NO bloqueante ---
// tone(pin, freq, duracion) ya suena sin bloquear (lo maneja un timer). Para
// encadenar varios pitidos con pausas, programamos cada uno con millis() en
// vez de usar delay().
const unsigned int FREQ_OK = 1000; // Hz, agudo = correcta
const unsigned int FREQ_ERROR = 400; // Hz, grave = incorrecta
const unsigned long DURACION_BEEP_MS = 200;
const unsigned long PERIODO_BEEP_MS = 500; // 200 ms de tono + 300 ms de pausa
byte beepsPendientes = 0; // cuantos pitidos quedan por emitir
unsigned int freqBeep = 0; // frecuencia del pitido en curso
unsigned long proximoBeepMs = 0; // cuando disparar el proximo pitido
// Estado de la puerta: true = abierta, false = cerrada.
bool puertaAbierta = false;
Servo cerradura;
void mostrarEstadoEnLeds() {
digitalWrite(PIN_LED_VERDE, puertaAbierta ? HIGH : LOW);
digitalWrite(PIN_LED_ROJO, puertaAbierta ? LOW : HIGH);
}
// Programa una serie de pitidos (los emite actualizarBuzzer sin bloquear).
void programarBeeps(byte cantidad, unsigned int frecuencia) {
beepsPendientes = cantidad;
freqBeep = frecuencia;
proximoBeepMs = millis(); // el primero suena ya
}
// Debe llamarse en cada vuelta de loop(): dispara el proximo pitido cuando
// corresponde, sin detener el programa.
void actualizarBuzzer() {
if (beepsPendientes == 0) {
return;
}
if (millis() >= proximoBeepMs) {
tone(PIN_BUZZER, freqBeep, DURACION_BEEP_MS); // no bloqueante
beepsPendientes--;
proximoBeepMs = millis() + PERIODO_BEEP_MS;
}
}
bool claveCorrecta() {
for (byte i = 0; i < LARGO_CLAVE; i++) {
if (tecleado[i] != clave[i]) {
return false;
}
}
return true;
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
cerradura.attach(PIN_SERVO);
puertaAbierta = false; // arranca CERRADA (LED rojo)
cerradura.write(ANGULO_CERRADA);
mostrarEstadoEnLeds();
}
void loop() {
// 1) Atender el teclado (no bloquea: getKey devuelve 0 si no hay tecla).
char tecla = teclado.getKey();
if (tecla) {
tecleado[indice] = tecla;
indice++;
if (indice == LARGO_CLAVE) {
indice = 0;
if (claveCorrecta()) {
programarBeeps(1, FREQ_OK); // un pitido de OK
puertaAbierta = !puertaAbierta; // conmuta la cerradura
cerradura.write(puertaAbierta ? ANGULO_ABIERTA : ANGULO_CERRADA);
mostrarEstadoEnLeds();
} else {
programarBeeps(2, FREQ_ERROR); // doble pitido de error
}
}
}
// 2) Atender el buzzer sin bloquear.
actualizarBuzzer();
// 3) (Aca iria el mantenimiento IoT: en UNO es conceptual — ver ESP32.)
} // Cerradura automatizada — Nivel Avanzado (ESP32 DevKit v1), NO BLOQUEANTE.
//
// Misma cerradura por teclado del nivel intermedio, pero SIN delay(): los
// pitidos del buzzer se manejan con una pequena maquina de estados basada en
// millis(). Asi loop() nunca se detiene y el sistema queda listo para sumar el
// monitoreo IoT (ver cerradura-iot.ino en esta misma carpeta). El teclado 4x4
// lee la clave, la cerradura conmuta con clave correcta y los LED indican el
// estado (verde = abierta, rojo = cerrada).
//
// Clave del original: '1' '5' '9' 'D'.
//
// Este sketch es el paso previo al IoT: deja toda la logica NO bloqueante para
// que agregar WiFi + MQTT (cerradura-iot.ino) no desatienda el teclado.
//
// Plataforma: ESP32 (DevKit v1). Servo con ESP32Servo; buzzer con tone().
// Nota electrica: 3,3 V (alimentar el servo desde 5 V con GND comun).
// Proyecto de BAJA TENSION: sin 220 V ni agua.
//
// Basado en: Fragmentos de codigo de los Niveles Intermedio y Avanzado —
// Cerradura Automatica, Saberes Digitales (INET/EDUCAR 2019).
// Desvios (ver README): pines GPIO seguros del contrato; buzzer con tone();
// pitidos NO bloqueantes con millis().
#include <Keypad.h>
#if defined(ESP32)
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
// --- Pines (GPIO seguros del contrato) ---
const int PIN_SERVO = 13;
const int PIN_LED_VERDE = 25; // encendido = ABIERTA
const int PIN_LED_ROJO = 26; // encendido = CERRADA
const int PIN_BUZZER = 14;
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// --- Teclado matricial 4x4 ---
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {16, 17, 18, 19};
byte pinesColumnas[COLUMNAS] = {21, 22, 23, 27};
Keypad teclado = Keypad(makeKeymap(teclas), pinesFilas, pinesColumnas,
FILAS, COLUMNAS);
// --- Clave (la del original) y buffer de lo tecleado ---
const byte LARGO_CLAVE = 4;
char clave[LARGO_CLAVE] = {'1', '5', '9', 'D'};
char tecleado[LARGO_CLAVE] = {0, 0, 0, 0};
byte indice = 0;
// --- Buzzer NO bloqueante ---
const unsigned int FREQ_OK = 1000; // Hz, agudo = correcta
const unsigned int FREQ_ERROR = 400; // Hz, grave = incorrecta
const unsigned long DURACION_BEEP_MS = 200;
const unsigned long PERIODO_BEEP_MS = 500; // 200 ms de tono + 300 ms de pausa
byte beepsPendientes = 0;
unsigned int freqBeep = 0;
unsigned long proximoBeepMs = 0;
// Estado de la puerta: true = abierta, false = cerrada.
bool puertaAbierta = false;
Servo cerradura;
void mostrarEstadoEnLeds() {
digitalWrite(PIN_LED_VERDE, puertaAbierta ? HIGH : LOW);
digitalWrite(PIN_LED_ROJO, puertaAbierta ? LOW : HIGH);
}
void programarBeeps(byte cantidad, unsigned int frecuencia) {
beepsPendientes = cantidad;
freqBeep = frecuencia;
proximoBeepMs = millis();
}
void actualizarBuzzer() {
if (beepsPendientes == 0) {
return;
}
if (millis() >= proximoBeepMs) {
tone(PIN_BUZZER, freqBeep, DURACION_BEEP_MS); // no bloqueante
beepsPendientes--;
proximoBeepMs = millis() + PERIODO_BEEP_MS;
}
}
bool claveCorrecta() {
for (byte i = 0; i < LARGO_CLAVE; i++) {
if (tecleado[i] != clave[i]) {
return false;
}
}
return true;
}
void setup() {
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
cerradura.attach(PIN_SERVO);
puertaAbierta = false; // arranca CERRADA (LED rojo)
cerradura.write(ANGULO_CERRADA);
mostrarEstadoEnLeds();
}
void loop() {
char tecla = teclado.getKey();
if (tecla) {
tecleado[indice] = tecla;
indice++;
if (indice == LARGO_CLAVE) {
indice = 0;
if (claveCorrecta()) {
programarBeeps(1, FREQ_OK);
puertaAbierta = !puertaAbierta;
cerradura.write(puertaAbierta ? ANGULO_ABIERTA : ANGULO_CERRADA);
mostrarEstadoEnLeds();
} else {
programarBeeps(2, FREQ_ERROR);
}
}
}
actualizarBuzzer();
} Para el aula
Este es el concepto más transferible del nivel: la diferencia entre esperar deteniendo el
mundo (delay()) y consultar un reloj sin detenerlo (millis()). Funciona muy bien
teatralizarlo: un estudiante “bloqueante” que se congela mientras cuenta y no puede responder
nada, contra uno “no bloqueante” que sigue atendiendo y cada tanto mira el reloj de la pared.
Cuando el grupo predice cuál de los dos puede, además, “atender el teléfono de internet”, el paso
5 se explica solo. Si el curso ya hizo Invernadero o Riego, este millis() es un reencuentro.
Conectar el dispositivo a internet
El material original conectaba al Arduino UNO un módulo OBLOQ por dos pines serie. Además de
estar discontinuado, esa implementación en el UNO era irrealizable: el código lo cableaba
por SoftwareSerial a los pines 0 y 1, que son el USB/serial de programación (lo dejarían sin
poder subirse código); y el texto lo mandaba a los pines 8 y 9, que chocan con dos filas del
teclado 4x4 de este proyecto. En cualquiera de las dos formas, el sistema no puede funcionar.
Por eso, la parte práctica de conexión se hace con la variante ESP32: cambiá la plataforma con el selector. Todo lo conceptual —qué se publica, por qué el código no debe bloquear— ya lo tenés resuelto con tu UNO en los pasos anteriores.
En el ESP32 no hay que conectar ningún módulo ni cable extra: la conexión se resuelve por software, con el WiFi integrado de la placa. Solo necesitamos instalar la librería que habla con Adafruit IO: desde el Gestor de bibliotecas del Arduino IDE, buscá e instalá la “Adafruit MQTT Library”. Es la que reemplaza a la biblioteca ObloqAdafruit del original (discontinuada junto con su módulo) y publica los datos por el protocolo MQTT.
Programar IoT
Con el panel listo y el código ya libre de bloqueos, falta el último eslabón: que el sistema publique el estado de la cerradura en Adafruit IO.
Esta parte práctica corresponde a la variante ESP32 (el Arduino UNO clásico no tiene WiFi, y el OBLOQ del original era irrealizable en este circuito). Cambiá la plataforma con el selector para ver el código y las instrucciones. Todo lo conceptual —qué se publica en el feed, el porqué del código no bloqueante— ya lo tenés de los pasos anteriores.
El programa final integra tres cosas que ya conocés: el control no bloqueante del paso 3, la conexión WiFi nativa del ESP32 y la publicación en Adafruit IO por MQTT. Antes de subirlo, completá cuatro datos al comienzo del código: el nombre (SSID) y la contraseña de tu red WiFi, y el usuario y la clave que copiaste de Adafruit IO en el paso 2.
La publicación es por evento: cada vez que la puerta cambia de estado (una clave correcta
la abre o la cierra), el programa publica en el feed cerradura el nuevo valor —1 si quedó
abierta, 0 si quedó cerrada—. No publica en cada vuelta del loop(), sino solo en el instante
del cambio, así que el cupo gratuito de Adafruit IO (30 publicaciones por minuto) queda de sobra.
Por esto es importante que el código no sea bloqueante: si el programa quedara colgado en un
delay(), la conexión no podría atenderse. Y como bonus del mismo diseño: si internet se cae, la
cerradura sigue funcionando localmente, y al recuperar la conexión el panel se pone al día con el
estado actual.
Ver el código completo · cerradura-iot.ino
Nota: Este código está disponible para ESP32. Cambiá la plataforma con el selector del encabezado para verlo y practicarlo.
// Cerradura automatizada — Nivel Avanzado + IoT (ESP32 DevKit v1).
//
// Toma la cerradura por teclado NO bloqueante (cerradura-no-bloqueante.ino) y
// le suma monitoreo remoto: cada vez que la puerta CAMBIA de estado, publica
// en el feed "cerradura" de Adafruit IO:
//
// 1 = ABIERTA 0 = CERRADA
//
// Asi el abuelo puede ver desde el celular si la puerta se abrio o cerro, tal
// como plantea la situacion-problema del nivel avanzado. La publicacion es
// EVENT-DRIVEN: solo se publica en el momento del cambio (no en cada vuelta),
// por lo que se respeta de sobra el cupo gratuito de Adafruit IO (30/min).
//
// Clave del original: '1' '5' '9' 'D'.
//
// Plataforma: ESP32 (DevKit v1) — WiFi integrado. Servo con ESP32Servo; buzzer
// con tone(). Nota electrica: 3,3 V (alimentar el servo desde 5 V, GND comun).
// Proyecto de BAJA TENSION: sin 220 V ni agua.
//
// Basado en: Fragmento de codigo del Nivel Avanzado (Paso 6, publicacion del
// estado de la cerradura) — Cerradura Automatica, Saberes Digitales
// (INET/EDUCAR 2019).
// Desvios / modernizaciones (ver README):
// (1) OBLOQ IRREALIZABLE EN EL ORIGINAL: usaba SoftwareSerial(0,1) — que son
// el USB/serial de programacion del UNO (lo rompe) — y el texto lo cableaba
// a (8,9), que CHOCAN con dos filas del teclado 4x4. El OBLOQ y su libreria
// ObloqAdafruit ademas estan discontinuados. Se reemplaza por el WiFi
// NATIVO del ESP32 + MQTT contra io.adafruit.com (libreria "Adafruit MQTT
// Library"). El IoT de este proyecto solo es realizable en ESP32.
// (2) Publicacion EVENT-DRIVEN (solo al cambiar el estado), no en cada vuelta.
// (3) Reconexion MQTT NO bloqueante (reintento cada 5 s), sin frenar teclado
// ni buzzer.
// (4) Pines GPIO seguros del contrato; buzzer con tone(); pitidos NO
// bloqueantes con millis().
#include <WiFi.h>
#include <Keypad.h>
#include <ESP32Servo.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
// ---------------------------------------------------------------------------
// Credenciales: reemplazar por las propias. El usuario y la key de Adafruit se
// copian desde io.adafruit.com con el icono de la "llave" (como en la guia).
// ---------------------------------------------------------------------------
#define WIFI_SSID "TU_SSID"
#define WIFI_PASSWORD "TU_PASSWORD"
#define IO_USERNAME "TU_USUARIO_ADAFRUIT"
#define IO_KEY "TU_KEY_ADAFRUIT"
// Servidor MQTT de Adafruit IO.
const char SERVIDOR_MQTT[] = "io.adafruit.com";
const int PUERTO_MQTT = 1883;
// --- Pines (GPIO seguros del contrato) ---
const int PIN_SERVO = 13;
const int PIN_LED_VERDE = 25; // encendido = ABIERTA
const int PIN_LED_ROJO = 26; // encendido = CERRADA
const int PIN_BUZZER = 14;
// Angulos del servo (unificados en los tres niveles).
const int ANGULO_ABIERTA = 90;
const int ANGULO_CERRADA = 0;
// --- Teclado matricial 4x4 ---
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {16, 17, 18, 19};
byte pinesColumnas[COLUMNAS] = {21, 22, 23, 27};
Keypad teclado = Keypad(makeKeymap(teclas), pinesFilas, pinesColumnas,
FILAS, COLUMNAS);
// --- Clave (la del original) y buffer de lo tecleado ---
const byte LARGO_CLAVE = 4;
char clave[LARGO_CLAVE] = {'1', '5', '9', 'D'};
char tecleado[LARGO_CLAVE] = {0, 0, 0, 0};
byte indice = 0;
// --- Buzzer NO bloqueante ---
const unsigned int FREQ_OK = 1000; // Hz, agudo = correcta
const unsigned int FREQ_ERROR = 400; // Hz, grave = incorrecta
const unsigned long DURACION_BEEP_MS = 200;
const unsigned long PERIODO_BEEP_MS = 500;
byte beepsPendientes = 0;
unsigned int freqBeep = 0;
unsigned long proximoBeepMs = 0;
// Estado de la puerta: true = abierta, false = cerrada.
bool puertaAbierta = false;
// --- WiFi + MQTT (Adafruit IO) ---
WiFiClient clienteWifi;
Adafruit_MQTT_Client mqtt(&clienteWifi, SERVIDOR_MQTT, PUERTO_MQTT,
IO_USERNAME, IO_KEY);
Adafruit_MQTT_Publish feedCerradura(&mqtt, IO_USERNAME "/feeds/cerradura");
// Reintento de conexion MQTT sin bloquear.
const unsigned long INTERVALO_REINTENTO_MQTT_MS = 5000;
unsigned long millisUltimoIntentoMqtt = 0;
// Ultimo valor publicado con exito (-1 fuerza la primera publicacion).
int cerraduraPublicada = -1;
Servo cerradura;
void mostrarEstadoEnLeds() {
digitalWrite(PIN_LED_VERDE, puertaAbierta ? HIGH : LOW);
digitalWrite(PIN_LED_ROJO, puertaAbierta ? LOW : HIGH);
}
void programarBeeps(byte cantidad, unsigned int frecuencia) {
beepsPendientes = cantidad;
freqBeep = frecuencia;
proximoBeepMs = millis();
}
void actualizarBuzzer() {
if (beepsPendientes == 0) {
return;
}
if (millis() >= proximoBeepMs) {
tone(PIN_BUZZER, freqBeep, DURACION_BEEP_MS); // no bloqueante
beepsPendientes--;
proximoBeepMs = millis() + PERIODO_BEEP_MS;
}
}
bool claveCorrecta() {
for (byte i = 0; i < LARGO_CLAVE; i++) {
if (tecleado[i] != clave[i]) {
return false;
}
}
return true;
}
// Mantiene viva la conexion MQTT sin bloquear: si esta caida, reintenta como
// mucho una vez cada 5 s (mismo patron que el resto de la coleccion).
void mantenerConexionMqtt() {
if (mqtt.connected()) {
return;
}
unsigned long millisActual = millis();
if (millisActual - millisUltimoIntentoMqtt < INTERVALO_REINTENTO_MQTT_MS) {
return;
}
millisUltimoIntentoMqtt = millisActual;
Serial.print("Conectando a Adafruit IO... ");
int8_t resultado = mqtt.connect(); // 0 = conexion exitosa
if (resultado == 0) {
Serial.println("conectado.");
} else {
Serial.println(mqtt.connectErrorString(resultado));
mqtt.disconnect();
}
}
// Publica el estado SOLO cuando cambia (event-driven). Si no hay conexion o el
// publish falla, no se actualiza el registro: se reintenta en el proximo cambio
// o cuando vuelva la conexion.
void publicarEstadoSiCambio() {
int valor = puertaAbierta ? 1 : 0;
if (valor == cerraduraPublicada) {
return; // sin cambios: nada que publicar
}
if (!mqtt.connected()) {
return; // sin internet: se publicara cuando reconecte
}
if (feedCerradura.publish((int32_t) valor)) {
cerraduraPublicada = valor;
}
}
void setup() {
Serial.begin(115200);
pinMode(PIN_LED_VERDE, OUTPUT);
pinMode(PIN_LED_ROJO, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
cerradura.attach(PIN_SERVO);
puertaAbierta = false; // arranca CERRADA (LED rojo)
cerradura.write(ANGULO_CERRADA);
mostrarEstadoEnLeds();
// Conexion WiFi. Bloquear aca con delay() es aceptable: el ciclo de teclado
// todavia no arranco. Una vez en loop(), nada bloquea.
Serial.print("Conectando a la red WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("WiFi conectado, IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
// 1) Teclado (no bloquea).
char tecla = teclado.getKey();
if (tecla) {
tecleado[indice] = tecla;
indice++;
if (indice == LARGO_CLAVE) {
indice = 0;
if (claveCorrecta()) {
programarBeeps(1, FREQ_OK);
puertaAbierta = !puertaAbierta;
cerradura.write(puertaAbierta ? ANGULO_ABIERTA : ANGULO_CERRADA);
mostrarEstadoEnLeds();
publicarEstadoSiCambio(); // publica el cambio al instante
} else {
programarBeeps(2, FREQ_ERROR);
}
}
}
// 2) Buzzer no bloqueante.
actualizarBuzzer();
// 3) Mantenimiento IoT en cada vuelta. Si el publish previo fallo por estar
// sin conexion, al reconectar se reintenta el estado actual.
mantenerConexionMqtt();
publicarEstadoSiCambio();
} Podés ver el sistema completo —la clave abriendo la cerradura y el panel reflejando el cambio de estado— 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.
De monitorear a comandar
Hoy el sistema avisa a distancia si la cerradura está abierta o cerrada, pero abrirla sigue siendo presencial (hay que tipear la clave en el teclado). Piensen en grupo: ¿cómo harían para abrir la cerradura desde el celular, a través del mismo Adafruit IO? Pista: en vez de solo publicar en un feed, el dispositivo tendría que suscribirse a uno y reaccionar a lo que llega. ¿Y qué nuevos problemas de seguridad aparecen cuando una puerta se puede abrir por internet?
Verificá
Con el programa cargado y el ESP32 conectado a tu red WiFi, abrí el panel de Adafruit IO en el
celular o la computadora. Tipeá la clave correcta en el teclado: el indicador cerradura tiene
que ponerse verde (1, abierta). Volvé a tipearla: la cerradura cierra y el indicador pasa a
rojo (0), con una pequeña demora de red. Como se publica por cambios, entre apertura y
apertura no llega nada nuevo: es lo esperado. Si el indicador no cambia, revisá las credenciales y
el nombre de la red.
Cierre del nivel
Al terminar este nivel, la cerradura dejó de ser solo local: es un dispositivo IoT. El programa
funciona con millis(), sin ningún delay() que lo cuelgue; sabés explicar por qué eso es
imprescindible cuando hay que atender internet en simultáneo; y el panel de Adafruit IO muestra,
desde cualquier lado, si la puerta está abierta o cerrada. Con esto queda completo el recorrido del
proyecto. Si el grupo quiere seguir, en la ficha están las extensiones sugeridas: sumar un
lector RFID para abrir con tarjeta, o dar el salto de monitorear a comandar la cerradura de
forma remota.
Evidencia de cierre
Al terminar este nivel, esto tiene que estar funcionando:
- El sistema funciona con código no bloqueante: los pitidos del buzzer se manejan con millis() en una pequeña máquina de estados y no queda ningún delay() que detenga el loop entre teclas.
- Podés explicar con tus palabras por qué el código bloqueante es un problema cuando el dispositivo tiene que atender internet en simultáneo.
- El panel de Adafruit IO muestra el estado de la cerradura (feed cerradura, 1 = abierta / 0 = cerrada) publicado por el dispositivo cada vez que la puerta cambia de estado (variante ESP32).