Como Fazer uma Fechadura Inteligente Caseira: Tutorial Passo a Passo com Arduino

Construir uma fechadura inteligente caseira é um dos projetos DIY mais procurados por entusiastas de casa inteligente. Com componentes simples como Arduino, servo motor e módulo Wi-Fi, é possível criar um sistema de controle de acesso por smartphone gastando apenas R$ 150-300, uma fração do preço de fechaduras comerciais que custam R$ 1.500+.

Visão Geral do Projeto

O Que Vamos Construir

Sistema Completo:

  • Fechadura controlada por servo motor
  • Controle via smartphone (Android/iOS)
  • Interface web para múltiplos usuários
  • Histórico de acessos
  • Modo manual de emergência

Funcionalidades:

  • Abertura remota: De qualquer lugar com internet
  • Códigos temporários: Para visitantes e prestadores
  • Log de acesso: Histórico completo de entradas
  • Notificações: Alertas em tempo real
  • Backup manual: Chave física sempre funcional

Nível de Dificuldade

Conhecimentos Necessários:

  • Eletrônica básica (uso de protoboard)
  • Programação Arduino iniciante
  • Soldagem simples (opcional)
  • Uso de ferramentas básicas

Tempo de Construção:

  • Montagem: 4-6 horas
  • Programação: 2-3 horas
  • Instalação: 2-4 horas
  • Total: 8-13 horas (fim de semana)

Lista de Materiais

Componentes Eletrônicos

Microcontrolador:

  • Arduino Nano: R$ 25-35 (ou ESP32: R$ 40-60)
  • Protoboard: R$ 15-25
  • Jumpers: R$ 10-15

Atuador:

  • Servo Motor SG90: R$ 20-30 (para portas leves)
  • Servo MG996R: R$ 40-60 (para portas pesadas)
  • Micro servo DS04-NFC: R$ 30-45 (alternativa compacta)

Conectividade:

  • Módulo ESP8266: R$ 25-40 (se usar Arduino Nano)
  • Antena Wi-Fi: R$ 10-15 (opcional para melhor sinal)

Alimentação:

  • Fonte 5V 2A: R$ 20-30
  • Bateria 9V: R$ 15-25 (backup)
  • Conector P4: R$ 5-10

Sensores e Interface:

  • LED RGB: R$ 3-5
  • Buzzer: R$ 5-8
  • Botão push: R$ 2-5
  • Resistores: R$ 5-10 (kit)

Proteção:

  • Caixa plástica: R$ 15-30
  • Relé 5V: R$ 8-15 (se usar fechadura elétrica)

Ferramentas Necessárias

Básicas:

  • Furadeira com brocas variadas
  • Chaves de fenda e philips
  • Alicate descascador
  • Multímetro básico
  • Cola quente ou epóxi

Opcionais:

  • Ferro de solda + solda
  • Sugador de solda
  • Morsa pequena
  • Dremel (para ajustes finos)

Materiais de Instalação

Mecânicos:

  • Suporte servo: Impresso 3D ou adaptado
  • Acoplamento: Conectar servo à fechadura
  • Parafusos M3: Fixação do sistema
  • Abraçadeiras: Organização de cabos

Custo Total Estimado:

  • Básico: R$ 150-250
  • Completo: R$ 250-400
  • Premium: R$ 400-600 (com ESP32, sensores extras)

Esquema Eletrônico

Conexões Arduino Nano + ESP8266

Arduino Nano:
- D2  → Servo Motor (sinal)
- D3  → LED RGB (vermelho)
- D4  → LED RGB (verde)
- D5  → LED RGB (azul)
- D6  → Buzzer
- D7  → Botão manual
- D8  → ESP8266 (TX)
- D9  → ESP8266 (RX)
- 5V  → VCC componentes
- GND → GND comum

ESP8266:
- TX  → Arduino D9
- RX  → Arduino D8
- VCC → 3.3V (divisor de tensão)
- GND → GND comum

Alternativa com ESP32 (Recomendada)

