Community project
Jola Device Controller
The Jola Device Controller is an ESP32-based smart lock system that manages access to a secured container using RFID authentication and servo-controlled locking. It combines an MFRC522 RFID reader, reed switches for key and lid detection, a servo motor for the latch mechanism, and status LEDs to provide real-time feedback on the device state.
This guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for building the controller. The included firmware enables WiFi connectivity and a web interface accessible from a mobile device, allowing remote monitoring of loan approval status, key presence, and lid position. Builders will learn how to integrate RFID authentication with mechanical actuation and web-based control in a single project.
Wiring diagram
Gather all the parts
Assemble it in 5 steps
1. Prepara el frente de la caja
En el frente de la caja de 20 × 15 × 10 cm coloca el lector RFID y los dos LED: el verde para autorización y el rojo para rechazo. Deja visible la antena plana del RFID y marca cada LED con una etiqueta corta.
- Coloca los LED junto al lector para que se entiendan como una sola zona de acceso.
- No conectes el lector RFID RC522 a 5 V: su alimentación es de 3,3 V y 5 V puede dañarlo.
2. Monta la llave y sus detectores
Dentro de la caja fija el soporte de la llave y sitúa el microinterruptor debajo para que la llave lo presione cuando está guardada. Coloca el sensor magnético en el borde de la tapa y su imán enfrente, de modo que detecte cuando la tapa se separa.
- Comprueba a mano que la llave pulsa el sensor sin quedarse atascada y que el imán queda cerca del sensor al cerrar.
- Un sensor mal alineado puede indicar que la llave está presente o que la tapa está cerrada cuando no lo está.
3. Instala el pestillo
Atornilla el microservo cerca del cierre y une su brazo al pestillo para que un giro corto libere la tapa. Prueba el recorrido sin alimentar el servo para evitar que el mecanismo fuerce la tapa.
- Deja un pequeño margen mecánico al final del recorrido; el servo no debe seguir empujando contra un tope.
- Si el pestillo se traba, el servo puede calentarse y dañarse.
4. Separa control y alimentación
En el compartimento separado instala el ESP32 y el módulo de alimentación de 5 V. Mantén el módulo de 5 V cerca del servo y lleva una tierra común a todos los módulos.
- Usa una fuente USB de 5 V capaz de entregar suficiente corriente para el movimiento breve del servo.
- No intercambies 5 V, 3,3 V y GND: una conexión de alimentación equivocada puede dañar el ESP32 o el lector RFID.
5. Conecta los módulos según el lienzo
Sigue las líneas del esquema de Schematik: el lector RFID, los LED, el sensor bajo la llave, el sensor de tapa y el servo llegan al ESP32; la aplicación del celular se comunica por Wi‑Fi, sin cable físico.
- Los dos LED necesitan su resistencia limitadora; el esquema la añade con cada LED.
- Antes de cerrar la caja, verifica que ningún cable pueda rozar el brazo del servo ni el pestillo.
Review all connections
1. Connections between "rfid_frontal" and "ESP32"
2. Connections between "led_verde_frontal" and "ESP32"
3. Connections between "led_rojo_frontal" and "ESP32"
4. Connections between "sensor_llave" and "ESP32"
5. Connections between "servo_pestillo" and "ESP32"
6. Connections between "sensor_tapa" and "ESP32"
7. Connections between "alimentacion_5v" and "ESP32"
Deploy the firmware
#include <WiFi.h>
#include <WebServer.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESP32Servo.h>
constexpr uint8_t RFID_SS_PIN = 4;
constexpr uint8_t RFID_RST_PIN = 27;
constexpr uint8_t LED_GREEN_PIN = 32;
constexpr uint8_t LED_RED_PIN = 33;
constexpr uint8_t KEY_SENSOR_PIN = 14;
constexpr uint8_t LID_SENSOR_PIN = 13;
constexpr uint8_t SERVO_PIN = 25;
constexpr int SERVO_LOCKED = 10;
constexpr int SERVO_UNLOCKED = 100;
const char *AP_SSID = "LlaveLink";
const char *AP_PASSWORD = "LlaveLink2026";
MFRC522 rfid(RFID_SS_PIN, RFID_RST_PIN);
Servo lockServo;
WebServer server(80);
bool loanApproved = false;
bool unlocked = false;
bool lastKeyPresent = false;
bool lastLidOpen = false;
void setLock(bool open) {
unlocked = open;
lockServo.write(open ? SERVO_UNLOCKED : SERVO_LOCKED);
digitalWrite(LED_GREEN_PIN, open ? HIGH : LOW);
digitalWrite(LED_RED_PIN, open ? LOW : HIGH);
}
String statusText() {
bool keyPresent = digitalRead(KEY_SENSOR_PIN) == HIGH;
bool lidOpen = digitalRead(LID_SENSOR_PIN) == HIGH;
String text = "Préstamo: ";
text += loanApproved ? "aprobado" : "pendiente";
text += " | Llave: ";
text += keyPresent ? "en soporte" : "retirada";
text += " | Tapa: ";
text += lidOpen ? "abierta" : "cerrada";
return text;
}
void sendPage() {
String page = "<!doctype html><html><meta name='viewport' content='width=device-width,initial-scale=1'>";
page += "<style>body{font-family:Arial;margin:28px;color:#17324d}button{display:block;width:100%;padding:16px;margin:12px 0;font-size:18px;border:0;border-radius:8px;background:#1677c8;color:white}</style>";
page += "<h1>LlaveLink</h1><p>" + statusText() + "</p>";
page += "<form action='/request'><button>Solicitar llave</button></form>";
page += "<form action='/approve'><button>Aprobar préstamo</button></form>";
page += "<form action='/history'><button>Historial</button></form></html>";
server.send(200, "text/html; charset=utf-8", page);
}
void setup() {
Serial.begin(115200);
pinMode(LED_GREEN_PIN, OUTPUT);
pinMode(LED_RED_PIN, OUTPUT);
pinMode(KEY_SENSOR_PIN, INPUT_PULLUP);
pinMode(LID_SENSOR_PIN, INPUT_PULLUP);
lockServo.setPeriodHertz(50);
lockServo.attach(SERVO_PIN, 500, 2400);
setLock(false);
SPI.begin();
rfid.PCD_Init();
WiFi.softAP(AP_SSID, AP_PASSWORD);
server.on("/", sendPage);
server.on("/request", []() { loanApproved = false; server.sendHeader("Location", "/"); server.send(303); });
server.on("/approve", []() { loanApproved = true; server.sendHeader("Location", "/"); server.send(303); });
server.on("/history", []() { server.send(200, "text/plain; charset=utf-8", "Historial conceptual: " + statusText()); });
server.begin();
lastKeyPresent = digitalRead(KEY_SENSOR_PIN) == HIGH;
lastLidOpen = digitalRead(LID_SENSOR_PIN) == HIGH;
Serial.println("LlaveLink listo. Conéctate a la red Wi-Fi LlaveLink.");
}
void loop() {
server.handleClient();
bool keyPresent = digitalRead(KEY_SENSOR_PIN) == HIGH;
bool lidOpen = digitalRead(LID_SENSOR_PIN) == HIGH;
if (keyPresent != lastKeyPresent || lidOpen != lastLidOpen) {
lastKeyPresent = keyPresent;
lastLidOpen = lidOpen;
Serial.println(statusText());
}
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
if (loanApproved) {
setLock(true);
Serial.println("Tarjeta RFID aceptada: pestillo liberado.");
} else {
digitalWrite(LED_RED_PIN, HIGH);
delay(200);
digitalWrite(LED_RED_PIN, LOW);
Serial.println("Tarjeta RFID leída: préstamo pendiente de aprobación.");
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
delay(10);
}Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.




