NFC Lock

NFC Lock

Zaprezentowany projekt to jeden z tych, które od dawna znajdowały się na mojej liście rzeczy do zrobienia. Przyznam szczerze, że podchodziłem do tego zagadnienia trochę jak przysłowiowy „pies do jeża”, głównie dlatego, że tematyka jest dość skomplikowana a i zakres dostępnego hardware’u bywał dość ograniczony. Wszystko do czasu… gdy natknąłem się na wygodny moduł czytnika RFID/NFC firmy NXP pod postacią peryferium oznaczonego symbolem PN532, który integruje w sobie specjalizowany układ o tej samej nazwie (mikrokontroler z rdzeniem 80C51 wyposażony w 40 kB pamięci ROM i 1 kB pamięci RAM) oraz cały interfejs RF.

Podstawowe parametry:
  • liczba obsługiwanych kart Mifare/znaczników NFC: 64,
  • zbliżenie karty, która wcześniej została zarejestrowana w pamięci urządzenia powoduje załączenie przekaźnika na czas 1,5 s,
  • parametry wyjścia (parametry styków przekaźnika): AC: 250 V, 10 A; DC: 125 V, 8 A (szczegóły w dokumentacji przekaźnika),
  • napięcie zasilania: 8…10 V, maksymalny prąd 115 mA.

Moduł PN532 to peryferium pozwalające na wymianę danych (jednokierunkową lub dwukierunkową) z kartami i urządzeniami w standardzie RFID i NFC. Obsługiwane rodzaje kart to: Mifare1k, 4k, Ultralight, DesFire, ISO/IED 14443-4, Innovision Jewel (IRT5001) oraz FeliCa (RCS_860, RCS_854). Oprócz obsługi kart lub tagów RFID, moduł pozwala również na dwukierunkową komunikację z urządzeniami NFC – np. z telefonem komórkowym, a także wymianę danych między dwoma modułami w trybie P2P.

Wspomniany moduł wyposażono dodatkowo w wygodne interfejsy komunikacyjne, dzięki którym możliwa jest jego konfiguracja i wymiana danych z procesorem hosta. Interfejsy te to: I2C, SPI lub HSU (High Speed Uart). Wybór aktywnego interfejsu komunikacyjnego dokonywany jest za pomocą dedykowanych switchy SMD zintegrowanych na obwodzie drukowanym PCB. Aby przygotować moduł do pracy z interfejsem SPI, przełączamy przełącznik oznaczony jako ON w pozycję On (blisko znacznika „1”), zaś przełącznik oznaczony, jako KE w pozycję Off (blisko znacznika „KE”). Na tym samym obwodzie drukowanym zintegrowano również antenę poprowadzoną na brzegach płytki, dzięki czemu dostajemy urządzenie gotowe do pracy. Tyle w kwestii samego modułu.

RFID/NFC

Kilka słów uwagi należy w tym miejscu poświęcić samej metodologii przesyłania danych RFID/NFC. Obie technologie to dwie bardzo zbliżone do siebie technologie bezprzewodowej komunikacji, które używane są praktycznie na każdym kroku w różnych dziedzinach naszego życia – od weryfikacji tożsamości poprzez kontrolę dostępu aż do systemów bezprzewodowych płatności zbliżeniowych. Jakie są główne różnice pomiędzy RFID a NFC? RFID pozwala na jednokierunkową (w sensie inicjowania transferu), bezprzewodową komunikację pomiędzy niezasilonym znacznikiem a zasilonym czytnikiem RFID, przy czym odległość między tym czytnikiem a znacznikiem może dochodzić nawet do 200 m (w wybranych implementacjach) i zależy w dużej mierze od zastosowanych zakresów częstotliwości radiowych oraz protokołów komunikacji.

W odróżnieniu od RFID, technologia NFC pozwala natomiast na dwustronną komunikację pomiędzy dwoma urządzeniami NFC, realizując obsługę bardziej złożonych mechanizmów, a także wymianę danych. Wynika to głównie z idei powstania tej technologii, która w zamierzeniu miała umożliwiać komunikację na małe, bezpieczne odległości (poniżej 20 cm), przez co idealnie nadaje się do zastosowania w telefonach komórkowych do przeprowadzania bezpiecznych transakcji bezgotówkowych lub wymiany danych pomiędzy tymi urządzeniami. W zaprezentowanym urządzeniu skupię się wyłącznie na obsłudze kart Mifare 1k oraz urządzeń NFC zgodnych z tym standardem, gdyż naszym zadaniem będzie wyłącznie odczytanie unikalnego numeru seryjnego karty/urządzenia RFID/NFC (tzw. UID).

