Sistema de pagamento automatizado utilizando NodeMCU, MQTT e RFId

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.

20170820_211059-ANIMATION

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

arquitetura

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.

WhatsApp Image 2017-08-23 at 21.42.04

Baixe e instale a IDE do Arduino.

Com a IDE instalada siga os seguintes passos:

  1. Arquivos > Preferencias > URLs adicionais e gerenciadores de placa: http://arduino.esp8266.com/package_esp8266com_index.json
  2. Ferramentas > Placa > Gerenciador de Placa > Pesquisar por ESP8266 e instalar
  3. 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à!

Um comentário sobre “Sistema de pagamento automatizado utilizando NodeMCU, MQTT e RFId

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s