Este tutorial tem como objetivo mostrar o passo a passo da construção de um sistema de controle de acesso acionado por TAG’s RFID.
- Funcionalidades:
- Liberar o acesso para TAG’s cadastradas;
- Mostrar o nome do usuário no visor LCD;
- Comunicar com o servidor para verificação da TAG;
- Manter o histórico de entrada e saída dos usuários;
- Equipamentos necessários:
- Hardware:
- Placa Node MCU – Atuará como controlador do hardware e fará a comunicação com o servidor;
- Visor LCD – Exibirá informações a respeito do acesso (liberado/negado/verificando);
- Leitor RFID RC 522 – Atuará na leitura das TAG’s RFID;
- Software:
- Hardware:
- Infraestrutura:
Preparação:
- Criando filas no MQTT:
Abra sua conta no CloudMQTT e crie uma nova instância. Essa instância será o host das suas filas MQTT. Com o host criado, abra os detalhes da instância, e crie os usuários que terão acesso a instância criada. Depois desça mais um pouco e crie as filas (Rules). Para este projeto, vamos precisar de fuas filas, uma para enviar informações no sentido NodeMCU -> Cloud9 e uma outra fila para fazer o sentido inverso.
As informações necessárias para a criação das filas são:
- O usuário que terá acesso
- O nome da fila (topic)
- O usuário poderá Ler e/ou Escrever na fila?
Depois de ter criado as filas, é hora de inciarmos a parte do servidor em si.
- Desenvolvimento parte 1 (Servidor):
Após criar uma conta no Cloud9 (C9), vamos criar o banco de dados da aplicação.
Crie um arquivo .sql e nele insira o seguinte código:
-- MySQL Workbench Forward Engineering SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; -- ----------------------------------------------------- -- Schema ac -- ----------------------------------------------------- DROP SCHEMA IF EXISTS `ac` ; -- ----------------------------------------------------- -- Schema ac -- ----------------------------------------------------- CREATE SCHEMA IF NOT EXISTS `ac` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; USE `ac` ; -- ----------------------------------------------------- -- Table `ac`.`User` -- ----------------------------------------------------- DROP TABLE IF EXISTS `ac`.`User` ; CREATE TABLE IF NOT EXISTS `ac`.`User` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '', `rfid` VARCHAR(45) NOT NULL COMMENT '', `name` VARCHAR(45) NOT NULL COMMENT '', PRIMARY KEY (`id`) COMMENT '', UNIQUE INDEX `rfid_UNIQUE` (`rfid` ASC) COMMENT '') ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `ac`.`History` -- ----------------------------------------------------- DROP TABLE IF EXISTS `ac`.`History` ; CREATE TABLE IF NOT EXISTS `ac`.`History` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '', `idUser` INT NOT NULL COMMENT '', `entry` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '', `saida` VARCHAR(45) NULL DEFAULT NULL COMMENT '', `status` CHAR NOT NULL DEFAULT 0 COMMENT '', PRIMARY KEY (`id`) COMMENT '', INDEX `fk_History_User_idx` (`idUser` ASC) COMMENT '', CONSTRAINT `fk_History_User` FOREIGN KEY (`idUser`) REFERENCES `ac`.`User` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; -- ----------------------------------------------------- -- Data for table `ac`.`User` -- ----------------------------------------------------- START TRANSACTION; USE `ac`; INSERT INTO `ac`.`User` (`id`, `rfid`, `name`) VALUES (DEFAULT, 'ae43d3b5', 'Guilherme Araujo'); INSERT INTO `ac`.`User` (`id`, `rfid`, `name`) VALUES (DEFAULT, 'd798b4f5', 'Matheus Uehara'); INSERT INTO `ac`.`User` (`id`, `rfid`, `name`) VALUES (DEFAULT, 'b3f7729a', 'Francois Michell'); COMMIT;
Nesse código sql estamos criando duas tabelas, uma aonde ficará as informações do usuário (TAG e nome),
e uma outra tabela onde ficarão as informações sobre o histórico de entrada e saida dos usuários.
para executar o script, basta executar o seguinte comando no prompt do Cloud9 (bash):
mysql -uroot -proot < ac.sql
sintaxe do comando: mysql -uUSER -pPASSWD < NOMEARQUIVO.sql
Depois, abra o terminal do C9 e instale o paho-mqtt com o seguinte comando:
pip install paho-mqtt
Após instalar o paho-mqtt crie um arquivo .py e utilize o código:
# FAZENDO OS IMPORTS NECESSARIOS PARA A APLICACAO import os, urlparse import paho.mqtt.client as mqtt import pymysql import cgitb from datetime import datetime # CONEXÃO COM O BANCO - DATABASE, USUÁRIO, SENHA E HOST conn = pymysql.connect( db='ac', user='root', passwd='root', host='localhost') c = conn.cursor() cgitb.enable() # CODIGO DE CONSULTA AO BANCO # VERIFICA SE O RFID PASSADO EXISTE NO BANCO # SE SIM, RETORNA UMA LISTA CONTENDO NOME E ID DO USUARIO CADASTRADO # NAQUELE RFID def consulta(num): retorno = {} retorno["userId"] = 0 retorno["userName"] = "" sql = "SELECT id,name FROM User WHERE rfid = '%s'" % (num) c.execute(sql) r = c.fetchall() if len(r) > 0: retorno["userId"] = int(r[0][0]) retorno["userName"] = r[0][1] + "" return retorno # VERIFICA SE DADO USUÁRIO POSSUI REGISTRO ABERTO ASSOCIADO A SEU RFID # CASO NÃO HAJA, O HORARIO E REGISTRADO E TEM SEU SATUS DEFINIDO COMO ABERTO (1). # CASO HAJA, O HORARIO E REGISTRADO E O STATUS DEFINIDO COMO FECHADO (0) def registro(userData): try: sql_consulta = "SELECT id FROM History WHERE idUser = %i AND status = 1;"/ % (userData["userId"]) c.execute(sql_consulta) r = c.fetchall() if len(r) > 0: timestamp = datetime.now() id_hist = r[0][0] sql_update = "UPDATE `History` SET `status` = 0, `saida` = '%s' WHERE / id = %i;" % (timestamp,id_hist) #print sql_update c.execute(sql_update) conn.commit() return "SAINDO/" + userData["userName"] else: sql_insert = "INSERT INTO History (idUser,status) VALUES (%i,1);"/ % (userData["userId"]) c.execute(sql_insert) conn.commit() return "ENTRANDO/" + userData["userName"] except: return "ERRO"; # SOBREESCREVEMOS O COMPORTAMENTO DE ALGUMAS # FUNCOES PROPRIAS DO MQTT # EXECUTADA QUANDO UMA NOVA CONEXAO E FEITA def on_connect(mosq, obj, rc): print("rc: " + str(rc)) # EXECUTADA QUANDO UMA NOVA MENSAGEM E LIDA NA FILA # PUBLICA NA FILA DE RESPOSTA SE O ACESSO FOI/NAO FOI LIBERADO # + O NOME DO CADASTRADO PARA EXIBICAO NO LCD def on_message(mosq, obj, msg): print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload)) cons = consulta(str(msg.payload)) if(cons["userName"] != ""): retorno = registro(cons) print(retorno) mqttc.publish("retorno", retorno) else: mqttc.publish("retorno", "FALSE") # EXECUTADO A CADA PUBLICACAO def on_publish(mosq, obj, mid): print("Publish: " + str(mid)) # EXECUTADO A CADA FILA QUE UM SUBSCRIBE E DADO def on_subscribe(mosq, obj, mid, granted_qos): print("Subscribed: " + str(mid) + " " + str(granted_qos)) # EXECUTADO EM CADA ESCRITA NO LOG def on_log(mosq, obj, level, string): print(string) # CRIACAO DO OBJETO DO TIPO mqtt.Client mqttc = mqtt.Client() # SOBRESCRITA DOS METODOS NATIVOS DO MQTT mqttc.on_message = on_message mqttc.on_connect = on_connect mqttc.on_publish = on_publish mqttc.on_subscribe = on_subscribe # URL DO CLOUDMQTT E DA INSTANCIA AONDE AS FILAS ESTAO # A URL DA INSTANCIA E COMPOSTA POR: mqtt://m12.cloudmqtt.com: + PORTA # PORTA PODE SER ENCONTRADO NAS INFORMACOES DA INSTANCIA url_str = os.environ.get('m12.cloudmqtt.com', 'mqtt://m12.cloudmqtt.com:PORTA') url = urlparse.urlparse(url_str) # ATRIBUICAO DO USUARIO COM ACESSO AS FILAS #os parametros do username_pw_set são os dados usuário e senha do MQTT mqttc.username_pw_set("USUARIO", "SENHA") mqttc.connect(url.hostname, url.port) # SUBSCRIBE NA FILA ACESSO mqttc.subscribe("acesso", 0) # LOOP ENQUANTO UM ERRO NAO FOR ENCONTRADO O NOSSO SERVIDOR ESTARÁ OUVINDO A FILA # ACESSO E ESCREVENDO AS RESPOSTAS NA FILA RETORNO rc = 0 while rc == 0: rc = mqttc.loop() print("rc: " + str(rc))
Pronto, o servidor foi finalizado.
Com o arquivo criado temos nosso código do servidor pronto, antes de dar run no nosso arquivo lembre-se de iniciar o apache.
- Desenvolvimento parte 2 (Hardware)
Baixe e instale a IDE do arduino em: https://www.arduino.cc/en/Main/Software
Com a IDE instalada siga os seguintes passos:
Ferramentas > Placa > Gerenciador de Placa > Pesquisar por ESP8266 e instalar
Ferramentas > Placa > selecionar ESP8266Modules
Arquivos > Preferencias > URLs adicionais e gerenciadores de placa : http://arduino.esp8266.com/package_esp8266com_index.json (Update 03/11/17: http://arduino.esp8266.com/stable/package_esp8266com_index.json)
Todos os links de bibliotecas do Github existentes no código devem ser baixados como .ZIP
Após baixar bibliotecas vá em Sketch > Incluir Biblioteca > Adicionar Biblioteca .ZIP *
*Realizar este procedimento para todas as bibliotecas.
Reiniciar o Arduino
Começando o código:
//Biblioteca wifi do nodeMCUESP8266 #include <ESP8266WiFi.h> //https://www.arduino.cc/en/Reference/SPI #include <SPI.h> //Biblioteca do RFID //https://github.com/miguelbalboa/rfid #include <MFRC522.h> //https://www.arduino.cc/en/Reference/Wire #include <Wire.h> //Biblioteca do display LCD //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #include <LiquidCrystal_I2C.h> //Biblioteca do clientMQTT //http://pubsubclient.knolleary.net/api.html //https://github.com/knolleary/pubsubclient #include <PubSubClient.h> //Pinos do RFID #define RST_PIN 15 // RST-PIN für RC522 - RFID - SPI - Modul GPIO15 #define SS_PIN 2 // SDA-PIN für RC522 - RFID - SPI - Modul GPIO2 //Criando WIFIClient WiFiClient espClient; //Criando o clientMQTT com o wificlient PubSubClient client(espClient); //Criando LCD com posições dos pinos,tamanho da linha, tamanho coluna. LiquidCrystal_I2C lcd(0x27,16,2); //Criando RFID nas posições dos pinos. MFRC522 mfrc522(SS_PIN, RST_PIN); //WIFI parametros. const char* ssid = "NOME_DA_REDE_WIFI"; const char* password = "SENHA"; //MQTT parametros. const char* mqtt_server = "m12.cloudmqtt.com"; const int mqtt_port = PORTA_MQTT; void setup() { //Definindo porta de saida do Serial Serial.begin(9600); //Chamando método de conexão WIFI setup_wifi(); //Definindo server mqtt do Client client.setServer(mqtt_server, mqtt_port); //Definindo método callback que irá receber os callbacks do client criado. client.setCallback(callback); // Init SPI bus SPI.begin(); //Iniciando PCD do RFID mfrc522.PCD_Init(); // sda, scl With sda cable connected to D2 and scl cable connected to D1. Wire.begin(4, 5); //Iniciando LCD lcd.begin(); printLCD("Passe o Cartao!"); } //Método de conexão com rede WIFI void setup_wifi() { delay(10); Serial.println(); Serial.print("Conectando com "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi conectado"); Serial.println("Endereco IP : "); Serial.println(WiFi.localIP()); } //Método de conexão com servidor MQTT void conectMqtt() { while (!client.connected()) { Serial.print("Conectando ao MQTT ..."); //Parametros são nodeMCUClient, usuárioMQTT, senhaMQTT if (client.connect("ESP8266Client","USUARIO_MQTT","SENHA_MQTT")) { Serial.println("Conectado"); //Inscrevendo-se no tópico retorno. client.subscribe("retorno"); } else { Serial.print("Falha, rc="); Serial.print(client.state()); Serial.println(" Tentando novamente em 5 segundos"); // Wait 5 seconds before retrying delay(5000); } } } //Método que foi definido para receber os retornos dos tópicos que demos subscribe, // neste caso apenas o tópico 'retorno' //Parametros: NomedoTópico, mensagem , tamanho da mensagem void callback(char* topic, byte* payload, unsigned int length) { Serial.println(); Serial.print("Messagem recebida ["); Serial.print(topic); Serial.print("]: "); String mensagem = ""; //Conversão da mensagem recebidade de byte pra String for (int i = 0; i < length; i++) { mensagem += (char)payload[i]; } Serial.println(mensagem); Serial.println(); //Chamada ao método que controla o acesso verificaAcesso(mensagem); } //Método de controle de acesso. void verificaAcesso(String mensagem){ //Estrutura da mensagem recebida // ENTRANDO/Nome_do_dono_do_cartao // SAINDO/Nome_do_dono_do_cartao // Retorno FALSE é dado sempre que o id do cartão não existe no servidor //ou ocorreu alguma falha de validação // FALSE if ( mensagem.substring(0,8) == "ENTRANDO" ){ printLCD("BEM VINDO"); delay(1000); printLCD(mensagem.substring(9)); delay(1000); }else if (mensagem.substring(0,6) == "SAINDO" ){ printLCD("ATE MAIS"); delay(1000); printLCD(mensagem.substring(7)); delay(1000); }else{ printLCD("Acesso negado!"); delay(1000); } delay(1000); printLCD("Passe o Cartao!"); return; } //Método utilitário para print no display LCD //Nele variamos apenas a mensagem da segunda linha void printLCD(String mensagem){ lcd.clear(); lcd.setCursor(0,0); lcd.print("Infra-Hardware"); lcd.setCursor(0,1); lcd.print(mensagem); } //Método de envio do id do cartão lido pra fila acesso void sendMessage(MFRC522 mfrc522){ printLCD("Lendo Cartao"); char rfidstr[15]; char s[100]; for (int i = 0; i < mfrc522.uid.size; i++){ //Conversão de byte pra Hexadecimal sprintf(s,"%x",mfrc522.uid.uidByte[i]); //Concatenando para o array de char que será enviado strcat( &rfidstr[i] , s); } Serial.print("Card ID : "); Serial.print(rfidstr); //Publicando na fila acesso o id do cartão lido client.publish("acesso", rfidstr); Serial.println(); printLCD("Verificando..."); return; } void loop() { //Verificando Status do ClientMQTT if (!client.connected()) { conectMqtt(); } client.loop(); //Verificando existencia do card no leitor if ( ! mfrc522.PICC_IsNewCardPresent()) { delay(1000); return; } //Verificando Leitura do card if ( ! mfrc522.PICC_ReadCardSerial()) { delay(1000); return; } //Enviando mensagem sendMessage(mfrc522); }
Após tudo concluido, inicie o código no Cloud9 dando RUN no arquivo .py criado para iniciar o servidor e depois execute o código no NodeMCU pra ver a magia acontecer!
Qual o esquema de ligação dos pinos? Não utilizará miso e mosi?
CurtirCurtir
Poderia detalhar mais o procedimento de como criar um arquivo .stl no cloud9?
CurtirCurtir
Não estou sabendo o que é o arquivo “.stl”, mas acho que seria o “.sql”. Se for este é só criar um arquivo de texto normal.
CurtirCurtir
Muito bom exemplo, só fiquei com dúvida de como ligar o leitor rfid no nodemcu? Poderia detalhar?
Valeu.
CurtirCurtir
Olá Leandro, obrigado pela visita.
Esta versão se baseia em outra que tínhamos com Arduino. Como não tinha diferença na ligação física, acabou ficando de fora. Mas você pode ter uma ideia vendo o post com o Arduino aqui: https://jualabs.wordpress.com/2015/07/23/controle-de-acesso-com-arduinorfid/
CurtirCurtir
Bom dia! Glauco é possivel postar a ligação dos pinos do leito RFID, da forma que liguei não funcionou, apesar de ter visto o artigo citado acima, ainda to com duvidas na ligação.
CurtirCurtir
Boa noite Glauco, olha amigo, não consegui criar a conta no C9 porque ele pede para usar cartão de credito e n tenho, tem outra opção de criar conta la, ou fazer um turtorial criando o database + scrpyt e broker no windows?
CurtirCurtir
Olá Maurício. Realmente, desde que a Amazon comprou o C9 precisa do cartão. Se você deseja fazer um controle de acesso com todos os serviços locais, pode usar um broker mais simples como o mosquitto. O mysql você consegue instalar no windows sem maiores problemas. E o script, por ser python, você consegue rodar também. Então acho que dá pra fazer tranquilo.
CurtirCurtir
Olá como eu crio o banco de dados no cloud9? É dentro do workspace?
CurtirCurtir
Olá Jônata. Você pode criar o banco no workspace do C9, basta instalar via terminal.
CurtirCurtir
Olá, também estou com dúvida na criação do banco de dados no c9. Poderia dizer como fazer isso? Visto que eu já cadastrei meu cartão e nunca trabalhei com essa IDE.
CurtirCurtir
Olá Igor. Você pode criar o banco no workspace do C9, basta instalar via terminal, o comando abaixo deve resolver:
sudo apt-get install mysql-server-5.6
CurtirCurtir
Olá! Consegue me dizer se teria como enviar o codigo da tag rfid diretamente a uma impressora para permitir ou não o acesso? Obrigado!
CurtirCurtir
http://arduino.esp8266.com/package_esp8266com_index.json – Esse link esta com problemas, alguem sabe o motivo?
CurtirCurtir
Olá Jhow. Realmente este link parece estar com problemas aqui. Tente este outro: http://arduino.esp8266.com/stable/package_esp8266com_index.json
CurtirCurtir