Należy mieć jednak świadomość, że karty tego rodzaju udostępniają dużo większą funkcjonalność. Dla przykładu wyposażono je w 1024 bajty pamięci EEPROM (16 sektorów po 4 bloki o rozmiarze 16 B), która może być zabezpieczona przed zapisem/odczytem za pomocą zdefiniowanego klucza dostępowego (i to indywidualnie dla każdego z tych sektorów). Tyle w kwestiach technologii bezprzewodowej transmisji danych RFID/NFC.

Program sterujący

Moduł PN532 może współpracować z mikrokontrolerem sterującym na kilka sposobów. Ja zdecydowałem się na zastosowanie interfejsu SPI jako najszybszego, najprostszego w obsłudze i łatwego w programowej realizacji. Interfejs ten, co oczywiste, angażuje w celu transmisji aż 4 wyprowadzenia sterujące: MISO, MOSI, SCK oraz CS (inaczej SS). Co więcej, moduł PN532 używa dodatkowych wyprowadzeń sterujących, z których po części korzysta nasz program aplikacji: RST (resetowanie modułu PN532) oraz IRQ (wyprowadzenie przerwań modułu). Widać z tego, że aby w pełni obsłużyć moduł PN532, należy użyć aż 6 wyprowadzeń mikrokontrolera hosta. To dużo, ale z drugiej strony w ten sposób obsługujemy moduł RFID w najszybszy możliwy sposób i tego właśnie rozwiązania będziemy się trzymać.

Przejdźmy zatem do zagadnień programowych. Celowo pomijam tutaj szczegóły dotyczące metodologii wymiany danych i sterowania modułem PN532, gdyż dokumentacja w tym zakresie jest bardzo obszerna, więc jej przytaczanie w treści naszego krótkiego artykułu wydaje się bezcelowe. Już zupełnie inną sprawą jest, iż w mojej ocenie dokumentacja ta pozostawia wiele do życzenia, jeśli chodzi o szczegółowość omawianych zagadnień. Na szczęście w sieci można znaleźć wiele przykładów bibliotek obsługi modułu PN532, których lektura w połączeniu z dostępnym datasheetem rozjaśnia nieco to zagadnienie. Co ciekawe, większość z tych rozwiązań bazuje na dość dobrze udokumentowanej bibliotece firmy Adafruit, która i dla mnie stała się podstawą do opracowania własnych implementacji.

A więc do dzieła!

Pora rozpocząć od zagadnień związanych z obsługą interfejsu SPI, która w ramach naszej aplikacji realizowana jest programowo. Dlaczego? Z prostej przyczyny, zastosowany mikrokontroler ATtiny44 dysponuje co prawda uniwersalnym interfejsem szeregowej transmisji danych nazywanym tutaj USI, który może pracować zarówno w trybie I2C, jak i SPI, jednak jego użyteczność jest mocno ograniczona. Po pierwsze, w trybie SPI nie pozwala na wysyłanie poszczególnych bitów danych, począwszy od bitu LSB (czego wymaga nasz moduł RFID), a po drugie, wymaga programowej obsługi sygnału zegarowego SCK, co niweczy właściwie sens jego stosowania. Postanowiłem w takim razie zrealizować całą obsługę interfejsu SPI programowo, co jest zadaniem nader łatwym.

Zacznijmy od pliku nagłówkowego, którego ciało pokazano na listingu 1. Następnie, na listingu 2 pokazano funkcję umożliwiającą konfigurację sygnałów sterujących magistrali SPI. Dalej, na listingu 3 pokazano funkcję realizującą transmisję na magistrali SPI. Prawda, że proste? Przejdźmy zatem do szczegółów obsługi modułu PN532 z zastosowaniem magistrali SPI.

Listing 1. Plik nagłówkowy modułu obsługi interfejsu SPI

#define SPI_PORT PORTA
#define SPI_DDR DDRA
#define SPI_PIN PINA
#define MOSI_PIN PA2
#define MISO_PIN PA1
#define SCK_PIN PA0
#define CS_PIN PA3

#define RESET_MOSI SPI_PORT &= ~(1<<MOSI_PIN)
#define SET_MOSI SPI_PORT |= (1<<MOSI_PIN)
#define RESET_SCK SPI_PORT &= ~(1<<SCK_PIN)
#define SET_SCK SPI_PORT |= (1<<SCK_PIN)
#define RESET_CS SPI_PORT &= ~(1<<CS_PIN)
#define SET_CS SPI_PORT |= (1<<CS_PIN)
#define READ_MISO (SPI_PIN & (1<<MISO_PIN))
Listing 2. Funkcja umożliwiająca konfigurację sygnałów sterujących magistrali SPI

