Czytnik RFID za 15 złotych.
Nie licząc anteny, (kliknij obrazek żeby ją zobaczyć) 2cmx4cm:
Z czułości anteny niestety nie byłem zadowolony, zrobiłem własną, 50 zwojów drutu nawojowego (to ten czarny krążek średnicy około 50 mm widoczny na powiększeniu). Średnica drutu nawojowego nie bardzo istotna, użyłem 0,40 mm, bo taki akurat się pod łapę "nawinął".
Jak to działa?
Czytnik RDM630 za pomocą cewki generuje pole elektromagnetyczne o częstotliwości 125 kHz (są też inne standardy, nie będę ich tu opisywał).
Jeśli umieścimy w tym polu tzw. transponder, korzystając z energii tego pola zawarty w nim mikroukład scalony zacznie działać i wyśle swój pięciobajtowy identyfikator.
Identyfikator ten jest unikalny, nie ma dwu transponderów o takim samym identyfikatorze (no, przynajmniej nie powinno być, miałem już kiedyś pudełko transponderów jakiegoś taniego producenta, na 200 kart trafiły się dwa identyfikatory użyte dwukrotnie).
Czytnik, jak to czytnik, odczyta ten identyfikator i przekaże nam do wykorzystania.
Transponder może być plastikową kartą, breloczkiem, hermetycznym modułem do zastosowań przemysłowych itp.
To mały przykład:
No to zabieramy się za robotę.
Potrzebujemy czytnik, jakiś transponder do testów, dwa rezystory, np, 1k i 1,9k, no i oczywiście Rapberry.
Po co te rezystory? Ano, GPIO Raspberry pracuje z napięciem 3,3V, a nasz czytnik transmituje w standardzie 5V. Raspberry zasilany jest napięciem 5V, ale na pinach łączówki GPIO sygnały są w standardzie CMOS, 3,3V. Podanie napięcia wyższego niż 3,3V na pin GPIO może skończyć się jego uszkodzeniem.
Sygnał z czytnika musimy podpiąć do GPIO przez zwykły dzielnik z dwu rezystorów:
Podpinamy więc do naszego czytnika zasilanie 5V, wyjście TX (pin1 pięciopinowej łączówki) przez dzielnik do pinu 10 (RX) łączówki GPIO:
- Pin +5V czytnika (piąty na łączówce pięciopinowej) do pinu +5V GPIO Raspberry (2 lub 4 fizyczny pin)
- Pin GND czytnika (czwarty na łączówce pięciopinowej) do pinu 0V GPIO Raspberry (na przykład 6 fizyczny pin)
- Pin RX czytnika (pierwszy na łączówce pięciopinowej) przez dzielnik, do pinu RX GPIO Raspberry (10 fizyczny pin)
Dokumentację RDM630 bez problemu znajdziemy w internecie, dla wygody skopiowałem ją też tutaj.
I tu pierwsza przeszkoda. Potrzebujemy na wyłączność portu szeregowego UART Raspberry. Może on być jednak używany jako port konsoli, albo przez bluetooth. Jeśli po wydaniu komendy
gpio readall
zobaczymy, ze piny TX i RX są w trybie ALT5, jak w poniższej tabeli, to czeka nas trochę przygotowawczej pracy.+-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | | | 3.3v | | | 1 || 2 | | | 5v | | | | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5v | | | | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT5 | TxD | 15 | 14 | | | | 0v | | | 9 || 10 | 1 | ALT5 | RxD | 16 | 15 | | 17 | 0 | GPIO. 0 | OUT | 0 | 11 || 12 | 0 | OUT | GPIO. 1 | 1 | 18 | | 27 | 2 | GPIO. 2 | OUT | 1 | 13 || 14 | | | 0v | | | | 22 | 3 | GPIO. 3 | OUT | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | | | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 | | 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | | | 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 0 | OUT | CE0 | 10 | 8 | | | | 0v | | | 25 || 26 | 0 | OUT | CE1 | 11 | 7 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+
Trzeba przeedytować zbiory konfiguracyjne /boot/cmdline.txt (usunąć uruchamianie konsoli) i /boot/config.txt (usunąć uruchamianie bluetooth i włączyć UART, jeśli był wyłączony.).
Trudno podać jednoznaczną instrukcję jak to zrobić, w różnych wersjach Raspbiana różnie to wygląda, polecam poklikać w internecie.
W moim Raspbianie (10.4) w pliku /boot/cmdline.txt trzeba było usunąć console=tty1, a w pliku /boot/config.txt dopisać (i usunąć, poprzedzić znakiem komentarza):
enable_uart=1 dtoverlay=pi3-disable-bt-overlay dtoverlay=disable-bt dtoverlay=miniuart #dtparam=audio=onW Raspbian 9.8 poprawki wyglądały tak:
#dtparam=audio=off enable_uart=1 dtoverlay=miniuart dtoverlay=pi3-disable-bt dtoverlay=disable-btPotem jeszcze trzeba było zatrzymać:
systemctl disable hciuart.service systemctl disable bluealsa.service systemctl disable bluetooth.service
Po przeładowaniu systemu port szeregowy powinien być do naszej wyłącznej dyspozycji.
No to kawałek programu.
Jak zwykle gotowy, działający, przykładowy program znajdziesz tutaj, wystarczy skompilować:
cc rdm.c -o rdm
i uruchomić:
./rdm
Po pierwszej kompilacji pewnie trzeba będzie zmienić prawa do zbioru:
chmod 754 rdm
Ale jak on działa?
Na początku programujemy port szeregowy, opisałem to już przy okazji programu do wysyłania SMS , nie będę więc pisał drugi raz tego samego (choć myszką łatwo skopiować i wkleić).
Problem może się pojawić przy otwieraniu portu szeregowego:
if((pd = open("/dev/ttyAMA0",O_RDWR | O_NDELAY |O_NONBLOCK)) != -1)Układ BCM2835 w Raspberry (w starszych modelach trochę inny) ma dwa porty szeregowe, jeśli interesują Cię szczegóły techniczne, polecam dokumentację
Nie wchodząc w szczegóły, jeśli na ttyAMA0 nie zadziała, podmień w tej linii kodu ttyAMA0 na ttyS0
Po każdym odczycie karty czytnik wysyła na port szeregowy:
- bajt 02, jako znacznik początku transmisji
- 10 bajtów ID karty
- bajt CRC (XOR z bajtów ID karty, tych 5 zapisanych na karcie)
- bajt 03, jako znacznik końca transmisji
Bajty ID wysyłane sa w postaci kodów ASCII, więc na przykład bajt 7A wysłany zostanie jako dwa znaki, 37H (szesnastkowo) i 41H, stąd 10 znaków zamiast 5.
No to zaczynamy: Właściwy fragment programu do odczytu kart zaczyna się tu:
W niekończącej się pętli czytamy dane z portu szeregowego.
for(;;) {Dwie poniższe linijki kodu wymagają wyjaśnienia. Kiedy transponder znajdzie się w polu elektromagnetycznym anteny, czytnik wysyła kilka jej odczytów.
Nie wiem dlaczego, czasami 3, czasami 7, kolejne odczyty transpondera wysyłane są dopiero jak transponder usuniemy i zbliżymy ponownie.
Dlatego po każdym odczycie odczekujemy sekundę , po czym czytamy wszystkie bajty które są w buforze UART. W ten sposób pozbywamy się wielokrotnych odczytów karty.
// remove multiple card readings sleep(1); while( read(pd, &znak, 99) > 0) ;Teraz czekamy na bajt 02H, start transmisji:
znak[0]=0x00; while (znak[0] != 0x02) // start of card ID { read(pd, &znak[0], 1); } n=0;Czytamy 11 kolejnych bajtów, czyli kody ASCII ID karty i CRC. Bajt końca transmisji, 03H ignorujemy, zostanie usunięty mechanizmem opisanym wyżej.
for(;;) { if ((a=read (pd, &znak, 1)) != -1) { karta[n] = znak[0]; n++; if (n>11) break; } }Nie sprawdzamy CRC, praktyka uczy, że przekłamania są bardzo rzadkie, a w praktyce nasz program i tak będzie sprawdzał ID karty w bazie danych, żeby ustalić na przykład uprawnienia właściciela.
W miejsce bajtu CRC wpisujemy bajt 00H, znacznik końca łańcucha w języku C:
// remove CRC karta[10]=0x00;No i coś teraz z odczytanym ID musimy zrobić, sprawdzić w bazie danych, zapisać do pliku... W naszym przykładowym programie po prostu go wyświetlimy:
printf("karta: %s\n", karta); fflush(0); }