HiPi::Interface::MFRC522
This module provides an interface to the common MFCR522 NFC module.
Supported tag types are MiFare Classic 1k and 4k, 4 byte UID and 7 byte UID.
It is a port of the Arduino MFRC522 library from https://github.com/miguelbalboa/rfid
Examples
The examples below are also part of the source distribution.
Module Info
Display information about the MFRC522 module.
#!/usr/bin/perl use strict; use warnings; use HiPi qw( :rpi :mfrc522); use HiPi::Interface::MFRC522; my $resetpin = RPI_PIN_38; # the pin connected to reset my $rfid = HiPi::Interface::MFRC522->new( reset_pin => $resetpin, devicename => '/dev/spidev0.1' ); my $versionstring = $rfid->get_firmware_version_string; print qq(Module Version : $versionstring\n); if( $rfid->self_test_ok ) { print qq(Self Test Result : SUCCESS\n); } else { print qq(Self Test Result : FAILED\n); } # after self test we have to re init $rfid->init; 1;
Dump Tag Info
Wait for tags to be presented and display basic info
#!/usr/bin/perl use strict; use warnings; use HiPi qw( :rpi :mfrc522); use HiPi::Interface::MFRC522; my $resetpin = RPI_PIN_38; # the pin connected to reset my $rfid = HiPi::Interface::MFRC522->new( reset_pin => $resetpin, devicename => '/dev/spidev0.1' ); sub handle_scan { my $continue = 1; my( $uid, $uidstring) = @_; my $output = $rfid->picc_dump_details( $uid ); print $output . qq(\n); $rfid->picc_end_session; return $continue; } sub handle_timeout { warn q(timeout called); return 1; } $rfid->scan( \&handle_scan, \&handle_timeout, 10 ); 1;
Dump all Tag Blocks
Print all tag block information to the console.
#!/usr/bin/perl use strict; use warnings; use HiPi qw( :rpi :mfrc522); use HiPi::Interface::MFRC522; my $resetpin = RPI_PIN_38; # the pin connected to reset my $rfid = HiPi::Interface::MFRC522->new( reset_pin => $resetpin, devicename => '/dev/spidev0.1' ); $rfid->scan( \&handle_scan ); sub handle_scan { my $continue = 1; my( $uid, $uidstring) = @_; print qq(\nReading Tag UID $uidstring Blocks\n); print qq( do not remove tag from field ....\n); my $output = $rfid->picc_dump_tag_info( $uid ); print $output; # set the card inactive $rfid->picc_end_session; print qq(\nTag $uidstring read complete\n\n); return $continue; } 1;
Read and Write
Write data to a tag block and retrieve information
#!/usr/bin/perl use strict; use warnings; use HiPi qw( :rpi :mfrc522); use HiPi::Interface::MFRC522; my $resetpin = RPI_PIN_38; # the pin connected to reset my $rfid = HiPi::Interface::MFRC522->new( reset_pin => $resetpin, devicename => '/dev/spidev0.1' ); my $uidswritten = {}; # keep a record of if we have written to a tag in current invocation my $infoblock = 2; # the block we will write to / read from my $infostring = 'HiPi Block Test'; my $key = $rfid->get_default_key; # change if you changed it sub handle_read_write { my( $uid, $uidstring) = @_; print qq(Tag ID : $uidstring\n); my $picctype = $rfid->picc_get_type( $uid->{'sak'} ); my $piccname = $rfid->picc_get_type_name( $picctype ); print qq(Tag Type : $piccname\n); my $continue = 1; if(exists($uidswritten->{$uidstring})) { # read the data from info block my ( $bdstatus, $blockdata ) = $rfid->read_block_data( $infoblock, $uid, $key ); if( $bdstatus == MFRC522_STATUS_OK ) { my $stringdata = ''; my $bldata = ''; for my $byte ( @$blockdata ) { $bldata .= ' ' if $bldata; $bldata .= sprintf('%02X', $byte); $stringdata .= chr($byte) if $byte; # ignore 0x00 == NULL - this is text } print qq(READ BLOCK $infoblock DATA : $bldata\n); print qq(BLOCK $infoblock STRING : $stringdata\n\n); $continue = 1; # wait for next tag print qq(present next tag ...\n\n); } else { print $rfid->get_status_code_name( $bdstatus ) . qq(\n); } } else { # write the data to the block my $writestring = $infostring; my @chars = split(//, $writestring); my @writedata = (); for (my $i = 0; $i < 16; $i ++) { # block is 16 bytes my $char = $chars[$i]; if(defined($char)) { $writedata[$i] = ord($char); } else { $writedata[$i] = 0; } } my $bdstatus = $rfid->write_block_data( $infoblock, $uid, \@writedata, $key ); print qq(WRITE BLOCK $infoblock DATA RESULT : ) . $rfid->get_status_code_name( $bdstatus ) . qq(\n\n); if( $bdstatus == MFRC522_STATUS_OK ) { $uidswritten->{$uidstring} = 1; print qq(re-present tag to read block ....\n\n); } $continue = 1; } # end session so we can start communicating with same tag or new tag $rfid->picc_end_session; return $continue; } $rfid->scan( \&handle_read_write ); 1;
Change Keys
Chaneg keys and possibly access permissions on tag.
#!/usr/bin/perl use strict; use warnings; use HiPi qw( :rpi :mfrc522); use HiPi::Interface::MFRC522; my $resetpin = RPI_PIN_38; # the pin connected to reset my $rfid = HiPi::Interface::MFRC522->new( reset_pin => $resetpin, devicename => '/dev/spidev0.1' ); my $reverse = 0; # change $reverse to 1 to switch back a tag to defaults # ( the current key must be the one in $replaceA below, of course); my $defaultkey = $rfid->get_default_key; my $replaceA = [ 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6 ]; # New Key A my $replaceB = [ 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6 ]; # New Key B my $blockswritten = {}; $rfid->scan( \&handle_scan ); sub handle_scan { my $continue = 1; my( $uid, $uidstring) = @_; print qq(Tag UID: $uidstring\n); # record which blocks we wrote successfully so # that we can retry other blocks without failing # authentication. # This only lasts while the script is running if(!exists($blockswritten->{$uidstring})) { $blockswritten->{$uidstring} = {}; } # These are the default existing access bits. # We won't pass this value and existing bits will remain my $accessbits = [ 0b000, 0b000, 0b000, 0b001 ]; # General Purpose Bit. We won't pass this value and all existing # values will remain my $gpb = 0x69; my $key = ( $reverse ) ? $replaceA : $defaultkey; my $newkeyA = ( $reverse ) ? $defaultkey : $replaceA; my $newkeyB = ( $reverse ) ? $defaultkey : $replaceB; my $picctype = $rfid->picc_get_type( $uid->{'sak'} ); my $blocks = $rfid->get_sector_trailer_blocks( $picctype ); my $success = 1; for my $block( sort { $a <=> $b } ( keys %$blocks ) ) { if($blockswritten->{$uidstring}->{$block}) { print qq(skipping written block $block\n); next; } my $status = $rfid->write_sector_trailer( $block, $key, $uid, $newkeyA, $newkeyB, undef, undef ); if( $status == MFRC522_STATUS_OK ) { $blockswritten->{$uidstring}->{$block} = 1; } else { $success = 0; } print qq(result sector trailer write $block : ) . $rfid->get_status_code_name( $status ) . qq(\n); # now read back in my( $rstatus, $rdata ) = $rfid->read_block_data( $block, $uid, $newkeyA ); if( $rstatus == MFRC522_STATUS_OK ) { printf(qq(result sector trailer read %03d %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n), $block, @$rdata ); } else { print qq(result sector trailer read $block : ) . $rfid->get_status_code_name( $rstatus ) . qq(\n); } $rfid->sleep_milliseconds(1); } if( $success ) { my $setype = ( $reverse ) ? 'Default' : 'New Custom'; print qq(Tag Access Set to $setype Key On All Sectors\n); } else { print qq(Failed - 1 or more sector trailers were not written. Re-present tag / card\n); } $rfid->picc_end_session; return $continue; } 1;