void SPIinit(void){
//Setup SPI ports: MISO to INPUT (default, pulled up to VCC),
//others to OUTPUT (default 0)
SPI_DDR |= (1<<MOSI_PIN)|(1<<SCK_PIN)|(1<<CS_PIN);
SPI_PORT |= (1<<MISO_PIN);
}
Listing 3. Funkcja realizująca transmisję na magistrali SPI

uint8_t SPItransmit(uint8_t Byte){
for(uint8_t i = 0; i < 8; i++){
//Send one bit of Byte (LSB first)
if(Byte & 0x01) SET_MOSI; else RESET_MOSI;
SET_SCK;
Byte >>= 1;
//Receive one bit of Byte (LSB first)
if(READ_MISO) Byte |= (1<<7);
RESET_SCK;
}
return Byte;
}

Na początku warto naświetlić mechanizm wymiany danych pomiędzy modułem RFID a mikrokontrolerem, który skonstruowano w taki sposób, aby zminimalizować prawdopodobieństwo ewentualnych błędów. Każda ramka danych wysyłana do/odbierana z modułu PN532 zawiera dodatkowe bajty sterujące, a jej konstrukcję pokazano na rysunku 1.

Rysunek 1. Konstrukcja ramki danych modułu PN532

Kolejno wysyłane są następujące bajty danych:

  • PREAMBLE: to bajt preambuły o wartości 0×00,
  • START CODE: to 2 bajty startowe o wartościach odpowiednio 0×00 i 0xxFF,
  • LEN: to bajt informujący o liczbie przesyłanych danych użytecznych (suma bajtów TFI i PD0…PDn),
  • LCS: to suma kontrolna bajta LEN liczona według zależności: LCS = ~LEN+1,
  • TFI: to identyfikator typu ramki danych: 0xD4 oznacza ramkę wysyłaną z hosta do modułu PN532, zaś 0xD5 oznacza ramkę wysyłaną z modułu PN532 do hosta,
  • PD0…PDn: to dane użyteczne,
  • DCS: to suma kontrolna całej ramki danych,
  • POSTAMBLE: to bajt postambuły o wartości 0×00.

Z kolei każda transakcja w ramach wymiany danych z modułem RFID składa się z następujących kroków:

  • wysłanie ramki danych do modułu PN532,
  • odczyt bajta statusu modułu PN532 (wartość 0×01 oznacza gotowość modułu do przesłania danych),
  • wysłanie ramki potwierdzenia (ACK) przez moduł PN532,
  • odczyt bajta statusu modułu PN532 (wartość 0×01 oznacza gotowość modułu do przesłania danych),
  • odczyt ramki odpowiedzi modułu PN532,
  • wysłanie opcjonalnej ramki potwierdzenia (ACK) przez układ hosta.

W tym miejscu przejdźmy od teorii do praktyki. Pora zacząć od pliku nagłówkowego modułu obsługi układu PN532, którego ciało pokazano na listingu 4.

Listing 4. Plik nagłówkowy modułu obsługi układu PN532

//PN532 ports
#define PN532_PORT_REG PORTA
#define PN532_DDR_REG DDRA
#define PN532_PIN_REG PINA
#define PN532_IRQ PA4
#define PN532_RES PA5

#define SET_RES PN532_PORT_REG |= (1<<PN532_RES)
#define RESET_RES PN532_PORT_REG &= ~(1<<PN532_RES)
#define RES_AS_OUTPUT PN532_DDR_REG |= (1<<PN532_RES)
#define PULL_UP_IRQ_TO_VCC PN532_PORT_REG |= (1<<PN532_IRQ)
#define READ_IRQ_PIN (PN532_PIN_REG & (1<<PN532_IRQ))

//PN532 frame parts
#define PN532_PREAMBLE 0x00
#define PN532_STARTCODE1 0x00
#define PN532_STARTCODE2 0xFF
#define PN532_POSTAMBLE 0x00
#define PN532_HOSTTOPN532 0xD4

//PN532 Commands
#define PN532_FIRMWAREVERSION 0x02
#define PN532_GETGENERALSTATUS 0x04
#define PN532_SAMCONFIGURATION 0x14
#define PN532_INLISTPASSIVETARGET 0x4A
#define PN532_INDATAEXCHANGE 0x40
#define PN532_MIFARE_READ 0x30
#define PN532_MIFARE_WRITE 0xA0
#define PN532_AUTH_WITH_KEYA 0x60
#define PN532_AUTH_WITH_KEYB 0x61
#define PN532_WAKEUP 0x55
#define PN532_RFCONFIGURATION 0x32

