Raspberry Pi

Registro de Acessos Raspberry Pi RFID MQTT com o Leitor RC522

Ligue um leitor RFID RC522 a um Raspberry Pi, leia tags em Python, confira-as em uma lista de permissões local e publique cada tentativa de acesso como um evento MQTT que loggers, dashboards e ferramentas de alerta podem consumir.

Leitor RFID RC522 ligado a um Raspberry Pi ao lado de uma porta, com um cartão sendo lido e um evento de acesso enviado para a nuvem em um projeto raspberry pi rfid mqtt
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Um logger Raspberry Pi RFID MQTT registra cada leitura de crachá em uma porta, máquina de oficina ou armário de ferramentas e a envia para qualquer sistema que precise saber: um banco de dados de logs, um dashboard ou um script de alertas. Neste tutorial você vai ligar um leitor RC522, ler tags em Python, decidir localmente se uma tag é permitida e publicar eventos de acesso estruturados via MQTT, incluindo alertas para cartões desconhecidos.

O que você vai construir

O Pi roda um único serviço Python que:

  1. Consulta o leitor RC522 em busca de cartões MIFARE e chaveiros de 13,56 MHz.
  2. Gera um hash do UID de cada tag para que o ID bruto nunca saia do dispositivo.
  3. Procura o hash em um arquivo local de lista de permissões e decide entre liberado ou negado.
  4. Publica um evento de acesso a cada leitura e um alerta separado para tags desconhecidas.
  5. Mantém uma mensagem retained de "último acesso" e um status online/offline.

A decisão de acesso é tomada no Pi, não na nuvem. Assim a porta continua funcionando quando a rede cai; o MQTT é usado para registro e notificação, que é exatamente aquilo em que ele é bom.

Ligando o RC522

O módulo RC522 fala SPI e funciona apenas com 3,3 V. Ligue assim:

Pino do RC522 Pino físico do Raspberry Pi Função
SDA (SS) 24 SPI CE0
SCK 23 Clock SPI
MOSI 19 SPI MOSI
MISO 21 SPI MISO
IRQ Não conectado
GND 6 Terra
RST 22 GPIO25
3.3V 1 Alimentação

Nunca ligue o módulo em 5 V. Ative o SPI com sudo raspi-config nonint do_spi 0 e reinicie; depois, ls /dev/spidev* deve listar /dev/spidev0.0.

Instalando as bibliotecas

sudo apt update
sudo apt install -y python3-venv python3-dev
mkdir -p ~/rfid-mqtt && cd ~/rfid-mqtt
python3 -m venv .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" mfrc522 spidev

O pacote mfrc522 depende do RPi.GPIO, que não funciona no Raspberry Pi 5. Em um Pi 5, substitua-o pelo pacote compatível rpi-lgpio dentro do ambiente virtual:

pip uninstall -y RPi.GPIO
pip install rpi-lgpio

Teste o leitor antes de adicionar o MQTT:

python -c "from mfrc522 import SimpleMFRC522; r = SimpleMFRC522(); print('Hold a tag near the reader'); print(r.read_id())"

Aproxime um cartão ou chaveiro da antena e um número longo deve ser impresso.

Desenhando os eventos

Topic Payload QoS Retained
connectmq/demo/2fa7c1/door1/access JSON do evento de acesso 1 Não
connectmq/demo/2fa7c1/door1/alert JSON da tag desconhecida 1 Não
connectmq/demo/2fa7c1/door1/last Evento mais recente 1 Sim
connectmq/demo/2fa7c1/door1/status online / offline 1 Sim

Um evento de acesso tem esta cara:

{
  "reader": "door1",
  "tag": "a3f09b7c41de2280",
  "name": "Alice",
  "granted": true,
  "ts": 1792163300
}

O campo tag é um hash com chave do UID, não o UID em si. Em um broker público, e em arquivos de log de modo geral, um UID bruto é algo que você não deve espalhar, porque cartões baratos podem ser clonados a partir dele.

O arquivo de lista de permissões

Crie allowlist.json na pasta do projeto. Você vai preencher os hashes depois das primeiras leituras:

{
  "a3f09b7c41de2280": "Alice",
  "51c2e8d4097fab13": "Workshop spare fob"
}

Para cadastrar um cartão novo, leia-o uma vez, copie o valor de tag da mensagem de alerta, adicione-o ao arquivo com um nome e envie ao serviço um SIGHUP para recarregar, ou simplesmente reinicie-o.

O script Raspberry Pi RFID MQTT

Salve como rfid_mqtt.py:

#!/usr/bin/env python3
"""RC522 access logging over MQTT."""
import hashlib
import hmac
import json
import os
import signal
import time
import uuid
from pathlib import Path