ESP32:
- GPIO 18 → Servo Motor
- GPIO 19 → LED RGB (R)
- GPIO 21 → LED RGB (G)
- GPIO 22 → LED RGB (B)
- GPIO 23 → Buzzer
- GPIO 25 → Botão manual
- 5V      → Servo VCC
- 3.3V    → ESP32 VCC
- GND     → GND comum

Diagrama de Alimentação

Fonte 5V 2A:
├── Arduino/ESP32 (via regulador)
├── Servo Motor (direto 5V)
├── LEDs (com resistores)
└── Backup Bateria 9V

Código Arduino Completo

Código para ESP32 (Versão Completa)

#include <WiFi.h>
#include <WebServer.h>
#include <ESP32Servo.h>
#include <EEPROM.h>
#include <ArduinoJson.h>

// Configurações Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";

// Pinos
#define SERVO_PIN 18
#define LED_R 19
#define LED_G 21
#define LED_B 22
#define BUZZER 23
#define BUTTON 25

// Objetos
Servo fechadura;
WebServer server(80);

// Variáveis
bool portaAberta = false;
unsigned long ultimoAcesso = 0;
String logAcessos = "";

// Posições do servo
const int POSICAO_FECHADA = 0;
const int POSICAO_ABERTA = 90;

void setup() {
  Serial.begin(115200);
  
  // Configurar pinos
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  pinMode(BUTTON, INPUT_PULLUP);
  
  // Inicializar servo
  fechadura.attach(SERVO_PIN);
  fechadura.write(POSICAO_FECHADA);
  
  // Conectar Wi-Fi
  WiFi.begin(ssid, password);
  setLED(255, 255, 0); // Amarelo = conectando
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Conectando WiFi...");
  }
  
  Serial.println("WiFi conectado!");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
  
  // Configurar servidor web
  configurarServidor();
  server.begin();
  
  setLED(0, 255, 0); // Verde = conectado
  bip(2); // Dois bips = sistema pronto
}

void loop() {
  server.handleClient();
  
  // Verificar botão manual
  if (digitalRead(BUTTON) == LOW) {
    delay(50); // Debounce
    if (digitalRead(BUTTON) == LOW) {
      togglePorta("manual");
      while (digitalRead(BUTTON) == LOW) delay(10);
    }
  }
  
  // Auto-fechar após 10 segundos
  if (portaAberta && (millis() - ultimoAcesso > 10000)) {
    fecharPorta("auto");
  }
}

void configurarServidor() {
  // Página principal
  server.on("/", HTTP_GET, []() {
    String html = gerarPaginaHTML();
    server.send(200, "text/html", html);
  });
  
  // API - Abrir porta
  server.on("/abrir", HTTP_POST, []() {
    String codigo = server.arg("codigo");
    if (validarCodigo(codigo)) {
      abrirPorta("app");
      server.send(200, "application/json", "{\"status\":\"sucesso\"}");
    } else {
      server.send(401, "application/json", "{\"status\":\"codigo_invalido\"}");
    }
  });
  
  // API - Fechar porta
  server.on("/fechar", HTTP_POST, []() {
    fecharPorta("app");
    server.send(200, "application/json", "{\"status\":\"sucesso\"}");
  });
  
  // API - Status
  server.on("/status", HTTP_GET, []() {
    String json = "{";
    json += "\"porta_aberta\":" + String(portaAberta ? "true" : "false") + ",";
    json += "\"ultimo_acesso\":" + String(ultimoAcesso) + ",";
    json += "\"wifi_signal\":" + String(WiFi.RSSI());
    json += "}";
    server.send(200, "application/json", json);
  });
  
  // API - Histórico
  server.on("/historico", HTTP_GET, []() {
    server.send(200, "application/json", logAcessos);
  });
}