//SPI commands
#define PN532_SPI_DATAWRITE 0x01
#define PN532_SPI_STATUSREAD 0x02
#define PN532_SPI_DATAREAD 0x03
#define PN532_SPI_READY 0x01
#define PN532_MIFARE_ISO14443A 0x00

//Errors
#define NO_ERROR 0x00
#define ACKNOWLEDGE_FRAME_ERROR 0x01
#define STATUS_TIMEOUT 0x02
#define FIRMWARE_HEADER_ERROR 0x03
#define RFCONFIGURATION_ERROR 0x04
#define SAMCONFIGURATION_ERROR 0x05
#define NO_TAGS_FOUND 0x06

//Miscellaneous
#define DUMMY_BYTE 0x00

Dalej, w ramach modułu obsługi układu PN532, wprowadzono 3 zmienne tablicowe, których definicje pokazano na listingu 5. Pierwsza ze zmiennych to w zasadzie stała zdefiniowana w ten sposób, będąca de facto wzorcem ramki ACK, której konstrukcję pokazano na rysunku 2.

Rysunek 2. Konstrukcja ramki potwierdzenia (ACK) modułu PN532

Druga ze zmiennych to podobny wzorzec, ale tym razem ramki danych zawierającej nagłówek wersji oprogramowania modułu PN532 (sam nagłówek, nie zaś wersję oprogramowania). Z kolei trzecia zmienna to bufor danych używany podczas wysyłania/odbierania danych w trakcie współpracy hosta z modułem PN532. Przejdźmy zatem do konkretów.

Listing 5. Zmienne modułu obsługi układu PN532

uint8_t pn532AckPattern[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};
uint8_t pn532FirmwarePattern[] = {0x00, 0xFF, 0x06, 0xFA, 0xD5, 0x03};
uint8_t pn532PacketBuffer[64];
Listing 6. Funkcja umożliwiająca odczyt danych z modułu PN532

void pn532ReadData(uint8_t *Buffer, uint8_t Bytes){
RESET_CS;
_delay_ms(2);
SPItransmit(PN532_SPI_DATAREAD);

while(Bytes--){
*Buffer++ = SPItransmit(DUMMY_BYTE);
_delay_ms(1);
}
SET_CS;
_delay_ms(1);
}

Na pierwszy ogień funkcja umożliwiająca odczyt danych z modułu PN532, której ciało pokazano na listingu 6. Jak widać, sam odczyt danych poprzedzony jest wysłaniem bajta PN532_SPI_DATAREAD, który informuje moduł o żądaniu odczytania danych. Dalej, na listingu 7, funkcja umożliwiająca odczyt bajta statusu modułu PN532, o czym była mowa powyżej.

Listing 7. Funkcja umożliwiająca odczyt bajta statusu modułu PN532

uint8_t pn532ReadStatus(void){
uint8_t Status;
RESET_CS;
_delay_ms(2);
SPItransmit(PN532_SPI_STATUSREAD);
Status = SPItransmit(DUMMY_BYTE);
SET_CS;
return Status;
}

Kolejna funkcja to funkcja umożliwiająca odczyt ramki potwierdzenia (ACK) modułu PN532, której ciało pokazano na listingu 8.

Listing 8. Funkcja umożliwiająca odczyt ramki potwierdzenia (ACK) modułu PN532

uint8_t pn532ReadAck(){
uint8_t ackBuffer[6];

pn532ReadData(ackBuffer, 6);

//Check if the answer matches the acknowledge pattern
if(memcmp((char *)ackBuffer, (char *)pn532AckPattern, 6) == 0)
return NO_ERROR;
else
return ACKNOWLEDGE_FRAME_ERROR;
}

Kolejna funkcja jest kluczowa. To funkcja pozwalająca na wysłanie ramki danych do modułu PN532 (według specyfikacji z rysunku 1), której ciało pokazano na listingu 9. Rozwinięciem funkcji z listingu 9 jest funkcja odpowiedzialna za przeprowadzenie transakcji wymiany danych z modułem PN532 (z kontrolą poszczególnych jej etapów), której ciało pokazano na listingu 10.

Listing 9. Funkcja pozwalająca na wysłanie ramki danych do modułu PN532

