18 de novembro de 2017

Controle de Acesso com RFID e EEPROM

Controlar o acesso através de tags RFID (Radio Frequency IDentification) é uma solução muito utilizada nos dias hoje.
Esse projeto armazena os cartões cadastrados na memória EEPROM.



Utilizei o módulo RFID RC522, um servo motor SG90, e um Arduino Nano.

Para gravar as tags na EEPROM, utilizei a MemoryLib, que facilita muito o gerenciamento da memória.
Para 1Kb de memória é possível cadastrar 101 cartões de acesso!

A biblioteca para uso do leitor RFID é a MFRC522.

Segue o esquema elétrico e o código fonte com comentários para fácil entendimento.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*---------------------------------------------------------
  Programa : CONTROLE DE ACESSO - RFID / EEPROM
  Autor    : Fellipe Couto [ http://www.efeitonerd.com.br ]
  Data     : 18/11/2017
  ---------------------------------------------------------*/

/* INSTRUÇÕES:
 * - Para cadastrar um novo acesso, pressione o botão e aproxime o novo cartão no leitor.
 * - Para remover um acesso já cadastrado, pressione o botão e aproxime o cartão cadastrado no leitor.
 * - Para apagar todos os cartões já cadastrados, mantenha o botão pressionado até somente o led verde permanecer acesso.
 */

/* --- RFID - MFRC522 ---
  SDA  = pin 10
  SCK  = pin 13
  MOSI = pin 11
  MISO = pin 12
  RST  = pino 9
  GND  = Terra
  Vcc  = 3,3v
*/

#include <SPI.h>       //Biblioteca de comunicação SPI, para comunição do módulo RFID
#include <MFRC522.h>   //Biblioteca do módulo RFID
#include <EEPROM.h>    //Biblioteca da memória EEPROM
#include <MemoryLib.h> //Biblioteca para gerenciar a EEPROM com variáveis
#include <Servo.h>     //Biblioteca do Servo Motor

#define ledGreen 2   //Led verde no pino 2
#define ledRed 3     //Led vermelho no pino 3
#define button 4     //Botão de cadastro no pino 4
#define servo 5      //Servo motor no pino 5
#define posLock 10   //Posição do servo quando fechado
#define posUnlock 90 //Posição do servo quando aberto
#define timeUnlock 3 //Tempo que o servo permance aberto (em segundos)
#define SDA_RFID 10  //SDA do RFID no pino 10
#define RST_RFID 9   //Reset do RFID no pino 9

/*----- VARIÁVEIS -----*/
MFRC522 mfrc522(SDA_RFID, RST_RFID);   //Inicializa o módulo RFID
MemoryLib memory(1, 2);                //Inicializa a biblioteca MemoryLib. Parametros: memorySize=1 (1Kb) / type=2 (LONG)
Servo myservo;                         //Inicializa o servo motor
int maxCards = memory.lastAddress / 2; //Cada cartão ocupa duas posições na memória. Para 1Kb será permitido o cadastro de 101 cartões
String cards[101] = {};                //Array com os cartões cadastrados

/*----- SETUP -----*/
void setup() {
  //Configura os pinos
  pinMode(ledGreen, OUTPUT);
  pinMode(ledRed, OUTPUT);
  pinMode(button, INPUT_PULLUP);

  //Inicia SPI
  SPI.begin();

  //Inicia o modulo RFID MFRC522
  mfrc522.PCD_Init();

  //Configura o pino do Servo Motor
  myservo.attach(servo);

  //Posiciona o servo na posição inicial
  myservo.write(posLock);

  //Retorna os cartões armazenados na memória EEPROM para o array
  ReadMemory();

  //Pisca os leds sinalizando a inicialização do circuito
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledRed, HIGH);
    digitalWrite(ledGreen, HIGH);
    delay(50);
    digitalWrite(ledRed, LOW);
    digitalWrite(ledGreen, LOW);
    delay(50);
  }
}

