Proyecto
Proyecto: cerradura de cajón abierta desde el celular con ESP32-C3 Super Mini y servo
Paso a paso de una cerradura con servo SG90 y LED en la ESP32-C3 Super Mini, controlada desde el celular por la red Wi-Fi de la propia placa, con contraseña.
Por Prof. Rodrigo Faustino · Actualizado el · 11 min de lectura
Métricas de este artículo
- Palabras
- 2159
- Caracteres
- 11.961
- Tiempo de lectura
- ~11 min
Este proyecto junta dos cosas que a los alumnos les encantan: un motor que se mueve y el celular. Una pequeña cerradura hecha con un servo SG90 sujeta el cajón de un armario (o la tapa de una caja); el celular entra en la red Wi-Fi creada por la propia placa, abre una página y, con la contraseña correcta, toca Abrir. La placa gira el servo, enciende un LED verde y, pasado un tiempo, se cierra sola.
La placa es la ESP32-C3 Super Mini, lo bastante pequeña para quedar escondida dentro de la caja. Las piezas son las de la ruta de PWM: el servo, un LED y una resistencia. El programa usa lo que enseñan los artículos anteriores — el servidor web en la placa y el PWM del servo — y agrega lo que hace que un proyecto parezca terminado: estados, contraseña y tiempo.
Una palabra sobre seguridad, para acordar con el curso desde el comienzo: esto es una cerradura de juguete, ideal para la caja de un juego de pistas, el armario de materiales del aula o una alcancía. No sirve para proteger nada de valor: un destornillador abre la caja, y la contraseña en una página simple no resiste a alguien decidido. Aprovecha para discutir por qué las cerraduras electrónicas de verdad son más complicadas de lo que parecen.
Lo que vas a necesitar
- una ESP32-C3 Super Mini (ficha);
- un micro servo SG90 (ficha);
- un LED verde y una resistencia de 220 Ω (ficha del LED);
- protoboard y cables;
- para la cerradura: un trozo de palito de helado o de plástico sujeto al brazo del servo, que entra en un tope pegado al cajón; cinta doble faz o pegamento caliente;
- un celular con Wi-Fi.
Del programa al semestre planificado
- Plan de enseñanza y calendario montados solos
- Diario de clases listo para pegar en el SIGA
- Diapositivas y listas de ejercicios con solucionario
Versión Community gratis · Windows
Paso 1: las conexiones
El SG90 tiene tres cables: marrón (GND), rojo (V+) y naranja (señal). La señal va en el GPIO7. La alimentación va en el pin 5V de la Super Mini, que trae los 5 V del USB-C: la lección del servo explica que el motor consume picos de corriente al moverse y, conectado a los 3,3 V, puede hacer caer la tensión de la placa hasta reiniciarla. El LED, con la resistencia de 220 Ω en serie, va en el GPIO6.
Los pines evitan el GPIO8 (LED azul de la placa), el GPIO9 (botón BOOT), el GPIO2 (configuración del chip en el arranque) y el GPIO20 y el GPIO21 (serie del REPL), como en la ficha de la placa.
Paso 2: la cerradura como una máquina de estados
Antes de la red, el corazón del proyecto: la cerradura tiene dos estados, cerrado y abierto, y el programa necesita saber siempre en cuál está. Es el patrón de la lección máquina de estados: una variable guarda el estado, y funciones pasan de uno al otro haciendo todo lo que exige el cambio — mover el servo, encender o apagar el LED, anotar la hora.
El servo recibe el ángulo por el ancho del pulso, a 50 Hz: en la lección, 500 µs son 0° y 2400 µs son 180°. Aquí, 0° es cerrado y 90° es abierto. Abre el ejemplo con el circuito y mira girar el brazo del servo en la vista 2D.
from machine import Pin, PWM
import time
servo = PWM(Pin(7), freq=50, duty_u16=0) # 50 Hz, todavía sin pulso
led = Pin(6, Pin.OUT, value=0)
TRANCADA, ABERTA = 0, 90 # grados
def mover(graus):
pulso_us = 500 + graus * 1900 // 180 # 0° = 500 µs ... 180° = 2400 µs
servo.duty_ns(pulso_us * 1000)
estado = None
def trancar():
global estado
mover(TRANCADA)
led.value(0)
estado = 'cerrado'
print('cerrado')
def abrir():
global estado
mover(ABERTA)
led.value(1)
estado = 'abierto'
print('abierto')
trancar()
time.sleep(1)
abrir()
time.sleep(1)
trancar()
time.sleep(1)
servo.duty_u16(0) # sin pulso: el servo deja de forzar y ahorra energíaLa última línea es un detalle que hace diferencia en una caja: después de llegar al ángulo, el servo puede quedar sin pulso. No se mueve solo (los engranajes lo sostienen), deja de hacer ese zumbido y deja de gastar corriente.
Paso 3: contraseña, rutas y cerrar sola
La página del celular va a pedir direcciones como /abrir?senha=1234 y /fechar. El programa necesita:
- separar la ruta (
/abrir) de los parámetros (senha=1234), como en el artículo del servidor web; - comprobar la contraseña antes de abrir — cerrar no necesita contraseña;
- cerrarse solo después de un tiempo abierto, para que nadie olvide el cajón sin cerrar.
El tercer ítem usa ticks_ms() y ticks_diff(), de la lección ticks: al abrir, el programa anota la hora; en cada vuelta del bucle, comprueba si ya pasó el tiempo. El ejemplo simula una secuencia de solicitudes — una contraseña equivocada, una correcta y un largo silencio — sin red, solo con la lógica, y con un tiempo corto de 3 segundos para que quepa en el ejemplo.
from machine import Pin, PWM
from time import ticks_ms, ticks_diff, sleep_ms
servo = PWM(Pin(7), freq=50, duty_u16=0)
led = Pin(6, Pin.OUT, value=0)
SENHA = '4821'
FECHA_SOZINHA_MS = 3000 # en el proyecto, unos 30 s; aquí corto para el ejemplo
def mover(graus):
servo.duty_ns((500 + graus * 1900 // 180) * 1000)
estado = 'cerrado'
aberta_desde = 0
def tratar(pedido):
global estado, aberta_desde
caminho, _, consulta = pedido.partition('?')
params = dict(p.split('=', 1) for p in consulta.split('&') if '=' in p)
if caminho == '/abrir':
if params.get('senha') != SENHA:
return 'contraseña incorrecta'
mover(90); led.value(1)
estado, aberta_desde = 'abierto', ticks_ms()
return 'abierto'
if caminho == '/fechar':
mover(0); led.value(0); estado = 'cerrado'
return 'cerrado'
return 'no encontré ' + caminho
# (momento en ms, solicitud que llega del celular)
pedidos = [(200, '/abrir?senha=1111'), (900, '/abrir?senha=4821'), (6000, '/fechar')]
inicio = ticks_ms()
while pedidos or estado == 'abierto':
agora = ticks_diff(ticks_ms(), inicio)
if pedidos and agora >= pedidos[0][0]:
t, p = pedidos.pop(0)
print('%5d ms %-20s -> %s' % (agora, p, tratar(p)))
if estado == 'abierto' and ticks_diff(ticks_ms(), aberta_desde) >= FECHA_SOZINHA_MS:
mover(0); led.value(0); estado = 'cerrado'
print('%5d ms (nadie lo cerró) -> cerrado solo' % agora)
sleep_ms(20)
servo.duty_u16(0)Lee la salida con el curso: la contraseña equivocada no abre nada, la correcta abre, y tres segundos después la cerradura se cierra sola. La última solicitud, /fechar, encuentra el cajón ya cerrado — y no hay ningún problema en eso: cerrar lo que ya está cerrado es seguro. Pensar en lo que pasa cuando un comando llega "a destiempo" es un hábito de quien escribe programas que mueven cosas físicas.
La IA que recuerda tu vida y vive en tu ordenador
- Memoria a largo plazo con la fuente de cada respuesta
- Funciona sin conexión: tus datos se quedan en tu PC
- Lee PDF, diapositivas, fotos y grabaciones de reuniones
Plan Esencial gratis para siempre
Paso 4: la página y la red propia de la placa
La placa crea su propia red Wi-Fi en modo punto de acceso (network.AP_IF), como muestra el artículo del servidor web. El celular se conecta a ella, y la página queda siempre en la dirección 192.168.4.1. La red tiene una contraseña de Wi-Fi (en WPA2, tiene que tener por lo menos 8 caracteres) — es la primera barrera; la contraseña de la cerradura es la segunda.
El programa completo, con el servidor, queda abajo. Usa socket, que el simulador todavía no ofrece como servidor, por eso este fragmento es para grabar en la placa. La estructura es la misma del artículo del servidor web: aceptar la conexión, leer la primera línea de la solicitud, responder una página y cerrar. Como accept() espera a un cliente, el servidor usa un tiempo límite (settimeout) para que el bucle vuelva de vez en cuando y compruebe si es hora de cerrar solo.
import network, socket
from machine import Pin, PWM
from time import ticks_ms, ticks_diff
servo = PWM(Pin(7), freq=50, duty_u16=0)
led = Pin(6, Pin.OUT, value=0)
SENHA = '4821'
FECHA_SOZINHA_MS = 30_000
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(ssid='cajon-del-aula', key='cambiar-esta-clave', security=network.AUTH_WPA2_PSK)
def mover(graus):
servo.duty_ns((500 + graus * 1900 // 180) * 1000)
estado, aberta_desde = 'cerrado', 0
mover(0)
PAGINA = """<!doctype html><meta charset=utf-8><meta name=viewport content="width=device-width">
<title>Cajón</title><h1>Cajón {estado}</h1><p>{aviso}</p>
<form action=/abrir><input name=senha type=password inputmode=numeric placeholder=contraseña>
<button>Abrir</button></form><p><a href=/fechar>Cerrar ahora</a></p>"""
def responder(cliente, aviso=''):
corpo = PAGINA.format(estado=estado, aviso=aviso)
cliente.send(b'HTTP/1.0 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n')
cliente.send(corpo.encode())
servidor = socket.socket()
servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
servidor.bind(('0.0.0.0', 80))
servidor.listen(2)
servidor.settimeout(0.5) # vuelve al bucle cada medio segundo
while True:
if estado == 'abierto' and ticks_diff(ticks_ms(), aberta_desde) >= FECHA_SOZINHA_MS:
mover(0); led.value(0); estado = 'cerrado'
try:
cliente, _ = servidor.accept()
except OSError:
continue # nadie pidió nada: comprueba el tiempo de nuevo
try:
linha = cliente.recv(512).decode().split('\r\n')[0] # "GET /abrir?senha=4821 HTTP/1.1"
caminho = linha.split(' ')[1] if ' ' in linha else '/'
rota, _, consulta = caminho.partition('?')
params = dict(p.split('=', 1) for p in consulta.split('&') if '=' in p)
aviso = ''
if rota == '/abrir':
if params.get('senha') == SENHA:
mover(90); led.value(1); estado, aberta_desde = 'abierto', ticks_ms()
else:
aviso = 'Contraseña incorrecta.'
elif rota == '/fechar':
mover(0); led.value(0); estado = 'cerrado'
responder(cliente, aviso)
finally:
cliente.close()Para probar: graba como main.py, enciende la placa, busca la red cajon-del-aula en el celular, conéctate con la contraseña del Wi-Fi y abre http://192.168.4.1 en el navegador. Cambia las dos contraseñas antes de usarla de verdad.
Cómo encuentra el celular a la placa
En el modo punto de acceso, la placa hace el papel que hace el router en casa: anuncia el nombre de la red, acepta a quien tiene la contraseña y da una dirección a cada aparato que entra. La propia placa queda siempre con el 192.168.4.1, y por eso la dirección de la página nunca cambia — a diferencia del modo estación, en el que el router elige el número y hay que mirar en el Serial cuál fue.
Algunos celulares, al entrar en una red sin internet, avisan que "la red no tiene acceso a internet" y preguntan si quieres seguir conectado. La respuesta es sí: la página de la cerradura no necesita internet. Si el celular insiste en cambiar de red solo, apaga los datos móviles durante la prueba.
Qué protege la contraseña (y qué no protege)
Vale la pena mirar con cuidado el camino de la contraseña, porque enseña mucho sobre seguridad en la práctica:
- la contraseña del Wi-Fi (WPA2) cifra todo lo que pasa por el aire entre el celular y la placa. Quien está fuera de la red no puede leer las solicitudes;
- ya dentro de la red, la página es HTTP simple, sin cifrado. Y la contraseña de la cerradura va en la dirección (
/abrir?senha=4821), que queda en el historial del navegador del celular. Cualquier persona a quien le presten el celular puede descubrirla; - el programa compara la contraseña y listo: no hay límite de intentos. Con cuatro dígitos, existen 10 mil combinaciones, y un programa que pruebe todas termina rápido.
Cada uno de esos puntos tiene una mejora posible, y son excelentes desafíos para el curso: enviar la contraseña en un formulario con POST (sale de la dirección), bloquear por un minuto después de tres errores seguidos y cambiar la contraseña desde una página de configuración. Ninguno convierte la cerradura en una caja fuerte, pero cada uno cierra una puerta — y entender qué puertas quedaron abiertas es exactamente lo que se espera de quien diseña sistemas.
Paso 5: la parte mecánica
La cerradura más simple es un pestillo: un brazo largo en el servo, que en 0° entra en un tope fijo al cajón y en 90° sale del camino.
- sujeta el servo por dentro del mueble, al lado del cajón, con cinta doble faz gruesa o un soporte;
- pega un palito de helado al brazo del servo para que quede más largo;
- con la cerradura en 0°, marca dónde toca el palito al cajón y pega ahí un tope (un trozo de madera o de EVA firme);
- prueba abrir y cerrar varias veces antes de pegar todo definitivamente. Si el servo fuerza y tiembla al final del recorrido, ajusta el ángulo de 0° a 5° o 10°: la lección del servo recuerda que los extremos varían de un servo a otro.
Paso 6: probar como usuario
Antes de dar el proyecto por terminado, pásale el celular a un compañero que no haya acompañado el armado y pídele que abra el cajón. Observa sin ayudar. ¿Encontró la red? ¿Entendió que tenía que abrir el navegador? ¿Sabía la contraseña? ¿Supo si el cajón estaba abierto o cerrado?
Este tipo de prueba muestra lo que el autor del proyecto no ve: un nombre de red confuso, un botón demasiado pequeño, la falta de un aviso cuando la contraseña está equivocada. La página del ejemplo ya muestra el estado en el título y un aviso de contraseña equivocada; el LED verde ayuda a quien está lejos de la pantalla. Anota lo que le extrañó al compañero y mejora una cosa por vez.
Paso 7: energía, si la caja está lejos del enchufe
Con la radio Wi-Fi encendida todo el tiempo como punto de acceso, la placa gasta como un aparato siempre despierto — el artículo baterías en la ESP32 muestra que, en ese régimen, una batería común dura horas, no semanas. Para una caja lejos del enchufe, hay dos caminos: alimentar por el USB con un cargador de celular (el más simple) o cambiar el proyecto para que la placa duerma y solo cree la red cuando alguien apriete un botón. El segundo camino es un buen proyecto de continuación, que junta este artículo con el de baterías.
Problemas comunes
- La placa se reinicia cuando el servo se mueve: revisa que el servo esté en el 5V y no en el 3V3, y que el cable USB y la fuente aguanten la corriente. Un capacitor electrolítico entre el 5V y el GND, cerca del servo, ayuda en muchos casos.
- La red no aparece en el celular: revisa que la contraseña del Wi-Fi tenga por lo menos 8 caracteres. El Serial muestra los mensajes de error de
ap.config. - La página abre, pero el servo no se mueve: revisa el cable de la señal en el GPIO7 y que el GND del servo esté conectado al GND de la placa — sin la tierra común, el pulso no tiene referencia.
Ideas para ir más lejos
- Registro de quién abrió: guarda en un archivo la hora de cada apertura y cada contraseña equivocada, como en el artículo guardar datos en la ESP32. Tres contraseñas equivocadas seguidas pueden bloquear la cerradura por un minuto.
- Sensor de cajón abierto: un botón o un reed switch (con un imán en el cajón) muestra si el cajón fue realmente cerrado antes de cerrar con llave.
- Dos placas: la cerradura puede recibir el comando de un "control remoto" hecho con otra placa, por ESP-NOW, sin celular.
Lo que enseña este proyecto
Una cerradura parece un objeto simple, pero su programa ya tiene la forma de muchos sistemas de verdad. Hay un estado que necesita ser siempre conocido y coherente con el mundo físico: el LED encendido tiene que significar cajón abierto, siempre. Hay comandos que llegan de afuera, en cualquier orden y a cualquier hora, y el programa necesita responder a cada uno sin confundirse — incluso a los que no tienen sentido, como cerrar lo que ya está cerrado. Hay tiempo: la cerradura se cierra sola, y para eso el programa no puede quedarse parado esperando una solicitud. Y hay seguridad, que no es un interruptor de encendido y apagado, sino una lista de riesgos que se elige tratar o aceptar.
Un buen cierre para el curso es pedir que cada grupo escriba, en una hoja, los tres riesgos que su proyecto todavía tiene y qué haría para disminuir cada uno. Es el mismo ejercicio que hacen los equipos de ingeniería antes de lanzar un producto.
Para continuar
Las bases de este proyecto están en la ruta PWM (lección del servo), en la ruta Wi-Fi y HTTP (lección Wi-Fi) y en la ruta Tiempo sin trabarse (ticks y máquina de estados). El artículo PWM en la ESP32 profundiza en el servo, y el servidor web en la ESP32, en las páginas.
Fuentes
- Distribución de pines y cuidados de la ESP32-C3 Super Mini: espboards.dev y Last Minute Engineers.
- Pulso del servo, alimentación y velocidad del brazo: la lección del servo y la ficha del SG90, con las fuentes citadas en ellas.
- Documentación de MicroPython:
network.WLANysocket.
Para practicar en PyPlaca
Rutas
Lecciones
- Servo SG90: el ángulo viene del pulso
- Wi-Fi: la placa entra en la red
- ¿Ya es la hora? ticks_ms y ticks_diff
- Máquina de estados