Introdução
Com o propósito de proporcionar maior agilidade à entrada dos alunos ao Restaurante Universitário (RU) da Universidade Federal Rural de Pernambuco (UFRPE), o projeto em questão propõe uma solução baseada em um cartão RFId para identificar e gerenciar o crédito dos alunos para aquisição das refeições ofertadas pelo restaurante.
Para viabilizar a construção do projeto, as seguintes tecnologias e dispositivos foram utilizados:
- NodeMCU;
- Módulo relé;
- Leitor RFId – (RC522);
- Tags RFId;
- Display LCD 16×2;
- Protocolo MQTT;
- C/C++ (Programação do NodeMCU);
- Java + XML (Cliente Android);
- Python (Serviço Web);
- MySQL (Banco de Dados).
A primeira versão da solução possui as seguintes funcionalidades:
- Verificar se o aluno está cadastrado na base de dados;
- Verificar se o aluno possui saldo suficiente;
- Cadastrar o aluno na base de dados;
- Liberar a catraca;
- Debitar o valor da refeição no saldo atual do aluno;
- Realizar a recarga de saldo;
- Consultar o saldo.
Equipamentos necessários
- Plataforma NodeMCU – Atuará como controlador de toda a plataforma de hardware e fará a comunicação com o servidor;
- Display LCD – Exibirá informações relacionadas ao controle do acesso (liberado/negado/verificando);
- Leitor RFID RC 522 – Atuará na leitura das tags RFId;
- Relé – Será utilizado para liberar a catraca.
Arquitetura da solução
Passos para a implementação
Criando conta para os serviços na nuvem
Para o funcionamento do nosso controle de pagamento através de RFId utilizaremos dois serviços gratuitos disponíveis na nuvem: O CloudMQTT, para implementação dos serviços de comunicação através de filas; e o Cloud9, para hospedagem do Banco de Dados MySQL.
Criando as filas no MQTT
Após a criação da conta no serviço CloudMQTT, logue no sistema e crie uma nova instância. O host das suas filas MQTT será definido após isso. Com o host criado, abra os detalhes da instância, e crie os usuários que terão acesso a instância criada. Crie as filas (rules) para as mensagens do sistema. Para este projeto, vamos precisar de duas filas, uma para enviar informações no sentido NodeMCU → Cloud9 e uma outra fila para fazer o sentido contrário.
Servidor
Banco de dados
Hora de criar o banco de dados MySQL no servidor do serviço C9:
Crie um arquivo bancoru.sql . Depois abra o arquivo e monte o código:
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 dbru -- ----------------------------------------------------- DROP SCHEMA IF EXISTS `dbru` ; -- ----------------------------------------------------- -- Schema dbru -- ----------------------------------------------------- CREATE SCHEMA IF NOT EXISTS `dbru` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; USE `dbru` ; -- ----------------------------------------------------- -- Table `dbru`.`Usuario` -- ----------------------------------------------------- DROP TABLE IF EXISTS `dbru`.`Usuario` ; CREATE TABLE IF NOT EXISTS `dbru`.`Usuario` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '', `rfid` VARCHAR(45) NOT NULL COMMENT '', `nome` VARCHAR(45) NOT NULL COMMENT '', `cpf` integer(11) NOT NULL COMMENT '', `saldo` float(10) NOT NULL COMMENT '', PRIMARY KEY (`id`) COMMENT '', UNIQUE INDEX `rfid_UNIQUE` (`rfid` ASC) COMMENT '') ENGINE = InnoDB; SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
O banco terá apenas uma tabela “Usuario” que terá as seguintes colunas: código RFId, Nome do aluno, CPF do aluno e saldo do cartão.
para executar o script, basta executar o seguinte comando no prompt do Cloud9 (bash):
mysql -uroot -proot < bancoru.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 o arquivo 'servidor.py'. Depois edite o arquivo com o código:
import json import sys import os, urlparse import paho.mqtt.client as mqtt import pymysql from datetime import datetime conn = pymysql.connect( db = 'rurural', user = 'root', passwd = 'root', host = '127.0.0.1') cursorBD = conn.cursor() queries = {} queries['nodeSaldo'] = "SELECT saldo FROM usuario WHERE rfid = '%s' AND cpf <> ''" queries['SALDO'] = "SELECT saldo FROM usuario WHERE cpf = '%s'" queries['CADASTRO'] = "INSERT INTO usuario (rfid, nome, cpf, saldo) VALUES ('%s', '%s', '%s', '%s')" queries['RECARGA'] = "UPDATE usuario SET saldo = '%s' WHERE cpf = '%s'" #/////////// # id AI / # rfid / # nome / # cpf / # saldo / #/////////// def decrementaSaldo(valor, rfid): queryDesconto = "UPDATE usuario SET saldo = %.2f WHERE rfid = '%s'" % (valor, rfid) cursorBD.execute(queryDesconto) conn.commit() print(cursorBD._last_executed) return # VERIFICA SE O RFID PASSADO EXISTE NO BANCO, SE SIM, RETORNA SALDO JA DESCONTADO def consultaNode(rfid): #TO DO Testar charset JSON. #TO DO Decrementar sal na base. #TO DO Modificar caso para usuario inexistente, deve-ser verificar se os campos nome+cpf estao vazios. retornoJson = {} saldoDescontado = 0.00 case = "" queryConsultaNode = queries['nodeSaldo'] % (rfid) cursorBD.execute(queryConsultaNode) retornoQuery = cursorBD.fetchall() if(len(retornoQuery) > 0): #RFID valido saldoAtual = float(retornoQuery[0][0]) if((datetime.now().hour-3) < 16): #Almoco #Subtracao por tres dada a localizacao do servidor e o impacto do fuso horario if(saldoAtual >= 2.00): saldoDescontado = (saldoAtual - 2.00) decrementaSaldo(saldoDescontado, rfid) case = "sucesso_consultaNode" else: case = "erro_saldoInsuficienteNode" else: #Jantar if(saldoAtual >= 1.50): saldoDescontado = (saldoAtual - 1.50) decrementaSaldo(saldoDescontado, rfid) case = "sucesso_consultaNode" else: case = "erro_saldoInsuficienteNode" else: #RFID invalido case = "erro_usuarioInexistente" if(case == "sucesso_consultaNode"): retornoJson["STATUS"] = 0 retornoJson["saldoDescontado"] = "%.2f" % saldoDescontado elif(case == "erro_usuarioInexistente"): retornoJson["STATUS"] = 1 elif(case == "erro_saldoInsuficienteNode"): retornoJson["STATUS"] = 2 return retornoJson def consultaAndroid(parametro): pass #######Node def on_connect_filaNode(self, mosq, obj, rc): print("rc: " + str(rc)) def on_message_filaNode(mosq, obj, msg): print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload)) mensagemJson = json.loads(str(msg.payload)) rfid = mensagemJson['RFID'] retornoJson = consultaNode(str(rfid)) mqttcFilaAndroid.publish("retornoNode", json.dumps(retornoJson)) print(retornoJson) def on_publish_filaNode(mosq, obj, mid): print("Publish: " + str(mid)) def on_subscribe_filaNode(mosq, obj, mid, granted_qos): print("Subscribed: " + str(mid) + " " + str(granted_qos)) #######Node #######Android def on_connect_filaAndroid(self, mosq, obj, rc): print("rc: " + str(rc)) def on_message_filaAndroid(mosq, obj, msg): pass def on_publish_filaAndroid(mosq, obj, mid): print("Publish: " + str(mid)) def on_subscribe_filaAndroid(mosq, obj, mid, granted_qos): print("Subscribed: " + str(mid) + " " + str(granted_qos)) def on_log(mosq, obj, level, string): print(string) #######Android mqttcFilaNode = mqtt.Client() mqttcFilaAndroid = mqtt.Client() mqttcFilaNode.on_message = on_message_filaNode mqttcFilaNode.on_connect = on_connect_filaNode mqttcFilaNode.on_publish = on_publish_filaNode mqttcFilaNode.on_subscribe = on_subscribe_filaNode mqttcFilaAndroid.on_message = on_message_filaAndroid mqttcFilaAndroid.on_connect = on_connect_filaAndroid mqttcFilaAndroid.on_publish = on_publish_filaAndroid mqttcFilaAndroid.on_subscribe = on_subscribe_filaAndroid url_str = os.environ.get('m13.cloudmqtt.com','mqtt://m13.cloudmqtt.com:13988') url = urlparse.urlparse(url_str) mqttcFilaNode.username_pw_set("romero", "123") mqttcFilaNode.connect(url.hostname, url.port) mqttcFilaAndroid.username_pw_set("romero", "123") mqttcFilaAndroid.connect(url.hostname, url.port) mqttcFilaNode.subscribe("acessoNode", 0) mqttcFilaAndroid.subscribe("acessoAndroid", 0) rcFilaNode = 0 rcFilaAndroid = 0 while rcFilaNode == 0 or rcFilaAndroid == 0: rcFilaNode = mqttcFilaNode.loop() rcFilaAndroid = mqttcFilaAndroid.loop() print("rcFilaNode:" + str(rcFilaNode) + " | rcFilaAndroid:" + str(rcFilaAndroid) )
Após isso o servidor Python estará pronto para funcionar!
Cliente
Android
Como parte do projeto, deve haver uma parte de integração do servidor com os clientes através de um App Android, que será responsável por cadastrar o cartão, verificar saldo e se comunicar com o servidor.
Você pode baixar o exemplo do cliente Android no repositório indicado abaixo:
https://github.com/romeroclaudino/RUrural/tree/master/clientAndroid
Utilize o Android Studio para editar seu App Android. Depois do App montado, podemos continuar com o projeto, seguindo para a parte do hardware.
Hardware
Conecte os dispositivos de hardware ao NodeMCU conforme Figura abaixo.
Baixe e instale a IDE do Arduino.
Com a IDE instalada siga os seguintes passos:
- Arquivos > Preferencias > URLs adicionais e gerenciadores de placa: http://arduino.esp8266.com/package_esp8266com_index.json
- Ferramentas > Placa > Gerenciador de Placa > Pesquisar por ESP8266 e instalar
- Ferramentas > Placa > selecionar ESP8266Modules
Observação: Todas as bibliotecas utilizadas no projeto devem ser baixadas de seus respectivos repositórios no github no formato ‘.zip’. Depois de baixadas, as bibliotecas podem ser instaladas através do menu: Sketch > Incluir Biblioteca > Adicionar Biblioteca ‘.zip’.
Abra a IDE do Arduino e cole o código abaixo:
//Definindo WI-FI #include #include WiFiClient espClient;<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span> //Parametros Wi-Fi const char* ssid = ""; //Nome da rede const char* password = ""; //Senha da Rede //Definindo LCD #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); //Definindo RFID #include #define RST_PIN D3 // RST-PIN for RC522 - RFID - SPI - Modul GPIO15 #define SS_PIN D4 // SDA-PIN for RC522 - RFID - SPI - Modul GPIO2 MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 inst //Definindo MQTT #include PubSubClient client(espClient); //Parametros MQTT const char* mqtt_server = "m13.cloudmqtt.com"; //server MQTT const int mqtt_port = 13988; //Porta MQTT //Biblioteca para tratar Json #include int rele = D8; void setup() { // put your setup code here, to run once: Serial.begin(9600); // Initialize serial communications setup_wifi(); // Conecta ao WiFi client.setServer(mqtt_server, mqtt_port); // definindo server mqtt do client client.setCallback(callback); SPI.begin(); // Init SPI bus mfrc522.PCD_Init(); // Init MFRC522 lcd.init(); Wire.begin(4, 5); pinMode(rele, OUTPUT); lcd.begin(); lcd.backlight(); lcd.setCursor(0,0); lcd.print(" Passe o Cartao"); } //Configurando e Conectando Wi-Fi void setup_wifi() { delay(10); Serial.println(); Serial.print("Conectando"); 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()); } //Conectando a fila do MQTT void conectMqtt() { while (!client.connected()) { Serial.print("ConectandoQTT ..."); //Parametros são nodeMCUClient, usuárioMQTT, senhaMQTT if (client.connect("ESP8266Client","romero","123")) { Serial.println("Conectado"); //Inscrevendo-se no tópico retorno. client.subscribe("retornoNode"); } else { Serial.print("Falha"); Serial.print(client.state()); Serial.println(" Tentando novamente em 5 segundos"); // Wait 5 seconds before retrying delay(5000); } } } //Tratando resposta do MQTT void callback(char* topic, byte* payload, unsigned int length) { Serial.println(); 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(); //converte mensagem para Char char jsonChar[mensagem.length()]; mensagem.toCharArray(jsonChar, mensagem.length() + 1); //chamada ao Metodo que trata o Json jsonDecode(jsonChar); // jsonString = ""; //Chamada ao método que controla o acesso // verificaAcesso(mensagem); } //Metodo para tratar o Json void jsonDecode(char *json){ StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.parseObject(json); int op = root["STATUS"]; if (op == 0){ String saldo = root["saldoDescontado"]; digitalWrite(rele, HIGH); printLCD("Saldo: " + saldo); delay(3000); digitalWrite(rele, LOW); } else if (op == 1){ printLCD("Usuario", "Inexistente"); } else if (op == 2) { printLCD(" Saldo", "Insuficiente"); } } //Enviando Mensagem ao MQTT void sendMessage(String mfrc522){ lcd.setCursor(2,0); lcd.print("Lendo o Cartao"); String mensagem = "{\"RFID\":\""+mfrc522+"\"}"; // Transformando a String em char para poder publicar no mqtt char charpub[mensagem.length() + 1]; mensagem.toCharArray(charpub, mensagem.length()+1); Serial.print("Card "); Serial.print(mensagem); //Publicando na fila acesso o id do cartão lido client.publish("acessoNode", charpub); Serial.println(); lcd.setCursor(2, 1); lcd.print("Verificando..."); return; } //Mostrar Mensagem na Tela void printLCD(String mensagem){ lcd.clear(); lcd.setCursor(5,0); lcd.print("Sucesso!"); lcd.setCursor(3,1); lcd.print(mensagem); delay(4000); lcd.clear(); lcd.print(" Passe o Cartao"); } void printLCD(String linha1, String linha2){ lcd.clear(); lcd.setCursor(5,0); lcd.print(linha1); lcd.setCursor(3,1); lcd.print(linha2); delay(4000); lcd.clear(); lcd.print(" Passe o Cartao"); } // Metodo que Retorna o ID da Tag como String String dump_byte_array(byte *buffer, byte bufferSize) { String uuid; for (byte i = 0; i < bufferSize; i++) { uuid = uuid + String(buffer[i], HEX); } return uuid; } void loop() { //Verificando Status do ClientMQTT if (!client.connected()) { conectMqtt(); } client.loop(); // Look for new cards if ( ! mfrc522.PICC_IsNewCardPresent()) { delay(50); return; } // Select one of the cards if ( ! mfrc522.PICC_ReadCardSerial()) { delay(50); return; } lcd.clear(); String uid = dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); sendMessage(uid); delay(4000); lcd.clear(); lcd.print(" Passe o Cartao"); }
Depois de colar o código acima, envie-o para o NodeMCU. Feito isso, terminamos de codificar todas as partes do nosso sistema. Inicie o código no Cloud9 dando RUN no arquivo ‘.py’ criado para iniciar o servidor e depois inicie o NodeMCU. Et voilà!
Gostaria de saber qual o preço do projeto já em funcionamento.
CurtirCurtir