String gerarPaginaHTML() {
  String html = R"(
<!DOCTYPE html>
<html>
<head>
    <title>Fechadura Inteligente DIY</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body { font-family: Arial; margin: 20px; background: #f0f0f0; }
        .container { max-width: 400px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; }
        .status { text-align: center; font-size: 24px; margin: 20px 0; }
        .aberta { color: #27ae60; }
        .fechada { color: #e74c3c; }
        button { width: 100%; padding: 15px; margin: 10px 0; font-size: 18px; border: none; border-radius: 5px; cursor: pointer; }
        .btn-abrir { background: #27ae60; color: white; }
        .btn-fechar { background: #e74c3c; color: white; }
        .btn-refresh { background: #3498db; color: white; }
        .codigo { width: 100%; padding: 10px; margin: 10px 0; font-size: 16px; }
        .info { background: #ecf0f1; padding: 10px; border-radius: 5px; margin: 10px 0; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🔒 Fechadura DIY</h1>
        <div class="status" id="status">Carregando...</div>
        
        <input type="password" class="codigo" id="codigo" placeholder="Código de acesso" maxlength="6">
        
        <button class="btn-abrir" onclick="abrirPorta()">🔓 Abrir Porta</button>
        <button class="btn-fechar" onclick="fecharPorta()">🔒 Fechar Porta</button>
        <button class="btn-refresh" onclick="atualizarStatus()">🔄 Atualizar</button>
        
        <div class="info">
            <strong>Status Wi-Fi:</strong> <span id="wifi">-</span> dBm<br>
            <strong>Último acesso:</strong> <span id="ultimo">-</span>
        </div>
    </div>

    <script>
        function atualizarStatus() {
            fetch('/status')
                .then(response => response.json())
                .then(data => {
                    const status = document.getElementById('status');
                    if (data.porta_aberta) {
                        status.innerHTML = '🔓 PORTA ABERTA';
                        status.className = 'status aberta';
                    } else {
                        status.innerHTML = '🔒 PORTA FECHADA';
                        status.className = 'status fechada';
                    }
                    document.getElementById('wifi').textContent = data.wifi_signal;
                    document.getElementById('ultimo').textContent = new Date(data.ultimo_acesso).toLocaleString();
                });
        }
        
        function abrirPorta() {
            const codigo = document.getElementById('codigo').value;
            fetch('/abrir', {
                method: 'POST',
                headers: {'Content-Type': 'application/x-www-form-urlencoded'},
                body: 'codigo=' + codigo
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'sucesso') {
                    alert('✅ Porta aberta!');
                    document.getElementById('codigo').value = '';
                    atualizarStatus();
                } else {
                    alert('❌ Código inválido!');
                }
            });
        }
        
        function fecharPorta() {
            fetch('/fechar', {method: 'POST'})
                .then(response => response.json())
                .then(data => {
                    alert('✅ Porta fechada!');
                    atualizarStatus();
                });
        }
        
        // Atualizar status a cada 5 segundos
        setInterval(atualizarStatus, 5000);
        atualizarStatus(); // Primeira execução
    </script>
</body>
</html>
  )";
  return html;
}

bool validarCodigo(String codigo) {
  // Códigos válidos (pode expandir para EEPROM)
  return (codigo == "123456" || codigo == "admin" || codigo == "1234");
}

void abrirPorta(String origem) {
  fechadura.write(POSICAO_ABERTA);
  portaAberta = true;
  ultimoAcesso = millis();
  
  setLED(0, 255, 0); // Verde
  bip(1); // Um bip
  
  adicionarLog("ABRIR", origem);
  Serial.println("Porta aberta por: " + origem);
}

void fecharPorta(String origem) {
  fechadura.write(POSICAO_FECHADA);
  portaAberta = false;
  
  setLED(255, 0, 0); // Vermelho
  bip(2); // Dois bips
  
  adicionarLog("FECHAR", origem);
  Serial.println("Porta fechada por: " + origem);
}

void togglePorta(String origem) {
  if (portaAberta) {
    fecharPorta(origem);
  } else {
    abrirPorta(origem);
  }
}

void setLED(int r, int g, int b) {
  analogWrite(LED_R, r);
  analogWrite(LED_G, g);
  analogWrite(LED_B, b);
}

void bip(int vezes) {
  for (int i = 0; i < vezes; i++) {
    digitalWrite(BUZZER, HIGH);
    delay(100);
    digitalWrite(BUZZER, LOW);
    delay(100);
  }
}

void adicionarLog(String acao, String origem) {
  String timestamp = String(millis());
  String novoLog = "{\"timestamp\":" + timestamp + ",\"acao\":\"" + acao + "\",\"origem\":\"" + origem + "\"}";
  
  if (logAcessos.length() > 0) {
    logAcessos = novoLog + "," + logAcessos;
  } else {
    logAcessos = novoLog;
  }
  
  // Manter apenas últimos 50 registros
  int virgulas = 0;
  for (int i = 0; i < logAcessos.length(); i++) {
    if (logAcessos.charAt(i) == ',') {
      virgulas++;
      if (virgulas >= 50) {
        logAcessos = logAcessos.substring(0, i);
        break;
      }
    }
  }
}

Montagem Passo a Passo

Passo 1: Preparação da Eletrônica

Montagem na Protoboard:

  1. Posicione o ESP32 no centro da protoboard
  2. Conecte as alimentações:
    • Linha vermelha: 5V
    • Linha azul: GND
    • Linha amarela: 3.3V
  3. Teste as conexões com multímetro

Conexão do Servo:

Servo SG90:
├── Fio vermelho → 5V
├── Fio marrom → GND  
└── Fio laranja → GPIO 18

LEDs e Buzzer:

LED RGB (ânodo comum):
├── Ânodo → 3.3V
├── R → GPIO 19 (via resistor 220Ω)
├── G → GPIO 21 (via resistor 220Ω)
└── B → GPIO 22 (via resistor 220Ω)

Buzzer:
├── + → GPIO 23
└── - → GND

Passo 2: Teste da Eletrônica

Upload do Código:

  1. Instale Arduino IDE 2.0
  2. Adicione biblioteca ESP32
  3. Instale bibliotecas necessárias
  4. Configure sua rede Wi-Fi no código
  5. Faça upload para o ESP32

Testes Funcionais:

// Código de teste básico
void testarComponentes() {
  // Teste servo
  fechadura.write(0);
  delay(1000);
  fechadura.write(90);
  delay(1000);
  
  // Teste LEDs
  setLED(255, 0, 0); delay(500);
  setLED(0, 255, 0); delay(500);
  setLED(0, 0, 255); delay(500);
  
  // Teste buzzer
  bip(3);
}

Passo 3: Adaptação Mecânica

Análise da Fechadura Existente:

  • Meça o curso necessário do mecanismo
  • Identifique ponto de aplicação de força
  • Verifique espaço disponível para instalação
  • Teste resistência mecânica

Criação do Acoplamento:

Opções de acoplamento:
1. Impressão 3D (ideal)
2. Adaptação com perfil de alumínio
3. Soldagem de peças metálicas
4. Uso de abraçadeiras e parafusos

Dimensões Típicas:

  • Curso do servo: 0° a 90° (aprox. 2cm linear)
  • Força disponível: 1.5-3kg/cm
  • Velocidade: 0.1s/60°

Passo 4: Instalação Física

Preparação do Local:

  1. Desligue energia da fechadura elétrica (se houver)
  2. Remova painéis de acesso
  3. Meça e marque posições de fixação
  4. Teste mecanismo manualmente

Fixação do Sistema:

Checklist de instalação:
□ Servo fixado com parafusos M3
□ Acoplamento alinhado corretamente
□ Movimento livre sem travamentos
□ Cabos organizados e protegidos
□ Caixa de proteção instalada
□ Fonte de alimentação conectada

Teste de Integração:

  • Movimento manual suave
  • Acionamento eletrônico preciso
  • Retorno à posição inicial
  • Funcionamento da chave manual

Configuração do Aplicativo

Interface Web Responsiva

O código inclui uma interface web completa que funciona como aplicativo:

Recursos da Interface:

  • Design responsivo: Funciona em qualquer dispositivo
  • Status em tempo real: Atualização automática
  • Controles intuitivos: Botões grandes e claros
  • Histórico de acesso: Log completo de operações
  • Indicadores visuais: Status da porta e Wi-Fi

Criando Atalho no Smartphone

Android:

  1. Abra Chrome e acesse o IP da fechadura
  2. Menu → “Adicionar à tela inicial”
  3. Nomeie como “Fechadura Casa”
  4. Ícone aparecerá como app nativo

iOS:

  1. Abra Safari e acesse o IP da fechadura
  2. Botão compartilhar → “Adicionar à Tela de Início”
  3. Confirme o nome e ícone
  4. App disponível na tela principal

Configuração de Códigos

Códigos Padrão:

  • 123456 – Código família
  • admin – Código administrador
  • 1234 – Código emergência

Adicionar Novos Códigos:

bool validarCodigo(String codigo) {
  String codigos[] = {"123456", "admin", "1234", "visitante"};
  int totalCodigos = 4;
  
  for (int i = 0; i < totalCodigos; i++) {
    if (codigo == codigos[i]) {
      return true;
    }
  }
  return false;
}

Recursos Avançados

Integração com Assistentes de Voz

Alexa/Google Assistant via IFTTT:

// Webhook para IFTTT
server.on("/ifttt", HTTP_POST, []() {
  String key = server.arg("key");
  String action = server.arg("action");
  
  if (key == "SUA_CHAVE_IFTTT") {
    if (action == "abrir") {
      abrirPorta("alexa");
    } else if (action == "fechar") {
      fecharPorta("alexa");
    }
    server.send(200, "text/plain", "OK");
  } else {
    server.send(401, "text/plain", "Unauthorized");
  }
});

Sistema de Códigos Temporários

struct CodigoTemporario {
  String codigo;
  unsigned long expiracao;
  String nome;
};

CodigoTemporario codigosTemp[10];
int totalCodigosTemp = 0;

void adicionarCodigoTemporario(String codigo, unsigned long duracao, String nome) {
  if (totalCodigosTemp < 10) {
    codigosTemp[totalCodigosTemp].codigo = codigo;
    codigosTemp[totalCodigosTemp].expiracao = millis() + duracao;
    codigosTemp[totalCodigosTemp].nome = nome;
    totalCodigosTemp++;
  }
}

bool validarCodigoTemporario(String codigo) {
  unsigned long agora = millis();
  
  for (int i = 0; i < totalCodigosTemp; i++) {
    if (codigosTemp[i].codigo == codigo) {
      if (agora < codigosTemp[i].expiracao) {
        return true;
      } else {
        // Remove código expirado
        for (int j = i; j < totalCodigosTemp - 1; j++) {
          codigosTemp[j] = codigosTemp[j + 1];
        }
        totalCodigosTemp--;
        return false;
      }
    }
  }
  return false;
}

Notificações Push

Integração com Pushover:

#include <HTTPClient.h>

void enviarNotificacao(String mensagem) {
  HTTPClient http;
  http.begin("https://api.pushover.net/1/messages.json");
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  
  String dados = "token=SEU_TOKEN&user=SEU_USER&message=" + mensagem;
  int httpCode = http.POST(dados);
  
  if (httpCode > 0) {
    Serial.println("Notificação enviada");
  }
  http.end();
}

void abrirPorta(String origem) {
  fechadura.write(POSICAO_ABERTA);
  portaAberta = true;
  ultimoAcesso = millis();
  
  setLED(0, 255, 0);
  bip(1);
  
  adicionarLog("ABRIR", origem);
  enviarNotificacao("🔓 Porta aberta por: " + origem);
}

Solução de Problemas

Problemas Comuns

Servo Não Funciona:

  • Causa: Alimentação insuficiente
  • Solução: Use fonte 5V 2A dedicada
  • Teste: Meça tensão nos pinos do servo

Wi-Fi Não Conecta:

  • Causa: SSID/senha incorretos ou sinal fraco
  • Solução: Verifique credenciais e proximidade do roteador
  • Debug: Monitor serial mostra detalhes da conexão

Página Web Não Carrega:

  • Causa: IP incorreto ou firewall
  • Solução: Verifique IP no monitor serial
  • Teste: Ping para o dispositivo

Movimento Irregular:

  • Causa: Acoplamento desalinhado ou atrito
  • Solução: Ajuste mecânico e lubrificação
  • Prevenção: Teste regular de movimento

Diagnóstico Avançado

Código de Diagnóstico:

void diagnosticoCompleto() {
  Serial.println("=== DIAGNÓSTICO FECHADURA DIY ===");
  
  // Teste Wi-Fi
  Serial.print("Wi-Fi Status: ");
  Serial.println(WiFi.status() == WL_CONNECTED ? "Conectado" : "Desconectado");
  Serial.print("IP Local: ");
  Serial.println(WiFi.localIP());
  Serial.print("Sinal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  
  // Teste Servo
  Serial.println("Testando servo...");
  fechadura.write(0);
  delay(1000);
  fechadura.write(90);
  delay(1000);
  fechadura.write(45);
  
  // Teste LEDs
  Serial.println("Testando LEDs...");
  setLED(255, 0, 0); delay(300);
  setLED(0, 255, 0); delay(300);
  setLED(0, 0, 255); delay(300);
  setLED(0, 0, 0);
  
  // Teste Buzzer
  Serial.println("Testando buzzer...");
  bip(3);
  
  // Teste Botão
  Serial.println("Teste do botão - pressione para confirmar");
  while (digitalRead(BUTTON) == HIGH) {
    delay(10);
  }
  Serial.println("Botão OK!");
  
  Serial.println("=== DIAGNÓSTICO CONCLUÍDO ===");
}

Melhorias e Expansões

Versão 2.0 – Recursos Avançados

Leitor RFID:

#include <MFRC522.h>

#define SS_PIN 5
#define RST_PIN 27

MFRC522 rfid(SS_PIN, RST_PIN);

void setup() {
  // ... código existente ...
  SPI.begin();
  rfid.PCD_Init();
}

void loop() {
  // ... código existente ...
  
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    String uid = "";
    for (byte i = 0; i < rfid.uid.size; i++) {
      uid += String(rfid.uid.uidByte[i], HEX);
    }
    
    if (validarRFID(uid)) {
      abrirPorta("rfid");
    }
    
    rfid.PICC_HaltA();
  }
}

Sensor de Porta:

#define SENSOR_PORTA 26

void setup() {
  // ... código existente ...
  pinMode(SENSOR_PORTA, INPUT_PULLUP);
}

void loop() {
  // ... código existente ...
  
  bool portaFisicaAberta = digitalRead(SENSOR_PORTA) == HIGH;
  
  if (portaFisicaAberta && !portaAberta) {
    // Porta foi aberta fisicamente
    enviarNotificacao("⚠️ Porta aberta sem comando!");
    adicionarLog("ABERTA_FISICA", "sensor");
  }
}

Integração com Home Assistant

Configuração YAML:

switch:
  - platform: rest
    name: "Fechadura Casa"
    resource: "http://IP_DA_FECHADURA/toggle"
    method: POST
    body_on: '{"action": "abrir"}'
    body_off: '{"action": "fechar"}'
    headers:
      Content-Type: application/json

binary_sensor:
  - platform: rest
    name: "Status Fechadura"
    resource: "http://IP_DA_FECHADURA/status"
    method: GET
    value_template: "{{ value_json.porta_aberta }}"

Aplicativo Mobile Nativo

Estrutura React Native Básica:

import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';

const FechaduraApp = () => {
  const [status, setStatus] = useState(false);
  const [loading, setLoading] = useState(false);
  
  const IP_FECHADURA = '192.168.1.100'; // Seu IP aqui
  
  const abrir

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

G-PD085SMG2Z
Rolar para cima