import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522

BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"rfid-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/2fa7c1/door1")
READER_NAME = os.getenv("READER_NAME", "door1")
SECRET = os.getenv("TAG_SECRET", "change-me-to-a-long-random-string").encode()
ALLOWLIST_FILE = Path(os.getenv("ALLOWLIST", "allowlist.json"))
DEBOUNCE_S = 3

reader = SimpleMFRC522()
allowlist = {}


def load_allowlist(*_):
    global allowlist
    try:
        allowlist = json.loads(ALLOWLIST_FILE.read_text())
        print(f"Loaded {len(allowlist)} allowed tags")
    except (OSError, json.JSONDecodeError) as err:
        print(f"Could not load allowlist: {err}")


def tag_hash(uid: int) -> str:
    digest = hmac.new(SECRET, str(uid).encode(), hashlib.sha256).hexdigest()
    return digest[:16]


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connection refused: {reason_code}")
        return
    print(f"Connected as {CLIENT_ID}")
    client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.max_queued_messages_set(500)
if USERNAME:
    client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
    client.tls_set()

load_allowlist()
signal.signal(signal.SIGHUP, load_allowlist)
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()

last_tag, last_time = None, 0.0
try:
    while True:
        uid = reader.read_id_no_block()
        if uid is None:
            time.sleep(0.1)
            continue
        tag = tag_hash(uid)
        now = time.time()
        if tag == last_tag and now - last_time < DEBOUNCE_S:
            continue  # o cartão continua apoiado no leitor
        last_tag, last_time = tag, now

        name = allowlist.get(tag)
        event = {
            "reader": READER_NAME,
            "tag": tag,
            "name": name,
            "granted": name is not None,
            "ts": int(now),
        }
        payload = json.dumps(event)
        client.publish(f"{PREFIX}/access", payload, qos=1)
        client.publish(f"{PREFIX}/last", payload, qos=1, retain=True)
        if name is None:
            client.publish(f"{PREFIX}/alert", payload, qos=1)
        print(event)
        # Acione aqui uma fechadura elétrica ou LED com GPIO.output() se liberado.
except KeyboardInterrupt:
    pass
finally:
    client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
    client.loop_stop()
    client.disconnect()
    GPIO.cleanup()

O debounce impede que um cartão apoiado no leitor gere um evento a cada décimo de segundo. Como a paho enfileira mensagens QoS 1 na memória enquanto a conexão está caída, as leituras feitas durante uma queda curta de rede são entregues após a reconexão; max_queued_messages_set limita essa fila para que uma queda longa não esgote a memória.

Testando o fluxo

Acompanhe tudo a partir de outro terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i audit-2fa7c1 \
  -t 'connectmq/demo/2fa7c1/door1/#' -v

Leia um cartão desconhecido: você verá um evento access com "granted": false e o mesmo payload em alert. Adicione o hash dele ao allowlist.json, envie um SIGHUP ao processo com kill -HUP <pid> e leia de novo para ver "granted": true com o nome.

Registro e alertas

O leitor apenas publica; a persistência e as notificações ficam a cargo de outros clientes MQTT, o que mantém o dispositivo simples. As escolhas mais comuns são:

  1. Um assinante Python que grava cada evento access no SQLite, como mostrado em como salvar dados MQTT no SQLite com Python.
  2. Uma notificação por e-mail ou chat no topic alert, usando o padrão de alertas MQTT por e-mail com Python.
  3. Um fluxo Node-RED que exibe a mensagem retained last em um dashboard.
  4. Uma fechadura elétrica ou luz indicadora acionada por um segundo Pi, usando o controlador de relé no Raspberry Pi via MQTT.

Além da porta de entrada

O mesmo leitor e o mesmo formato de evento servem para vários outros trabalhos. Em um makerspace, um leitor ao lado de uma cortadora a laser ou de um torno pode registrar quem usou a máquina e por quanto tempo: publique um segundo evento quando o cartão for retirado, e um assinante pode calcular a duração da sessão. Para controle de presença em uma sala de aula ou em um pequeno escritório, o topic retained last funciona também como um painel ao vivo de "quem acabou de chegar", e o log no SQLite vira uma folha de ponto. Em um armário de ferramentas, uma leitura pode destravar uma gaveta e registrar qual chaveiro a abriu.

Em cada caso, só mudam o nome do leitor e o prefixo de topic. Mantenha um nível de prefixo por local, como site1/workshop/lathe, e um único assinante com o curinga + consegue coletar eventos de todos os leitores. Um campo granted e uma tag com hash estável são tudo de que as ferramentas a jusante precisam, o que mantém o código do dispositivo idêntico em todo o prédio.