/*----- LOOP PRINCIPAL -----*/
void loop() {
  //Variável UID recebe o valor do cartão lido
  String UID = ReadCard();

  if (UID != "") {
    boolean access = false;
    //Efetua a leitura de todas as posições do array
    for (int c = 0; c <= maxCards; c++) {
      if (UID == cards[c]) {
        //Se a posição do array for igual ao cartão lido, seta a varíavel como verdadeira e finaliza o for
        access = true;
        break;
      }
    }
    //Variável verdadeira, efetua o acesso. Caso contrário, pisca o led vermelho
    if (access) {
      Unlock();
    } else {
      for (int i = 0; i < 6; i++) {
        digitalWrite(ledRed, HIGH);
        delay(50);
        digitalWrite(ledRed, LOW);
        delay(50);
      }
      delay(500);
    }
  }

  //Verifica se o botão para castrado de novo cartão foi pressionado
  if (digitalRead(button) == LOW) { //Botão pressionado igual a LOW devido a declaração de PULL_UP
    //Grava novo cartão, ou apaga um cartão já cadastrado
    RecUser();

    //Após os leds apagarem, se o botão permanecer pressionado, apaga todos os cartões
    if (digitalRead(button) == LOW) {
      digitalWrite(ledGreen, HIGH);
      //Escreve zero em todos os enderecos da EEPROM
      for (int address = 0; address <= memory.lastAddress; address++) {
        memory.write(address, 0);
      }
      ReadMemory(); //Atualiza os valores do array
      digitalWrite(ledGreen, LOW);
    }
  }
}

/*----- ABRE O ACESSO -----*/
void Unlock() {
  int posServo = myservo.read();
  //Abre o acesso
  digitalWrite(ledGreen, HIGH);
  digitalWrite(ledRed, LOW);
  for (int i = posServo; i <= posUnlock; i++) {
    myservo.write(i);
    delay(10);
  }
  delay(timeUnlock * 1000);
  digitalWrite(ledGreen, LOW);
  //Fecha o acesso
  digitalWrite(ledRed, HIGH);
  for (int i = posUnlock; i >= posLock; i--) {
    myservo.write(i);
    delay(10);
  }
  delay(500);
  digitalWrite(ledRed, LOW);
}

/*----- EFETUA A LEITURA DO CARTAO RFID -----*/
String ReadCard() {
  String UID = "";
  //Verifica a presença de um novo cartao e efetua a leitura
  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
    //Retorna o UID para a variavel UIDcard
    for (byte i = 0; i < mfrc522.uid.size; i++) {
      UID += String(mfrc522.uid.uidByte[i]);
    }
  }
  return UID;
}

/*----- CADASTRA CARTÃO DE ACESSO -----*/
void RecUser() {
  String UID = "";
  boolean deleteCard = false;

  //Efetua a leitura do cartao para cadastro
  //Aprox 5 segundos para cadastrar o cartao
  for (int t = 0; t < 150; t++) {
    delay(10);
    //Acende os leds verde e vermelho
    digitalWrite(ledGreen, HIGH);
    digitalWrite(ledRed, HIGH);
    //Faz a leitura o cartao
    UID = ReadCard();
    if (UID != "") {
      //Se leu um cartão, mantém somente o led verde acesso enquanto segue executando o procedimento
      digitalWrite(ledRed, LOW);
      //Verifica se o cartao ja esta cadastrado
      for (int c = 0; c < maxCards; c++) {
        //Se já estiver cadastrado, exclui o cartão do array e também da memória
        if (cards[c] == UID) {
          digitalWrite(ledRed, HIGH);
          digitalWrite(ledGreen, LOW);
          cards[c] = "0";
          deleteCard = true;
        }
      }
      break; //finaliza o for
    }
  }

  //Cancela o cadastro se nenhum cartao foi lido
  if (UID == "") {
    //Apaga os leds verde e vermelho
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledRed, LOW);
    return;
  }

  if (deleteCard == false) {
    //Se for inclusão de novo cartão, verifica se ainda existe espaco para novos cadastros
    //Se a última posição da memória for diferente de zero, pisca o led vermelho sinalizando
    //que não existe mais espaço para novos cartões, e finaliza o procedimento
    if (cards[maxCards - 1].toInt()  != 0) {
      digitalWrite(ledGreen, LOW);
      for (int i = 0; i < 10; i++) {
        digitalWrite(ledRed, HIGH);
        delay(100);
        digitalWrite(ledRed, LOW);
        delay(100);
      }
      return;
    }
  }

  //Adiciona o cartão no array, somente se for inclusão de novo cartão
  if (deleteCard == false) {
    for (int c = 0; c < maxCards; c++) {
      if (cards[c].toInt() == 0) { //Posicao livre
        cards[c] = UID;
        break; //finaliza o for
      }
    }
  }

  //Grava na memória os cartões do array
  //Cada cartão ocupa duas posições da memória
  for (int e = 0; e <= memory.lastAddress; e++) { //Limpa os valores da memória
    memory.write(e, 0);
  }
  int a = 0;
  for (int c = 0; c < maxCards; c++) {
    if (cards[c].toInt() != 0) {
      memory.write(a, cards[c].substring(0, 6).toInt());
      memory.write(a + 1, cards[c].substring(6, cards[c].length()).toInt());
      a += 2;
    }
  }

  //Retorna os valores da memória para o array, para ajustar as posições do cartão no array como está na memória
  ReadMemory();

  if (deleteCard == false) {
    Unlock();
  } else {
    //Apaga os leds verde e vermelho
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledRed, LOW);
  }
}