void pn532WriteCommand(uint8_t *Command, uint8_t commandLength){
uint8_t checkSum;

commandLength++;

RESET_CS;
_delay_ms(2);

SPItransmit(PN532_SPI_DATAWRITE);

checkSum = PN532_PREAMBLE + PN532_PREAMBLE + PN532_STARTCODE2;
SPItransmit(PN532_PREAMBLE);
SPItransmit(PN532_STARTCODE1);
SPItransmit(PN532_STARTCODE2);
SPItransmit(commandLength);
SPItransmit(~commandLength+1);

SPItransmit(PN532_HOSTTOPN532);
checkSum += PN532_HOSTTOPN532;

for(uint8_t i=0; i<commandLength-1; i++){
SPItransmit(Command[i]);
checkSum += Command[i];
}

SPItransmit(~checkSum);
SPItransmit(PN532_POSTAMBLE);
SET_CS;
_delay_ms(1);
}
Listing 10. Funkcja odpowiedzialna za przeprowadzenie transakcji wymiany danych z modułem PN532

uint8_t pn532SendCommandCheckAck(uint8_t *Command, uint8_t commandLength){
uint8_t Timer = 0;

//Write the command
pn532WriteCommand(Command, commandLength);

//Wait for the chip to say it’s ready or timeout
while(pn532ReadStatus() != PN532_SPI_READY){
if(Timer++ > 100) return STATUS_TIMEOUT;
_delay_ms(10);
}

//Read acknowledgement
if(pn532ReadAck() != NO_ERROR)
return ACKNOWLEDGE_FRAME_ERROR;

Timer = 0;
//Wait for the chip to say it’s ready or timeout
while(pn532ReadStatus() != PN532_SPI_READY){
if(Timer++ > 100) return STATUS_TIMEOUT;
_delay_ms(10);
}

return NO_ERROR;
}

Dalej zaprezentuję różne funkcje konfiguracyjne za pomocą których możemy ustawić lub odczytać wybrane grupy parametrów modułu PN532. Na pierwszy ogień pójdzie funkcja pozwalająca na odczyt wersji firmware’u modułu PN532, której ciało pokazano na listingu 11.

Listing 11. Funkcja pozwalająca na odczyt wersji firmware modułu PN532

uint8_t pn532GetFirmwareVersion(uint32_t *firmwareVersion){
uint8_t Result;

pn532PacketBuffer[0] = PN532_FIRMWAREVERSION;
if((Result = pn532SendCommandCheckAck(pn532PacketBuffer, 1)) != NO_ERROR)
return Result;

//Read data packet
pn532ReadData(pn532PacketBuffer, 12);

//Check firmware version frame header
if(memcmp((char *)pn532PacketBuffer, (char *)pn532FirmwarePattern, 6) != 0)
return FIRMWARE_HEADER_ERROR;

*firmwareVersion = 0;
//Version of the IC. For PN532, the contain of this byte is 0x32
*firmwareVersion = pn532PacketBuffer[6];
*firmwareVersion <<= 8;
//Version of the firmware
*firmwareVersion |= pn532PacketBuffer[7];
*firmwareVersion <<= 8;
//Revision of the firmware
*firmwareVersion |= pn532PacketBuffer[8];
*firmwareVersion <<= 8;
//Indicates which are the functionalities supported by the firmware
*firmwareVersion |= pn532PacketBuffer[9];e

return NO_ERROR;
}
Listing 12. Funkcja pozwalająca na ustawienie liczby prób nawiązania komunikacji, jakie podejmuje moduł PN532 w procesie odczytu karty RFID/NFC

uint8_t pn532SetPassiveActivationRetries(uint8_t Retries){
uint8_t Result;

pn532PacketBuffer[0] = PN532_RFCONFIGURATION;
pn532PacketBuffer[1] = 5; // Config item 5 (MaxRetries)
pn532PacketBuffer[2] = 0xFF; // MxRtyATR (default = 0xFF)
pn532PacketBuffer[3] = 0x01; // MxRtyPSL (default = 0x01)
//Number of times that the PN532 will retry to activate a target
pn532PacketBuffer[4] = Retries;

if((Result = pn532SendCommandCheckAck(pn532PacketBuffer, 5)) != NO_ERROR)
return Result;

//Read data packet
pn532ReadData(pn532PacketBuffer, 9);
if(pn532PacketBuffer[5] == PN532_RFCONFIGURATION + 1)
return NO_ERROR;
else
return RFCONFIGURATION_ERROR;
}

