RFID bralnik MFRC-522, Krmiljenje zapornice

RFID bralnik RC-522 blagovne znamke Mifare spada v skupino HF (High Frequency) in deluje na frekvenci 13,56 MHz. Za delovanje potrebuje napetostno napajanje 3,3 V. Branje kartic oz. tag-ov je omogočeno do razdalje cca. 6 cm od bralnika (uradno do 10 cm). Kartice, ki jih bere so Mifare 1 S50, Mifare 1 S70, Mifare UltraLight, Mifare Pro in Mifare DESFire. Ostale karakteristične podatke za RFID bralnik RC-522 najdemo na tej povezavi.

Slika 1: RFID RC522 (vir: http://air.imag.fr/index.php/File:RFID-RC522_RF_IC_Card_Sensor_Module_203517.jpg)

Priključitev RFID RC522 na Arduino:

Arduino Uno in RFID RC522 komunicirata preko SPI (Serial Peripheral Interface) vodila.

Slika 2: SPI vodilo: nadrejena in podrejena naprava (vir: https://sl.wikipedia.org/wiki/SPI)

SPI je standard za sinhrono serijsko podatkovno povezavo elektronskih naprav, ki deluje v dvosmernem načinu. Protokol za komuniciranje uporablja princip nadrejen/podrejen (angl. master/slave), po katerem nadrejena naprava (Arduino Uno) vzpostavlja stik in vodi komunikacijo s podrejeno napravo (RFID RC522). Pri tem uporablja štiri signalne linije:

Vodilo SPI lahko uporablja ena nadrejena in več podrejenih naprav, nadrejena naprava pa lahko hkrati komunicira z natanko eno podrejeno.

Izdelali bomo program, ko bo deloval po zahtevah:

Program:

 /*

  Uporaba RFID kartic oz. tag-ov z bralnikom RFID RC522 za dostop na parkirišče.

  Avtor: Milan Ivič

  Avgust 2015

*/

 #include <Servo.h>          //Knjižnica za servo motor.

 #include <SPI.h>             //Omogoča komuniciranje RFID RC522 z Arduinom (Serial Peripheral Interface => serijsko podatkovno vodilo).

 #include <RFID.h>           //Knjižnica za RFID RC522.

 RFID rfid(10, 9);               //SDA => pin 10, RST => pin 9.

 Servo servo;

 int Zapornica_spodaj = 0;     //Inicializacija za servo motorček, zapornica je spodaj (0°).

 int Zapornica_zgoraj = 85;    //Inicializacija za servo motorček, zapornica je zgoraj (85°).

 int LED_Zelena = 6;

 int LED_Rdeca = 8;

 unsigned int Preklop = 0;                 //Uporabimo za preklapljanje rdeče LED diode.

 unsigned int Stevec_prekinitev = 0;   //Štejemo število prekinitev, ki jih proži Timer2.

 //Zaradi uporabljene knjižnice RFID.h moramo uporabiti ime serNum tipa int (glej knjižnico).

 int serNum[5];                                 //Kreiranje enodimenzionalnega polja s petimi elementi.

 //Kode pad-ov, ki jim dovolimo vstop na parkirišče:

 int cards[][5] =

 {

   {65,16,215,14,136},          //Koda prvega pad-a.

   {65,16,213,229,137}         //Koda drugega pad-a.

 };

 /*Booleanova spremenljivka lahko zavzave dve vrednosti: true ali false.

   Vsaka boolean spremenljivka zasede 1 byte pomnilnika (1 byte = 8 bitov).

   Resnično (true) je vse, kar nima vrednosti 0 in neresnično (false) je vse, kar ima vrednost 0.

 */

 boolean Dostop = false;    //Spremenljivki z imenom Dostop tipa boolean je dodeljena vrednost false, inicializacija.

 

 void setup()

 {

   Serial.begin(9600);

   SPI.begin();

   rfid.init();

   servo.attach(7);

   servo.write(Zapornica_spodaj);  

   pinMode(LED_Zelena, OUTPUT);

   pinMode(LED_Rdeca, OUTPUT);    

   digitalWrite(LED_Zelena, LOW);

    

   noInterrupts();                     //V času nastavitve in izvajanja prekinitvene rutine ne sme priti do prekinitve (interrupt).

  //Nastavitve Timer2 za prekinitev vsako 1 ms:

  TCCR2B = 0x00;                 //Inicializacija Timer2 registra TCCR2B.

  TCNT2  = 130;                     //Preset Timer2, ko prekorači svojo vrednost na vrednost 130.

  TIFR2  = 0x00;                    //Timer2 INT Flag Reg: Pobrišemo zastavico prekinitve (Overflow Flag).

  TIMSK2 = 0x01;                  //Timer2 INT Reg: Omogočamo prekinitve na Timer2.

  TCCR2A = 0x00;                 //Inicializacija Timer2 registra TCCR2A.

  TCCR2B = 0x05;                 //Timer2 Control Reg B: Preddelitev 1:128  

 }

 

 void loop()

 {    

    if(rfid.isCard())

    {    

       if(rfid.readCardSerial())              //Če je zaznan pad, preberi in izpiši posamezne elemente polja na serijski monitor.

       {

          Serial.print("Koda: ");

          Serial.print(rfid.serNum[0]);    //Prvi element polja serNum je element 0.

          Serial.print(", ");

          Serial.print(rfid.serNum[1]);

          Serial.print(", ");

          Serial.print(rfid.serNum[2]);

          Serial.print(", ");

          Serial.print(rfid.serNum[3]);

          Serial.print(", ");

          Serial.print(rfid.serNum[4]);

          Serial.println("");

            

          for(int x = 0; x < sizeof(cards); x++)                //V x je koda tag-a, ki smo mu dovolili vstop.

          {

              for(int i = 0; i < sizeof(rfid.serNum); i++ )     //V i shrani prebrano kodo pribljižanega tag-a.

              {

                  if(rfid.serNum[i] != cards[x][i])                 //Ali sta kodi različni?

                  {

                      Dostop = false;                                 //Kodi sta različni, zavrni vstop v prostor.

                      break;

                  }

                  else

                  {

                      Dostop = true;                                  //Kodi sta enaki, dovoli vstop v prostor.                      

                  }

              }

              if(Dostop) break;                                //Uporabimo break za izhod iz for zanke. Lahko bi napisali tudi if(Dostop != 0) break;

          }                                                           //Če je Dostop true, je sigurno vrednost spremenljivke Dostop različna od 0.

           

        }

        

       if(Dostop != 0)                                                 //Ali je vstop v prostor dovoljen (true => ni enak 0)?

       {

          interrupts();                                                  //Omogočimo prekinitve.

          Serial.println("Dovoljen vstop na parkirisce!");

          Serial.println("");

          digitalWrite(6, HIGH);  

          for(int k = 0; k < 85; k++)                              //Zanka for se izvede 85 krat od 0 do 85 po koraku 1.

          {            

            servo.write(k);                                            //Ob vsakem obhodu zanke for se zavrti servo motor za 1°.

            delay(15);                                                  //Za počasnejše dvigivanje zapornice.                     

          }

          delay(3000);

          for(int l = 85; l>0; l--)                                    //Zanka for se izvaja po korakih 1 od 85 do 0

          {

          servo.write(l);

          delay(15);             

          }

          digitalWrite(6, LOW);

          digitalWrite(8, LOW);

          

       }

       else

       {

          Serial.println("Vstop na parkirisce je zavrnjen!");

          Serial.println("");

       }

      noInterrupts();       

    }

 }

 //Timer2 proži owerflow prekinitve OVF (Overflow Interrupt Vector) vsako 1 ms.

 ISR(TIMER2_OVF_vect)              //Prekinitvena rutina.

 {  

   Stevec_prekinitev ++;               //Povečevanje števca prekinitev. Za vsako prekinitev se Stevec_prekinitev poveča za 1.

   if(Stevec_prekinitev > 499)        //Vsako 500-to prekinitev (vsakih 500 ms) izvedi blok if stavka (preklopi LED diodi).

   {

     Preklop =! Preklop;                //Da lahko preklapljamo LED diodo.

     Stevec_prekinitev = 0;            //Inicializacija števca prekinitev.

   }

   digitalWrite(8, Preklop);           //Preklopi LED diodo.

  TCNT2 = 130;                         //Preset Timer2, ko doseže overflow na vrednost 130.

  TIFR2 = 0x00;                         //Timer2 INT Flag Reg: Pobrišemo zastavico prekinitve (Overflow Flag).

}

Razlaga programa:

V Program moramo vključiti tri knjižnice: Servo.h za servo motor, SPI.h za že opisano komunikacijo med Arduino Uno in RFID RC522 ter knjižnico RFID.h. Knjižnico RFID.h, ki jo najdemo v prilogi tega poglavja spodaj, prenesemo na računalnik, jo razširimo ter mapo knjižnice vstavimo v Arduinovo mapo libraries. Po ponovnem zagonu Arduinovega okolja je knjižnica na voljo za uporabo (Skica > Uvozi knjižnico... > RFID).

Izmed spremenljivk, ki smo jih deklarirali, sta tudi spremenljivki z imenom Preklop in Stevec_prekinitev. Obe sta tipa unsigned int in inicializirani na vrednost 0. Tip spremenljivke unsigned int (integers => cela števila) določa shranjevanje nepredznačnih celih števil velikosti dva bajta (16 bitno število => od 0 do 2^16 - 1). Največje število, ki ga lahko shranimo v spremenljivko dolžine 2 bajta je torej 65535 (1111 1111 1111 1111(2) = 65535(10)).

Spremenljivko z imenom Preklop uporabimo za preklapljanje rdeče LED diode v prekinitveni rutini ISR (TIMER2_OVF_vect), saj ji vsakokrat, ko se izvede 500-ta prekinitev zamenjamo vrednost (Preklop =! Preklop). Če je bila vrednost spremenljivke Preklop prej 1 je po izvršitvi ukaza njena vrednost 0 in obratno.

S spremenljivko Stevec_prekinitev štejemo prekinitve, ki jih proži časovnik Timer2 (glej poglavje Prekinitve (Interrupt), Krmiljenje zapornice na železniškem prehodu), saj se ob vsaki prekinitvi njena vrednost poveča za 1. Timer2 smo nastavili tako, da sproži prekinitev vsako milisekundo. Po vsaki 500-ti prekinitvi (0,5 sekunde) se izvede blok if stavka if(Stevec_prekinitev > 499) v katerem preklopimo rdečo LED diodo. Rdeča LED dioda torej utripa s frekvenco 1 Hz.

Deklarirali smo tudi spremenljivko Dostop tipa boolean in ji dodelili vrednost false. Spremenljivka tipa boolean lahko zavzame samo dve vrednosti: true ali false. Resnično (true) je vse, kar nima vrednosti nič (0) in neresnično (false) je vse, kar ima vrednost nič (0). Spremenljivki Dostop v programu dodelimo vrednost true, če je koda kartice oziroma pad-a enaka kodi, ki smo ji dovolili dostop na parkirišče in vrednost false, če koda kartice oziroma pad-a ni enaka dovoljeni kodi.

Če je vrednost spremenljivke Dostop enaka true:

Če je vrednost spremenljivke Dostop enaka false:

Za kode pad-ov, ki jim dovolimo vstop na parkirišče smo deklarirali polje (array) spremenljivk z imenom cards. Polje je zbirka spremenljivk, ki so dostopne z indeksi. Prvi element polja je element z indeksom 0, drugi element polja je element z indeksom 1 itd. Ker bomo prebirali petmestne kode več pad-ov, smo deklarirali dvodimenzionalno polje dolžine petih elementov (int cards[][5]). Med zavite oklepaje, ločeno z vejico, vpišemo kode tistih pad-ov, ki jim dovolimo vstop na parkirišče. Kode pad-ov dobimo tako, da pad približamo RFID RC522 bralniku in koda pad-a se izpiše na serijskem monitorju.

Slika 3: Izpis na serijskem monitorju.

S stavkom for(int x = 0; x < sizeof(cards); x++) preberemo vse elemente polja cards. Koliko je teh elementov je odvisno od tega, koliko pad-om smo dovolili vstop na parkirišče. Zato uporabimo operator sizeof, ki v spremenljivko x vrača število bajtov, ki jih vsebuje polje cards. Če imamo dovoljenje samo za en pad, je teh elementov 10 (od 0 do 9). Če smo dali dovoljenje za dva pad-a, kot je to v našem primeru, je teh elementov 20 (od 0 do 19).

Z drugim, ugnezdenim stavkom for, for(int i = 0; i < sizeof(rfid.serNum); i++ ) v spremenljivko i vrnemo prebrane bajte pad-a, ki smo ga približali RFID bralniku. Teh je pet. Z if stavkom v drugi zanki for: if(rfid.serNum[i] != cards[x][i]) nato preverjamo, ali je pad-u, ki ga približamo RFID bralniku dovoljen vstop na parkirišče ali ne. Če je pad-u dovoljen vstop na parkirišče, je vrednost spremenljivke Dostop true, sicer pa false.

Knjnižnico za RFID RC522 s primeri (examples) pa najdemo na tej spletni strani. kjer jo lahko prenesemo, razširimo in dodamo v mapo Arduino libraries.