/*----- RETORNA OS DADOS DA MEMÓRIA PARA O ARRAY -----*/
void ReadMemory() {
  int a = 0;
  for (int c = 0; c < maxCards; c++) {
    cards[c] = String(memory.read(a)) + String(memory.read(a + 1));
    a += 2;
  }
}

10 comentários:

  1. Bem legal e interessante!
    Já pode vender para empresas, inclusive Metrô, trem e ônibus. rs

    ResponderExcluir
  2. A utilização com a sua lib ficou muito bom!
    Poderia adicionar o recurso de salvar strings tbm. Ficaria excelente!

    ResponderExcluir
    Respostas
    1. Boa tarde!
      Obrigado!
      Adicionar o recurso para strings já está na minha lista de tarefas! :)
      Abraços!

      Excluir
  3. Excelente!!
    Só uma dúvida com relação a capacidade de cartões, desculpa minha ignorância, mas se você tem 1024 de memoria e cada inteiro ocupa duas posições, não seria 1024/2 o que daria 512 carões?

    ResponderExcluir
    Respostas
    1. Boa tarde, Hermes!
      Na verdade o ID do cartão possui vários caracteres. Utilizando o biblioteca de gerenciamente da EEPROM, cada cartão ocupará dois endereços lógicos criados através do biblioteca.
      Dê uma olhada no post explicativo da biblioteca que conseguirá entender melhor:
      http://www.efeitonerd.com.br/2014/06/memorylibh-eeprom.html
      Abraços!

      Excluir
  4. Bom dia Fellipe Couto;
    ao usar a bibloteca MemoryLib.h para compilar este seu sketch, retornou o seguinte erro:

    error: invalid use of member function (did you forget the '()' ?)
    val = val.substring(String(value).length(), 10 + String(value).length
    ^

    Faltam um () ) ; no final da linha 44 da MemoryLib.cpp.

    Corrigi e compilou corretamente.

    Att:

    Rui

    ResponderExcluir
    Respostas
    1. Bom dia, Rui Lopes Viana!
      Obrigado pela informação! Realmente no github está com esse erro na linha 44. Fiquei confuso pois verifiquei agora meu backup e não está com o erro. Houve algum problema quando fui copiar para lá.

      Ajustado no github:
      https://github.com/fellipecouto/MemoryLib.h

      Muito obrigado!
      Abraços!!

      Excluir
  5. Boa tarde... muito top seu trabalho, gostaria de saber se teria como criar 2 tipos de armazenamentos na EEPROM,
    EX Aluno e outro PROFESSOR ?

    ResponderExcluir
    Respostas
    1. Bom dia, Washington Junio!
      Até teria como, mas para esse tipo de armazenamento a EEPROM do Arduino não seria a melhor opção. String ocuparia mais espaço ficando bem limitado. Se caso a necessidade for para salvar poucos nomes, poderia atender.
      Talvez usar outros recursos como SSD seria uma melhor opção, pois teria possibilidades de muitos nomes gravados.
      Abraços!

      Excluir