Dalej funkcja, której zadaniem jest ustawienie liczby prób nawiązania komunikacji, jakie podejmuje moduł PN532 w procesie odczytu karty RFID/NFC. Ciało tej funkcji pokazano na listingu 12. Dalej, na listingu 13, funkcja konfigurująca podsystem SAM (Security Access Module) układu PN532. W zasadzie przeprowadzamy taki rodzaj konfiguracji, w ramach której układ PN532 nie korzysta z rozszerzenia modułu SAM i jest to jego ustawienie domyślne, ale dla porządku wywołanie tej funkcji znajdzie się w treści funkcji inicjalizacyjnej.

Listing 13. Funkcja konfigurująca moduł SAM (Security Access Module) układu PN532

uint8_t pn532SAMConfig(void){
uint8_t Result;

//Configure Security Access Module
pn532PacketBuffer[0] = PN532_SAMCONFIGURATION;
pn532PacketBuffer[1] = 0x01; //Normal mode;
pn532PacketBuffer[2] = 0x14; //Timeout 50ms * 20 = 1 second
pn532PacketBuffer[3] = 0x01; //Use IRQ pin

if((Result = pn532SendCommandCheckAck(pn532PacketBuffer, 4)) != NO_ERROR)
return Result;

//Read data packet
pn532ReadData(pn532PacketBuffer, 8);

if(pn532PacketBuffer[5] == PN532_SAMCONFIGURATION+1)
return NO_ERROR;
else
return SAMCONFIGURATION_ERROR;
}
Listing 14. Funkcja pozwalająca na odczyt numeru seryjnego UID peryferium RFID/NFC

uint8_t pn532ReadPassiveTargetID(uint32_t *UID){
uint8_t Result, startByte;

pn532PacketBuffer[0] = PN532_INLISTPASSIVETARGET;
pn532PacketBuffer[1] = 1; //Max 1 card at once
//106 kbps type A (ISO/IEC14443 Type A)
pn532PacketBuffer[2] = PN532_MIFARE_ISO14443A;

if((Result = pn532SendCommandCheckAck(pn532PacketBuffer, 3)) != NO_ERROR)
return Result;

//Read data packet
pn532ReadData(pn532PacketBuffer, 20);
/* ISO14443A card response should be in the following format:
byte Description
--------------------- ------------------------------------------
b0...6 Frame header and preamble
b7 Number of found tags
b8 Tag Number
b9...10 SENS_RES
b11 SEL_RES
b12 UID Length
b13…UID Length UID
*/

if(pn532PacketBuffer[7] != 1)
return NO_TAGS_FOUND;

startByte = pn532PacketBuffer[12] == 4? 13:16;
*UID = 0;
for (uint8_t i = 0; i < 4; i++){
*UID <<= 8;
*UID |= pn532PacketBuffer[startByte+i];
}

return NO_ERROR;
}

Zmierzamy powoli do końca, w związku z czym pora na funkcję pozwalającą na odczyt numeru seryjnego UID peryferium RFID/NFC, której ciało pokazano na listingu 14. Jak widać, funkcja powyższa powoduje odczytanie numeru seryjnego i upchanie go w 32-bitowym argumencie wywołania *UID. Niby wszystko w porządku, ale numer UID może mieć długość 7 bajtów (oprócz standardowych 4 bajtów), zaś jego rzeczywisty rozmiar przechowywany jest w buforze odbiorczym pod adresem pn532 PacketBuffer[12]. Co wtedy? Wtedy zapamiętujemy ostatnie 4 bajty tegoż numeru w zmiennej *UID. To pewne uproszczenie, a wynika wyłącznie z niewielkiej pamięci EEPROM mikrokontrolera i chęci zapisania jak największej liczby aktywnych kart/tagów RFID/NFC. Nie powinno to budzić większych wątpliwości, gdyż nawet dla 4 bajtów mamy dostępnych 232 kombinacji. Uważny Czytelnik bez problemu poradzi sobie z modyfikacją przedstawionej funkcji w przypadku, gdy będzie chciał zapamiętywać kompletny numer seryjny w wersji 7-bajtowej.

Pora na funkcję inicjalizacyjną, której zadaniem jest przeprowadzenie pełnej konfiguracji modułu PN532. Ciało tej funkcji pokazano na listingu 15.

Listing 15. Funkcja inicjalizacyjna, której zadaniem jest przeprowadzenie pełnej konfiguracji modułu PN532

uint8_t pn532Init(void){
uint8_t Result;
uint32_t Firmware;

//Pulling up IRQ to VCC
PULL_UP_IRQ_TO_VCC;
//RES as output with 1
SET_RES;
RES_AS_OUTPUT;
_delay_ms(10);
RESET_RES;
_delay_ms(400);
SET_RES;
_delay_ms(10);

//SPI initialization
SPIinit();
//Check firmware version, needed for data sychronization
if((Result = pn532GetFirmwareVersion(&Firmware)) != NO_ERROR) return Result;
//Sets the amount of retries that the PN532 tries to activate a target
if((Result = pn532SetPassiveActivationRetries(3)) != NO_ERROR) return Result;
//Configure board to read RFID tags
if((Result = pn532SAMConfig()) != NO_ERROR) return Result;

return NO_ERROR;
}