Retorno para quem faz a leitura

As pessoas esperam ver ou ouvir que a leitura funcionou. Adicione um LED verde e um vermelho, ou um pequeno buzzer, em pinos GPIO livres e acione-os com GPIO.output() logo após a decisão; observe que a biblioteca do leitor já coloca o RPi.GPIO no modo de numeração BOARD, então use os números dos pinos físicos. Dê o retorno localmente em vez de esperar uma ida e volta pelo MQTT, para que ele continue instantâneo mesmo em uma rede lenta.

Considerações de segurança

Projetos com RFID tocam na segurança física, então seja realista sobre os limites deles. Cartões MIFARE Classic básicos e muitos chaveiros podem ser clonados, então um sistema baseado em UID é adequado para registro, controle de presença e acesso por conveniência, não para proteger áreas de alto valor. Além disso:

  • Nunca rode um sistema de acesso no servidor de testes público. Tudo no servidor MQTT público em broker.connectmq.com pode ser lido por qualquer pessoa e serve apenas para o primeiro teste.
  • Use um broker privado com TLS e credenciais por dispositivo, para que ninguém consiga publicar eventos falsos ou ler os seus logs.
  • Mantenha o TAG_SECRET fora do código e fora do controle de versão; guarde-o em um arquivo de ambiente do systemd com chmod 600.
  • Tome a decisão de liberação localmente, para que uma queda de rede não tranque as pessoas do lado de fora nem abra a porta.
  • Logs de acesso associados a nomes de pessoas são dados pessoais; no Brasil, trate-os de acordo com a LGPD, com retenção e acesso definidos.

Para orientações mais amplas, leia nossas boas práticas de segurança MQTT.

Indo para a produção

Um broker MQTT gerenciado como o ConnectMQ oferece TLS na porta 8883, credenciais geradas e vinculadas a cada client ID e um portal onde você pode acompanhar a chegada dos eventos de acesso no explorador de topics ao vivo. Defina as variáveis de ambiente no seu arquivo de serviço:

MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
MQTT_PREFIX=site1/building-a/door1
TAG_SECRET=a-long-random-secret

O tutorial MQTT com Raspberry Pi inclui um modelo de serviço do systemd que você pode adaptar para que o leitor inicie no boot.

Comece com o ConnectMQ

Dê aos seus logs de acesso um caminho privado e criptografado. Crie um broker Serverless gratuito no ConnectMQ, sem cartão de crédito, e conecte o seu leitor com as credenciais geradas via TLS. É um broker MQTT na nuvem que o seu logger, o seu dashboard e os seus scripts de alerta conseguem acessar sem abrir portas no local.

Perguntas frequentes

Quais cartões RFID funcionam com o RC522?

O RC522 lê cartões ISO 14443A de 13,56 MHz, como MIFARE Classic, MIFARE Ultralight e muitos chaveiros. Ele não lê cartões de proximidade de 125 kHz.

Por que gerar um hash do UID RFID antes de publicá-lo?

Um UID bruto pode ser usado para clonar cartões simples, e mensagens MQTT muitas vezes acabam em logs. Um hash com chave continua identificando o cartão de forma única sem revelar o UID.

O RC522 funciona no Raspberry Pi 5?

Sim, com o SPI ativado e o pacote rpi-lgpio instalado no lugar do RPi.GPIO, que a biblioteca mfrc522 espera.

O que acontece com as leituras quando a rede cai?

O Pi continua decidindo o acesso localmente. Os eventos QoS 1 ficam enfileirados na memória e são entregues após a reconexão, desde que o serviço continue rodando.

Artigos relacionados

Confiabilidade do Raspberry Pi: Rodando um Pi 24/7 como Gateway IoT

Um guia prático para manter um gateway MQTT com Raspberry Pi rodando por meses: desgaste do armazenamento, sistemas de arquivos somente leitura, alimentação, refrigeração, watchdogs de hardware e software e monitoramento remoto de saúde.

9 min de leitura

Pico W vs Pico 2 W para MQTT: Desempenho, Memória e TLS do RP2040 vs RP2350

O Pico 2 W mantém o formato e o chip Wi-Fi do Pico W, mas troca o RP2040 pelo RP2350, mais rápido. Esta comparação mostra o que isso significa para dispositivos MQTT: CPU, memória, TLS, recursos de segurança, suporte de software e qual placa comprar para cada projeto.

9 min de leitura

Ponte BLE para MQTT com Raspberry Pi usando Python e Bleak

Use o Bluetooth integrado do Raspberry Pi, a biblioteca bleak e o paho-mqtt para escanear advertisements de sensores BLE, decodificar temperatura e umidade e publicar leituras limitadas em MQTT.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.