Przebrnęliśmy przez część aplikacyjną. Warto zauważyć, że większość funkcji zwraca wartość informującą o potencjalnych błędach. Pora na omówienie sprzętowej części naszego urządzenia NFC Lock.

Budowa i działanie

Schemat urządzenia pokazano na rysunku 3. Jak widać, zaprojektowano bardzo prosty system mikroprocesorowy, którego sercem jest niewielki mikrokontroler firmy Microchip (dawniej Atmel) o oznaczeniu ATtiny44 taktowany wewnętrznym, wysokostabilnym generatorem RC o częstotliwości 1 MHz. Mikrokontroler ten odpowiedzialny jest za programową obsługę interfejsu SPI (w ramach współpracy z modułem PN532), obsługę interfejsu użytkownika w postaci dwóch diod LED (GREEN i RED) oraz dwóch przycisków funkcyjnych (ADD i REMOVE) oraz sterowanie pracą wbudowanego przekaźnika K1. Zadań nie jest zbyt wiele, w związku z czym wbudowana pamięć Flash o wielkości 4 kB spokojnie wystarcza do realizacji zamierzonych funkcjonalności (w zasadzie wykorzystana jest niespełna w połowie). Przejdźmy zatem do opisu sposobu działania urządzenia NFC Lock.

Rysunek 3. Schemat ideowy urządzenia NFC Lock

Diody sygnalizacyjne GREEN i RED służą standardowo do sygnalizowania stanu pracy urządzenia (w tym ewentualnych błędów), zaś przyciski ADD i REMOVE służą do obsługi urządzenia. Zacznijmy od początku. Tuż po włączeniu zasilania następuje włączenie diod RED i GREEN w celu kontroli ich sprawności. To jedyny moment w trakcie pracy urządzenia, gdy obie diody zapalają się jednocześnie. Po upływie ok. 1000 ms testu przeprowadzana jest konfiguracja modułu PN532. W przypadku pojawienia się jakichkolwiek błędów sytuacja taka zostanie zasygnalizowana poprzez mrugnięcia czerwonej diody LED (RED), przy czym liczba tych mrugnięć zależy od typu błędów, jakie pojawiły się podczas inicjalizacji modułu i pokazano je w tabeli 1.

Dalej urządzenie przechodzi do trybu nasłuchiwania ewentualnych tagów RFID/NFC. W przypadku wykrycia tego rodzaju karty/urządzenia NFC w pobliżu modułu czytnika, system podejmuje próbę odczytania numeru UID. W przypadku niepowodzenia odczytu urządzenie sygnalizuje ten stan poprzez pojedyncze mignięcie czerwoną diodą LED (RED). W przypadku odczytania tagu RFID/NFC możliwe są trzy scenariusze w zależności od stanu przycisków funkcyjnych ADD i REMOVE.

Pierwszy scenariusz, przycisk ADD wciśnięty, powoduje podjęcie próby dodania odczytanego numeru UID do obsługiwanych przez urządzenia numerów UID (pozwalających na otwarcie zamka). W przypadku powodzenia poniższej operacji zostanie to zasygnalizowane poprzez pojedyncze mignięcie zieloną diodą LED (GREEN), zaś w przypadku niepowodzenia zostanie to zasygnalizowane poprzez pojedyncze mignięcie czerwoną diodą LED (RED). Źródłem niepowodzenia mogą być dwa scenariusze: wprowadzany numer UID już znajduje się na liście obsługiwanych numerów lub osiągnięto maksymalną liczbę zapamiętywanych numerów UID (64 sztuki).

Drugi scenariusz, przycisk REMOVE wciśnięty, powoduje podjęcie próby usunięcia odczytanego numeru UID z obsługiwanych przez urządzenia numerów UID (pozwalających na otwarcie zamka). W przypadku powodzenia poniższej operacji zostanie to zasygnalizowane poprzez pojedyncze mignięcie zieloną diodą LED (GREEN), zaś w przypadku niepowodzenia zostanie to zasygnalizowane poprzez pojedyncze mignięcie czerwoną diodą LED (RED). Źródłem niepowodzenia może być wyłącznie stan, że odczytany numer UID nie znajduje się na liście obsługiwanych przez urządzenia numerów UID, w związku z czym nie można go z tej listy usunąć.

Scenariusz trzeci to stan, gdzie w czasie odczytu numeru UID nie jest wciśnięty żaden przycisk funkcyjny. W tym wypadku urządzenie szuka odczytanego numeru UID na liście obsługiwanych przez urządzenie numerów UID i jeśli operacja taka kończy się powodzeniem, to załączana jest zielona dioda LED (GREEN) oraz przekaźnik K1 na czas 1,5 s. Dokładny graf pokazujący sposób działania urządzenia NFC Lock pokazano na rysunku 4.

Rysunek 4. Graf pokazujący sposób działania urządzenia NFC Lock

Kilka słów uwagi należy się potencjalnej możliwości obsługi urządzeń NFC w rodzaju telefonu komórkowego. Jak wiadomo, niektóre z tych urządzeń wyposażono w interfejs NFC umożliwiający wymianę danych lub obsługę płatności zbliżeniowych. Czy nasz prosty system będzie w stanie odczytać numer seryjny takiego urządzenia? Oczywiście, że tak, bo musi ono spełniać standardy protokołu NFC. Problem jednak w tym, że systemy operacyjne telefonów komórkowych generują losowe numery seryjne (tzw. RID od Random ID), w związku z czym ich zapamiętywanie nie ma większego sensu, gdyż podczas kolejnego połączenia z takim urządzeniem wygeneruje ono zupełnie inny numer seryjny. Zachowanie to można zmienić, ale przynajmniej w Androidzie nie jest to takie proste i wymaga modyfikacji firmware lub użycia specjalnych aplikacji. Ponoć zdarzają się telefony ze statycznym numerem UID (takie informacje odnalazłem na specjalistycznych forach), lecz ja na takie nie natrafiłem, więc należy założyć, iż standardowo generują one losowe numery RID. Tyle w kwestiach obsługi, przejdźmy zatem do zagadnień montażowych.

Ustawienia Fusebitów:
CKSEL3...0: 0010
SUT1...0: 10
CKDIV8: 0
EESAVE: 0

Montaż i uruchomienie

Schemat montażowy urządzenia NFC Lock pokazano na rysunku 5. Jak widać, zaprojektowano bardzo prosty, jednostronny obwód drukowany z zastosowaniem wyłącznie elementów THT. Montaż urządzenia rozpoczynamy od przylutowania mikrokontrolera oraz pozostałych elementów półprzewodnikowych (z wyłączeniem modułu PN532), następnie lutujemy elementy bierne, zworki, przyciski, złącza, przekaźnik, a na samym końcu moduł RFID, posiłkując się odpowiedniej długości złączem GOLDPIN, by moduł PN532 zmieścił się nad takimi elementami, jak tranzystor T1 czy mikrokontroler.

Rysunek 5. Schemat montażowy urządzenia NFC Lock

Przyciski ADD i REMOVE powinny być dość niskie, gdyż służą wyłącznie konfiguracji modułu i powinny znajdować się pod docelową obudową urządzenia. Ostatnim krokiem jest ustawienie aktywnego medium transmisyjnego modułu PN532, co, jak wspomniano na wstępie, dokonujemy za pomocą switcha SMD zamontowanego na obwodzie drukowanym modułu RFID.

Robert Wołgajew, EP

Wykaz elementów:
Rezystory: (obudowa miniaturowa 1/8 W, raster 0,2”)
  • R1: 47 kΩ
  • R2, R3: 130 Ω
  • R4: 1 kΩ
Kondensatory:
  • C1, C4, C5: 100 nF ceramiczny (raster 0,1”)
  • C2, C3:100 μF/16 V elektrolityczny (raster 0,1”)
Półprzewodniki:
  • D1: 1 N4148 (obudowa DO-35)
  • RED: czerwona dioda LED 3 mm
  • GREEN: zielona dioda LED 3 mm
  • T1: BC548 (obudowa TO-92)
  • U1: LD1117 V33 (obudowa TO-220)
  • U2: ATtiny44 (obudowa DIP14)
  • U3: moduł RFID/NFC typu PN532
Pozostałe:
  • K1: przekaźnik G5 LE-14-9
  • PWR, LOCK: złącze śrubowe AK500/2 (raster 0,1”)
  • ADD, REMOVE: microswitch TACT
Artykuł ukazał się w
Elektronika Praktyczna
kwiecień 2022
DO POBRANIA
Materiały dodatkowe

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik marzec 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio marzec - kwiecień 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje marzec 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna marzec 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich kwiecień 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów